diff options
| -rw-r--r-- | README.md | 9 | ||||
| -rwxr-xr-x | bin/photoalbum | 52 | ||||
| -rwxr-xr-x | src/photoalbum.sh | 52 | ||||
| -rwxr-xr-x | tests/cli.sh | 74 |
4 files changed, 172 insertions, 15 deletions
@@ -45,10 +45,15 @@ settings, readable input and template directories, a writable output location, and ImageMagick availability. Generation stops before writing album output when validation fails. +Only regular files in `INCOMING_DIR` with supported image extensions are +processed as album images. Supported extensions are `jpg`, `jpeg`, `png`, `webp`, +and `gif`, matched case-insensitively. Other files, such as `.txt` or `.md` +notes, are ignored with a warning so generation can continue. + Successful generation writes `photoalbum.json` into the output directory. This metadata records the generator version and timestamp, config source, template -directory, source and generated file counts, tarball status, and effective -settings useful for debugging a published album. +directory, supported source image and generated file counts, tarball status, and +effective settings useful for debugging a published album. The following long options override config values: diff --git a/bin/photoalbum b/bin/photoalbum index 4dbc484..4429f7e 100755 --- a/bin/photoalbum +++ b/bin/photoalbum @@ -299,7 +299,8 @@ cleanphotos() { while IFS= read -r photo; do basename=$(basename "$photo") - if [ -f "$INCOMING_DIR/$basename" ]; then + if [[ -f "$INCOMING_DIR/$basename" ]] \ + && is_supported_image_file "$basename"; then continue fi @@ -312,6 +313,43 @@ cleanphotos() { done < <(find "$DIST_DIR/photos" -maxdepth 1 -type f) } +is_supported_image_file() { + local -r file="$1"; shift + local extension="${file##*.}" + + extension="${extension,,}" + + case "$extension" in + gif|jpeg|jpg|png|webp) + return 0 + ;; + *) + return 1 + ;; + esac +} + +incoming_image_files() { + local file + + while IFS= read -r file; do + if is_supported_image_file "$file"; then + printf '%s\n' "$file" + fi + done < <(find "$INCOMING_DIR" -maxdepth 1 -type f -printf '%f\n') \ + | sort +} + +warn_unsupported_incoming_files() { + local file + + while IFS= read -r file; do + if ! is_supported_image_file "$file"; then + echo "WARNING: Ignoring unsupported incoming file: $file" >&2 + fi + done < <(find "$INCOMING_DIR" -maxdepth 1 -type f -printf '%f\n' | sort) +} + scalephotos() { local destphoto local dirname @@ -342,10 +380,7 @@ scalephotos() { -auto-orient \ "$destphoto" fi - done < <( - find "$INCOMING_DIR" -maxdepth 1 -type f -printf '%f\n' \ - | sort - ) + done < <(incoming_image_files) } random_animation_css_class() { @@ -568,6 +603,10 @@ count_files() { fi } +count_incoming_images() { + incoming_image_files | wc -l +} + count_tree_files() { local -r dir="$1"; shift local -r name="$1"; shift @@ -585,7 +624,7 @@ write_generation_metadata() { local -r generated_at=$(date -u +'%Y-%m-%dT%H:%M:%SZ') local -r config_source="${PHOTOALBUM_CONFIG_SOURCE:-}" local -r template_name=$(basename "$TEMPLATE_DIR") - local -r source_image_count=$(count_files "$INCOMING_DIR") + local -r source_image_count=$(count_incoming_images) local -r generated_photo_count=$(count_files "$DIST_DIR/photos") local -r generated_thumb_count=$(count_files "$DIST_DIR/thumbs") local -r generated_html_count=$(count_tree_files "$DIST_DIR" '*.html') @@ -641,6 +680,7 @@ generate() { tarball_name="${base}-${now}${TARBALL_SUFFIX:-.tar}" fi + warn_unsupported_incoming_files mkdir -p "$DIST_DIR/photos" cleanphotos scalephotos diff --git a/src/photoalbum.sh b/src/photoalbum.sh index 4573946..18f3e70 100755 --- a/src/photoalbum.sh +++ b/src/photoalbum.sh @@ -299,7 +299,8 @@ cleanphotos() { while IFS= read -r photo; do basename=$(basename "$photo") - if [ -f "$INCOMING_DIR/$basename" ]; then + if [[ -f "$INCOMING_DIR/$basename" ]] \ + && is_supported_image_file "$basename"; then continue fi @@ -312,6 +313,43 @@ cleanphotos() { done < <(find "$DIST_DIR/photos" -maxdepth 1 -type f) } +is_supported_image_file() { + local -r file="$1"; shift + local extension="${file##*.}" + + extension="${extension,,}" + + case "$extension" in + gif|jpeg|jpg|png|webp) + return 0 + ;; + *) + return 1 + ;; + esac +} + +incoming_image_files() { + local file + + while IFS= read -r file; do + if is_supported_image_file "$file"; then + printf '%s\n' "$file" + fi + done < <(find "$INCOMING_DIR" -maxdepth 1 -type f -printf '%f\n') \ + | sort +} + +warn_unsupported_incoming_files() { + local file + + while IFS= read -r file; do + if ! is_supported_image_file "$file"; then + echo "WARNING: Ignoring unsupported incoming file: $file" >&2 + fi + done < <(find "$INCOMING_DIR" -maxdepth 1 -type f -printf '%f\n' | sort) +} + scalephotos() { local destphoto local dirname @@ -342,10 +380,7 @@ scalephotos() { -auto-orient \ "$destphoto" fi - done < <( - find "$INCOMING_DIR" -maxdepth 1 -type f -printf '%f\n' \ - | sort - ) + done < <(incoming_image_files) } random_animation_css_class() { @@ -568,6 +603,10 @@ count_files() { fi } +count_incoming_images() { + incoming_image_files | wc -l +} + count_tree_files() { local -r dir="$1"; shift local -r name="$1"; shift @@ -585,7 +624,7 @@ write_generation_metadata() { local -r generated_at=$(date -u +'%Y-%m-%dT%H:%M:%SZ') local -r config_source="${PHOTOALBUM_CONFIG_SOURCE:-}" local -r template_name=$(basename "$TEMPLATE_DIR") - local -r source_image_count=$(count_files "$INCOMING_DIR") + local -r source_image_count=$(count_incoming_images) local -r generated_photo_count=$(count_files "$DIST_DIR/photos") local -r generated_thumb_count=$(count_files "$DIST_DIR/thumbs") local -r generated_html_count=$(count_tree_files "$DIST_DIR" '*.html') @@ -641,6 +680,7 @@ generate() { tarball_name="${base}-${now}${TARBALL_SUFFIX:-.tar}" fi + warn_unsupported_incoming_files mkdir -p "$DIST_DIR/photos" cleanphotos scalephotos diff --git a/tests/cli.sh b/tests/cli.sh index 33e4d44..bcad987 100755 --- a/tests/cli.sh +++ b/tests/cli.sh @@ -111,8 +111,11 @@ assert metadata["config_source"] == config_source assert metadata["template"]["directory"] == template_dir assert metadata["template"]["name"] == template_path.name assert metadata["source"]["incoming_dir"] == incoming_dir +supported_extensions = {".gif", ".jpeg", ".jpg", ".png", ".webp"} assert metadata["source"]["image_count"] == sum( - 1 for path in incoming_path.iterdir() if path.is_file() + 1 + for path in incoming_path.iterdir() + if path.is_file() and path.suffix.lower() in supported_extensions ) assert metadata["generated"]["photo_count"] == sum( 1 for path in (dist_path / "photos").iterdir() if path.is_file() @@ -499,6 +502,72 @@ test_generate_cli_no_tarball_overrides_config() { test::teardown } +test_generate_ignores_unsupported_incoming_files_with_warning() { + local config_file + local fake_bin + local output + + test::setup + fake_bin="$TEST_TMPDIR/bin" + config_file="$TEST_TMPDIR/photoalbum.conf" + + test::install_fake_imagemagick "$fake_bin" + mkdir -p "$TEST_TMPDIR/incoming" + "$TEST_IMAGEMAGICK" -size 160x90 xc:red \ + "$TEST_TMPDIR/incoming/01-upper.JPG" + "$TEST_IMAGEMAGICK" -size 160x90 xc:red \ + "$TEST_TMPDIR/incoming/02-photo.jpeg" + "$TEST_IMAGEMAGICK" -size 160x90 xc:red \ + "$TEST_TMPDIR/incoming/03-photo.png" + "$TEST_IMAGEMAGICK" -size 160x90 xc:red \ + "$TEST_TMPDIR/incoming/04-photo.webp" + "$TEST_IMAGEMAGICK" -size 160x90 xc:red \ + "$TEST_TMPDIR/incoming/05-photo.gif" + printf 'notes\n' > "$TEST_TMPDIR/incoming/notes.txt" + printf '# album notes\n' > "$TEST_TMPDIR/incoming/README.md" + mkdir -p "$TEST_TMPDIR/dist/photos" + printf 'stale cached unsupported file\n' \ + > "$TEST_TMPDIR/dist/photos/notes.txt" + printf 'stale cached unsupported file\n' \ + > "$TEST_TMPDIR/dist/photos/README.md" + test::write_album_config \ + "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \ + 'Extension filter album' 40 + + output=$( + cd "$TEST_TMPDIR" + PATH="$fake_bin:$PATH" "$TEST_PHOTOALBUM" --generate 2>&1 + ) + + test::assert_file_exists "$TEST_TMPDIR/dist/photos/01-upper.JPG" + test::assert_file_exists "$TEST_TMPDIR/dist/photos/02-photo.jpeg" + test::assert_file_exists "$TEST_TMPDIR/dist/photos/03-photo.png" + test::assert_file_exists "$TEST_TMPDIR/dist/photos/04-photo.webp" + test::assert_file_exists "$TEST_TMPDIR/dist/photos/05-photo.gif" + test::assert_path_absent "$TEST_TMPDIR/dist/photos/notes.txt" + test::assert_path_absent "$TEST_TMPDIR/dist/photos/README.md" + test::assert_contains \ + 'WARNING: Ignoring unsupported incoming file: README.md' \ + "$output" + test::assert_contains \ + 'WARNING: Ignoring unsupported incoming file: notes.txt' \ + "$output" + test::assert_not_contains 'Processing notes.txt' "$output" + test::assert_not_contains 'Processing README.md' "$output" + + python3 - "$TEST_TMPDIR/dist/photoalbum.json" <<'PY' +import json +import pathlib +import sys + +metadata = json.loads(pathlib.Path(sys.argv[1]).read_text()) +assert metadata["source"]["image_count"] == 5 +assert metadata["generated"]["photo_count"] == 5 +PY + + test::teardown +} + test_generate_missing_incoming_fails() { local output @@ -1298,6 +1367,9 @@ main() { '--generate --no-tarball overrides config' \ test_generate_cli_no_tarball_overrides_config test::run_case \ + '--generate ignores unsupported incoming files with warning' \ + test_generate_ignores_unsupported_incoming_files_with_warning + test::run_case \ '--generate missing incoming fails' \ test_generate_missing_incoming_fails test::run_case \ |
