summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-30 22:13:12 +0300
committerPaul Buetow <paul@buetow.org>2026-05-30 22:13:12 +0300
commitdbfab00f3bbb7c21a04ec5036cb35ff463837a5a (patch)
tree60d2dcac2dc9f8c601a097532b3ac9a816a84241
parentf3f8244849fbe94ce482b4b660aae4a007d07b8d (diff)
Refactor photoalbum bash script
-rw-r--r--README.md3
-rwxr-xr-xsrc/photoalbum.sh423
2 files changed, 282 insertions, 144 deletions
diff --git a/README.md b/README.md
index e1631fb..709682b 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,8 @@ make
sudo make install
```
-Also, as a requirement, `convert` from ImageMagick needs to be installed.
+Also, as a requirement, ImageMagick needs to be installed. The script prefers the
+modern `magick` command and falls back to `convert` when needed.
## Usage
diff --git a/src/photoalbum.sh b/src/photoalbum.sh
index 7eb3e3b..bc721ee 100755
--- a/src/photoalbum.sh
+++ b/src/photoalbum.sh
@@ -1,21 +1,20 @@
-#!/bin/env bash
+#!/usr/bin/env bash
+set -euo pipefail
# photoalbum (c) 2011 - 2014, 2022 by Paul Buetow
# https://codeberg.org/snonux/photoalbum
-readonly VERSION='PHOTOALBUMVERSION'
-readonly DEFAULTRC='/etc/default/photoalbum'
-declare ARG1="$1" ; shift
-declare RC_FILE="$1" ; shift
+declare -r VERSION='PHOTOALBUMVERSION'
+declare -r DEFAULTRC='/etc/default/photoalbum'
-usage () {
+usage() {
cat - <<USAGE >&2
- Usage:
+ Usage:
$0 clean|generate|version|makemake [rcfile]
USAGE
}
-makemake () {
+makemake() {
[ ! -f ./photoalbumrc ] && cp "$DEFAULTRC" ./photoalbumrc
cat <<MAKEFILE > ./Makefile
all:
@@ -26,50 +25,76 @@ MAKEFILE
echo 'You may now customize ./photoalbumrc and run make'
}
-tarball () {
+imagemagick() {
+ if command -v magick >/dev/null 2>&1; then
+ magick "$@"
+ elif command -v convert >/dev/null 2>&1; then
+ convert "$@"
+ else
+ echo 'ERROR: ImageMagick is required; install magick or convert' >&2
+ return 127
+ fi
+}
+
+tarball() {
+ local -r tarball_name="$1"; shift
+ local -r tar_opts="${TAR_OPTS:--c}"
+ local base
+
# Cleanup tarball from prev run if any
- find "$DIST_DIR" -maxdepth 1 -type f -name \*.tar -delete
- declare base="$(basename "$INCOMING_DIR")"
+ find "$DIST_DIR" -maxdepth 1 -type f -name '*.tar' -delete
+ base=$(basename "$INCOMING_DIR")
echo "Creating tarball $DIST_DIR/$tarball_name from $INCOMING_DIR"
- cd "$(dirname "$INCOMING_DIR")"
- tar "$TAR_OPTS" -f "$DIST_DIR/$tarball_name" "$base"
- cd - &>/dev/null
+ (
+ cd "$(dirname "$INCOMING_DIR")"
+ tar "$tar_opts" -f "$DIST_DIR/$tarball_name" "$base"
+ )
}
-template () {
- declare template="$1" ; shift
- declare html="$1" ; shift
- declare dist_html="$DIST_DIR/$html_dir"
+template() {
+ local -r template_name="$1"; shift
+ local -r html="$1"; shift
+ local -r dist_html="$DIST_DIR/$html_dir"
+
echo "Generating $dist_html/$html"
- [ ! -d "$dist_html" ] && mkdir -p "$dist_html"
- source "$TEMPLATE_DIR/$template.tmpl" >> "$dist_html/$html"
+ mkdir -p "$dist_html"
+ source "$TEMPLATE_DIR/$template_name.tmpl" >> "$dist_html/$html"
}
-cleanphotos () {
- find "$DIST_DIR/photos" -maxdepth 1 -type f | while read photo; do
- local basename=$(basename $photo)
+cleanphotos() {
+ local basename
+ local photo
+ local sub
+
+ while IFS= read -r photo; do
+ basename=$(basename "$photo")
+
if [ -f "$INCOMING_DIR/$basename" ]; then
continue
fi
+
echo "Cleaning up $photo"
for sub in thumbs blurs photos; do
if [ -f "$DIST_DIR/$sub/$basename" ]; then
rm -v "$DIST_DIR/$sub/$basename"
fi
done
- done
+ done < <(find "$DIST_DIR/photos" -maxdepth 1 -type f)
}
-scalephotos () {
- cd "$INCOMING_DIR" && find ./ -maxdepth 1 -type f | sort |
- while read -r photo; do
- declare photo="$(sed 's#^\./##' <<< "$photo")"
- declare destphoto="$DIST_DIR/photos/$photo"
- declare destphoto_nospace="${destphoto// /_}"
- declare dirname="$(dirname "$destphoto")"
- [ ! -d "$dirname" ] && mkdir -p "$dirname"
+scalephotos() {
+ local destphoto
+ local destphoto_nospace
+ local dirname
+ local photo
+
+ while IFS= read -r photo; do
+ destphoto="$DIST_DIR/photos/$photo"
+ destphoto_nospace="${destphoto// /_}"
+ dirname=$(dirname "$destphoto")
+ mkdir -p "$dirname"
if [ -f "$destphoto_nospace" ]; then
echo "Already exists: $destphoto_nospace"
@@ -77,198 +102,310 @@ scalephotos () {
fi
echo "Processing $photo to $destphoto_nospace"
- if [ -n "$HEIGHT" ]; then
+ if [ -n "${HEIGHT:-}" ]; then
# Scale down size.
- convert -auto-orient -geometry "$HEIGHT" "$photo" "$destphoto_nospace"
+ imagemagick \
+ -auto-orient \
+ -geometry "$HEIGHT" \
+ "$INCOMING_DIR/$photo" \
+ "$destphoto_nospace"
else
- # Keep original size
- convert -auto-orient "$photo" "$destphoto_nospace"
+ # Keep original size.
+ imagemagick \
+ -auto-orient \
+ "$INCOMING_DIR/$photo" \
+ "$destphoto_nospace"
fi
- done
-}
-
-randomphoto () {
- declare photos_dir="$1" ; shift
- basename $(find "$photos_dir" -type f -maxpdeth 1 -mindepth 1 | sort -R | head -n 1)
+ done < <(
+ find "$INCOMING_DIR" -maxdepth 1 -type f -printf '%f\n' \
+ | sort
+ )
}
-random_animation_css_class () {
+random_animation_css_class() {
local -r speed="$1"; shift
- cat <<END | grep -v fading | sort -R | head -n 1
-animate-fading-$speed
-animate-opacity-$speed
-animate-top-$speed
-animate-left-$speed
-animate-right-$speed
-animate-bottom-$speed
-animate-zoom-$speed
-END
+ local -a classes=(
+ "animate-opacity-$speed"
+ "animate-top-$speed"
+ "animate-left-$speed"
+ "animate-right-$speed"
+ "animate-bottom-$speed"
+ "animate-zoom-$speed"
+ )
+
+ printf '%s\n' "${classes[@]}" | sort -R | sed -n '1p'
}
-maybe_shuffle () {
- if [ "$SHUFFLE" = yes ]; then
+maybe_shuffle() {
+ if [ "${SHUFFLE:-no}" = yes ]; then
sort -R
else
sort
fi
}
-albumhtml () {
- declare photos_dir="$1" ; shift
- declare html_dir="$1" ; shift
- declare thumbs_dir="$1" ; shift
- declare blurs_dir="$1" ; shift
- export backhref="$1" ; shift
+newest_html() {
+ local -r pattern="$1"; shift
- declare -i num=1
- declare -i i=0
- declare name="page-$num"
+ find "$DIST_DIR/$html_dir" \
+ -maxdepth 1 \
+ -name "$pattern" \
+ -printf '%T@ %f\n' \
+ | sort -nr \
+ | sed -n '1{s/^[^ ]* //;p}'
+}
+
+albumhtml() {
+ local -r photos_dir="$1"; shift
+ local -r html_dir="$1"; shift
+ local -r thumbs_dir="$1"; shift
+ local -r blurs_dir="$1"; shift
+ local -r backhref="$1"; shift
+
+ local animation_class
+ local background_image
+ local dirname
+ local height
+ local lastview
+ local name
+ local next
+ local nextredirect
+ local page
+ local photo
+ local prev
+ local prefix
+ local prevredirect
+ local redirect_page
+ local show_header_bar
+ local -i i=0
+ local -i num=1
+
+ export backhref
+ name="page-$num"
# Random background image for preview page.
- export background_image="$(randomphoto $photos_dir)"
- export show_header_bar='yes'
+ background_image=$(randomphoto "$photos_dir")
+ show_header_bar='yes'
+ export background_image show_header_bar
template 'header' "$name.html"
- cd "$DIST_DIR/$photos_dir" && find ./ -type f | maybe_shuffle | sed 's;^\./;;' |
- while read -r photo; do
- let i++
+ while IFS= read -r photo; do
+ (( ++i ))
- if [ "$i" -gt "$MAXPREVIEWS" ]; then
+ if (( i > MAXPREVIEWS )); then
i=1
- let num++
- declare next="page-$num"
+ (( ++num ))
+ next="page-$num"
+ prev="${prev:-}"
+ export next prev
template next "$name.html"
template footer "$name.html"
- export prev="$name"
- declare name="$next"
+ prev="$name"
+ name="$next"
- export background_image="$(randomphoto $photos_dir)"
- export show_header_bar='no'
+ background_image=$(randomphoto "$photos_dir")
+ show_header_bar='no'
+ export background_image prev show_header_bar
template header "$name.html"
template prev "$name.html"
fi
- # Preview page
- export animation_class=$(random_animation_css_class slow)
+ # Preview page.
+ animation_class=$(random_animation_css_class slow)
+ export animation_class
template preview "$name.html"
- # View page
- export background_image="$photo"
- export show_header_bar='no'
+ # View page.
+ background_image="$photo"
+ show_header_bar='no'
+ export background_image show_header_bar
template header "$num-$i.html"
- export animation_class=$(random_animation_css_class fast)
+ animation_class=$(random_animation_css_class fast)
+ export animation_class
template view "$num-$i.html"
template footer "$num-$i.html"
- if [[ -f "$DIST_DIR/$thumbs_dir/$photo" && -f "$DIST_DIR/$blurs_dir/$photo" ]]; then
- echo "Already exists: $DIST_DIR/$thumbs_dir/$photo and $DIST_DIR/$blurs_dir/$photo"
+ if [[ -f "$DIST_DIR/$thumbs_dir/$photo" \
+ && -f "$DIST_DIR/$blurs_dir/$photo" ]]; then
+ echo "Already exists: $DIST_DIR/$thumbs_dir/$photo and" \
+ "$DIST_DIR/$blurs_dir/$photo"
else
- declare dirname="$DIST_DIR/$thumbs_dir"
- test ! -d "$dirname" && mkdir -p "$dirname"
+ dirname="$DIST_DIR/$thumbs_dir"
+ mkdir -p "$dirname"
echo "Creating thumb $DIST_DIR/$thumbs_dir/$photo"
- # Double the height, as CSS will scale up/down images based on boxing too.
- declare height=$((THUMBHEIGHT * 2))
- convert -geometry "x$height" "$photo" "$DIST_DIR/$thumbs_dir/$photo"
+ # Double the height, as CSS scales images based on boxing too.
+ height=$(( THUMBHEIGHT * 2 ))
+ imagemagick \
+ -geometry "x$height" \
+ "$DIST_DIR/$photos_dir/$photo" \
+ "$DIST_DIR/$thumbs_dir/$photo"
dirname="$DIST_DIR/$blurs_dir"
- test ! -d "$dirname" && mkdir -p "$dirname"
+ mkdir -p "$dirname"
echo "Creating blur $DIST_DIR/$blurs_dir/$photo"
- convert -flip -blur 0x8 "$DIST_DIR/$thumbs_dir/$photo" "$DIST_DIR/$blurs_dir/$photo"
+ imagemagick \
+ -flip \
+ -blur 0x8 \
+ "$DIST_DIR/$thumbs_dir/$photo" \
+ "$DIST_DIR/$blurs_dir/$photo"
fi
- done
-
- template footer "$(cd "$DIST_DIR/$html_dir";ls -t page-*.html | head -n 1)"
+ done < <(
+ find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
+ | maybe_shuffle
+ )
- cd "$DIST_DIR/$html_dir" && ls | grep '.*\.html$' |
- grep -v page- | cut -d'-' -f1 | uniq |
+ template footer "$(newest_html 'page-*.html')"
- while read -r prefix; do
- declare page="$(ls -t "$prefix"-*.html | head -n 1 | sed 's#\(.*\)-.*.html#\1#')"
- declare lastview="$(ls -t "$prefix"-*.html | head -n 1 | sed 's/.*-\(.*\).html/\1/')"
+ while IFS= read -r prefix; do
+ page=$(newest_html "$prefix-*.html" | sed 's#\(.*\)-.*.html#\1#')
+ lastview=$(newest_html "$prefix-*.html" | sed 's/.*-\(.*\).html/\1/')
- declare prevredirect="${page}-0"
- declare nextredirect="${page}-$((lastview+1))"
+ prevredirect="${page}-0"
+ nextredirect="${page}-$(( lastview + 1 ))"
- declare redirect_page="$(( page-1 ))-${MAXPREVIEWS}"
+ redirect_page="$(( page - 1 ))-${MAXPREVIEWS}"
+ export redirect_page
template redirect "$prevredirect.html"
- if [ "$lastview" -eq "$MAXPREVIEWS" ]; then
- declare redirect_page="$(( page+1 ))-1"
+ if (( lastview == MAXPREVIEWS )); then
+ redirect_page="$(( page + 1 ))-1"
else
- declare redirect_page="${page}-$lastview"
+ redirect_page="${page}-$lastview"
+ export redirect_page
template redirect "0-$MAXPREVIEWS.html"
redirect_page='1-1'
fi
export redirect_page
template redirect "$nextredirect.html"
- done
-
- # Create per album index/redirect page
- declare redirect_page='page-1'
+ done < <(
+ find "$DIST_DIR/$html_dir" \
+ -maxdepth 1 \
+ -name '*.html' \
+ ! -name 'page-*' \
+ -printf '%f\n' \
+ | cut -d'-' -f1 \
+ | sort -u
+ )
+
+ # Create per album index/redirect page.
+ redirect_page='page-1'
+ export redirect_page
template 'redirect' 'index.html'
}
-randomphoto () {
- ls -f "$DIST_DIR/photos/" | sort -R | head -n 1
+randomphoto() {
+ local -r photos_dir="$1"; shift
+ local photo
+ local -a photos=()
+
+ while IFS= read -r photo; do
+ photos+=("$photo")
+ done < <(
+ find "$DIST_DIR/$photos_dir" -maxdepth 1 -type f -printf '%f\n' \
+ | sort
+ )
+
+ if (( ${#photos[@]} == 0 )); then
+ echo "ERROR: No photos found in $DIST_DIR/$photos_dir" >&2
+ return 1
+ fi
+
+ printf '%s\n' "${photos[RANDOM % ${#photos[@]}]}"
}
-generate () {
+generate() {
+ local base
+ local html_dir
+ local now
+ local redirect_page
+ local tarball_name=''
+
if [ ! -d "$INCOMING_DIR" ]; then
echo "ERROR: You have to create $INCOMING_DIR first" >&2
exit 1
fi
- if [ "$TARBALL_INCLUDE" = yes ]; then
- declare base="$(basename "$INCOMING_DIR")"
- declare now="$(date +'%Y-%m-%d-%H%M%S')"
- declare tarball_name="${base}-${now}$TARBALL_SUFFIX"
+ if [ "${TARBALL_INCLUDE:-no}" = yes ]; then
+ base=$(basename "$INCOMING_DIR")
+ now=$(date +'%Y-%m-%d-%H%M%S')
+ tarball_name="${base}-${now}${TARBALL_SUFFIX:-.tar}"
fi
- test ! -d "$DIST_DIR/photos" && mkdir -p "$DIST_DIR/photos"
+ mkdir -p "$DIST_DIR/photos"
cleanphotos
scalephotos
- find "$DIST_DIR" -type f -name \*.html -delete
- declare -a dirs=( $(find "$DIST_DIR/photos" -mindepth 1 -maxdepth 1 -type d | sort) )
-
+ find "$DIST_DIR" -type f -name '*.html' -delete
albumhtml 'photos' 'html' 'thumbs' 'blurs' '..'
- # Create top level index/redirect page
- declare html_dir='./'
- declare redirect_page='./html/index'
+ # Create top level index/redirect page.
+ html_dir='./'
+ redirect_page='./html/index'
+ export redirect_page
template 'redirect' 'index.html'
- if [ "$TARBALL_INCLUDE" = 'yes' ]; then
- tarball
+ if [ "${TARBALL_INCLUDE:-no}" = yes ]; then
+ tarball "$tarball_name"
fi
}
-if [ -z "$RC_FILE" ]; then
- if [ -f photoalbumrc ]; then
- RC_FILE=photoalbumrc
+resolve_rc_file() {
+ local rc_file="${1:-}"
+
+ if [ -n "$rc_file" ]; then
+ printf '%s\n' "$rc_file"
+ elif [ -f photoalbumrc ]; then
+ printf '%s\n' photoalbumrc
elif [ -f ~/.photoalbumrc ]; then
- RC_FILE=~/.photoalbumrc
+ printf '%s\n' ~/.photoalbumrc
else
- RC_FILE="$DEFAULTRC"
+ printf '%s\n' "$DEFAULTRC"
fi
-fi
+}
-if [ ! -f "$RC_FILE" ]; then
- echo "Error: Can not find config file $RC_FILE" >&2
- exit 1
-fi
+apply_config_defaults() {
+ HEIGHT="${HEIGHT:-}"
+ ORIGINAL_BASEPATH="${ORIGINAL_BASEPATH:-}"
+ SHUFFLE="${SHUFFLE:-no}"
+ TARBALL_INCLUDE="${TARBALL_INCLUDE:-no}"
+ TARBALL_SUFFIX="${TARBALL_SUFFIX:-.tar}"
+ TAR_OPTS="${TAR_OPTS:--c}"
+}
+
+main() {
+ local -r arg1="${1:-}"
+ local -r rc_file="$(resolve_rc_file "${2:-}")"
-source "$RC_FILE"
+ if [ ! -f "$rc_file" ]; then
+ echo "Error: Can not find config file $rc_file" >&2
+ exit 1
+ fi
-case "$ARG1" in
- clean) [ -d "$DIST_DIR" ] && rm -Rf "$DIST_DIR";;
- generate) generate;;
- version) echo "This is Photoalbum Version $VERSION";;
- makemake) makemake;;
- *) usage;;
-esac
+ source "$rc_file"
+ apply_config_defaults
+
+ case "$arg1" in
+ clean)
+ if [ -d "$DIST_DIR" ]; then
+ rm -rf "$DIST_DIR"
+ fi
+ ;;
+ generate)
+ generate
+ ;;
+ version)
+ echo "This is Photoalbum Version $VERSION"
+ ;;
+ makemake)
+ makemake
+ ;;
+ *)
+ usage
+ ;;
+ esac
+}
-exit 0
+main "$@"