diff options
| -rwxr-xr-x | bin/shuriken | 26 | ||||
| -rw-r--r-- | docs/stats-exif-audit.md | 4 | ||||
| -rw-r--r-- | src/lib/album.source.sh | 24 | ||||
| -rw-r--r-- | src/lib/config.staging.source.sh | 2 | ||||
| -rwxr-xr-x | tests/cli.sh | 9 |
5 files changed, 56 insertions, 9 deletions
diff --git a/bin/shuriken b/bin/shuriken index a4ec86d..89d7f94 100755 --- a/bin/shuriken +++ b/bin/shuriken @@ -1675,10 +1675,19 @@ cached_photo_identify_output() { local cached_signature='' local current_signature - cache_dir="$DIST_DIR/.shuriken-cache/exif" + # Persist the EXIF cache in a volatile ./cache directory parallel to ./dist + # (the staging dir is a sibling of the final dist, so dirname "$DIST_DIR" is + # the working dir in both staging and direct contexts). Keeping it outside + # dist means it survives a fresh/cleared dist and is never deployed, so an + # unchanged photo skips the slow `identify -verbose` on every regenerate. + cache_dir="$(dirname "$DIST_DIR")/cache/exif" cache_file="$cache_dir/$photo.txt" current_signature=$(photo_cache_signature "$photo" "$photo_path") + # Reuse the cache when its signature still matches the source file. --force + # is handled once up front by clear_exif_cache (which empties this directory), + # so the first call per photo then rebuilds it and the rest of the run reuses + # it -- exactly one identify per photo even under force. if [ -f "$cache_file" ]; then IFS= read -r cached_signature < "$cache_file" || true if [ "$cached_signature" = "$current_signature" ]; then @@ -2581,6 +2590,16 @@ create_generation_archive() { fi } +# Empty the volatile EXIF cache (./cache/exif, parallel to ./dist) so a --force +# run re-runs `identify` from scratch. Done once up front; the cache then +# repopulates and is reused for the rest of the run (one identify per photo). +clear_exif_cache() { + local -r cache_dir="$(dirname "$DIST_DIR")/cache/exif" + + log_verbose "Force generation; clearing EXIF cache $cache_dir" + rm -rf "$cache_dir" +} + # Aggregate EXIF stats and render the stats page plus the per-camera pages into # the dist root (html_dir and backhref are '.', matching render_album_pages). # Run after the album pages so the per-photo identify cache is already warm. @@ -2603,6 +2622,9 @@ generate() { log_verbose 'Tarball disabled; no archive will be created' fi + if [ "$SHURIKEN_FORCE_GENERATE" = yes ]; then + clear_exif_cache + fi prepare_generation_photo_assets prepare_generation_site_assets clear_rendered_html @@ -4169,7 +4191,7 @@ prepare_generation_staging_dir() { return fi - for cache_dir in photos thumbs blurs .shuriken-cache; do + for cache_dir in photos thumbs blurs; do if [ -d "$final_dist/$cache_dir" ]; then if ! mkdir -p "$staging_dir/$cache_dir"; then return 1 diff --git a/docs/stats-exif-audit.md b/docs/stats-exif-audit.md index 7076254..1b1023b 100644 --- a/docs/stats-exif-audit.md +++ b/docs/stats-exif-audit.md @@ -13,8 +13,8 @@ library (there is none to point at): * `src/lib/imagemagick.source.sh` runs `magick identify -verbose <file>` (or `convert <file> -verbose info:` when only the legacy `convert` is present). -* `src/lib/album.source.sh` caches that raw output per photo under - `$DIST_DIR/.shuriken-cache/exif/<photo>.txt` and parses it. +* `src/lib/album.source.sh` caches that raw output per photo under a volatile + `./cache/exif/<photo>.txt` (parallel to `./dist`) and parses it. The parser detail that drives every decision below: shuriken only reads EXIF through this regex (`photo_exif_details_html`, `_photo_exif_values_to`): diff --git a/src/lib/album.source.sh b/src/lib/album.source.sh index 7225492..17502ff 100644 --- a/src/lib/album.source.sh +++ b/src/lib/album.source.sh @@ -160,10 +160,19 @@ cached_photo_identify_output() { local cached_signature='' local current_signature - cache_dir="$DIST_DIR/.shuriken-cache/exif" + # Persist the EXIF cache in a volatile ./cache directory parallel to ./dist + # (the staging dir is a sibling of the final dist, so dirname "$DIST_DIR" is + # the working dir in both staging and direct contexts). Keeping it outside + # dist means it survives a fresh/cleared dist and is never deployed, so an + # unchanged photo skips the slow `identify -verbose` on every regenerate. + cache_dir="$(dirname "$DIST_DIR")/cache/exif" cache_file="$cache_dir/$photo.txt" current_signature=$(photo_cache_signature "$photo" "$photo_path") + # Reuse the cache when its signature still matches the source file. --force + # is handled once up front by clear_exif_cache (which empties this directory), + # so the first call per photo then rebuilds it and the rest of the run reuses + # it -- exactly one identify per photo even under force. if [ -f "$cache_file" ]; then IFS= read -r cached_signature < "$cache_file" || true if [ "$cached_signature" = "$current_signature" ]; then @@ -1066,6 +1075,16 @@ create_generation_archive() { fi } +# Empty the volatile EXIF cache (./cache/exif, parallel to ./dist) so a --force +# run re-runs `identify` from scratch. Done once up front; the cache then +# repopulates and is reused for the rest of the run (one identify per photo). +clear_exif_cache() { + local -r cache_dir="$(dirname "$DIST_DIR")/cache/exif" + + log_verbose "Force generation; clearing EXIF cache $cache_dir" + rm -rf "$cache_dir" +} + # Aggregate EXIF stats and render the stats page plus the per-camera pages into # the dist root (html_dir and backhref are '.', matching render_album_pages). # Run after the album pages so the per-photo identify cache is already warm. @@ -1088,6 +1107,9 @@ generate() { log_verbose 'Tarball disabled; no archive will be created' fi + if [ "$SHURIKEN_FORCE_GENERATE" = yes ]; then + clear_exif_cache + fi prepare_generation_photo_assets prepare_generation_site_assets clear_rendered_html diff --git a/src/lib/config.staging.source.sh b/src/lib/config.staging.source.sh index 8a5f8f1..ebb2694 100644 --- a/src/lib/config.staging.source.sh +++ b/src/lib/config.staging.source.sh @@ -19,7 +19,7 @@ prepare_generation_staging_dir() { return fi - for cache_dir in photos thumbs blurs .shuriken-cache; do + for cache_dir in photos thumbs blurs; do if [ -d "$final_dist/$cache_dir" ]; then if ! mkdir -p "$staging_dir/$cache_dir"; then return 1 diff --git a/tests/cli.sh b/tests/cli.sh index cabd96f..d2140e3 100755 --- a/tests/cli.sh +++ b/tests/cli.sh @@ -5444,13 +5444,16 @@ test_stats_bucket_boundaries_and_datetime_parsing() { test_stats_collect_reads_cached_identify_output() { local incoming_dir local dist_dir + local cache_dir test::setup test::source_shuriken_lib incoming_dir="$TEST_TMPDIR/incoming" dist_dir="$TEST_TMPDIR/dist" - mkdir -p "$incoming_dir" "$dist_dir/.shuriken-cache/exif" + # The EXIF cache lives in ./cache parallel to ./dist (dirname of DIST_DIR). + cache_dir="$TEST_TMPDIR/cache/exif" + mkdir -p "$incoming_dir" "$cache_dir" printf 'fake\n' > "$incoming_dir/one.jpg" printf 'fake\n' > "$incoming_dir/two.jpg" @@ -5463,12 +5466,12 @@ test_stats_collect_reads_cached_identify_output() { photo_cache_signature 'one.jpg' "$incoming_dir/one.jpg" printf ' exif:Make: Nikon\n' printf ' exif:Model: Nikon Z6\n' - } > "$dist_dir/.shuriken-cache/exif/one.jpg.txt" + } > "$cache_dir/one.jpg.txt" { photo_cache_signature 'two.jpg' "$incoming_dir/two.jpg" printf ' exif:Make: Nikon\n' printf ' exif:Model: Nikon Z6\n' - } > "$dist_dir/.shuriken-cache/exif/two.jpg.txt" + } > "$cache_dir/two.jpg.txt" collect_photo_exif_stats |
