summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-15 18:16:47 +0300
committerPaul Buetow <paul@buetow.org>2026-06-15 23:50:41 +0300
commitb684fa5113239e595ecd1ccbcbd267ebbed9e62d (patch)
tree64b0a196f8510a3e974bdae114dff52f4fe5eb9f
parent9d7c6ec3de935ef846e9f9a0512129990a1ddec4 (diff)
Persist the EXIF cache in a volatile ./cache dir parallel to ./dist
The per-photo `identify -verbose` cache lived in $DIST_DIR/.shuriken-cache, which is inside the throwaway staging dir and the published dist: a fresh/cleared dist lost it (forcing a full, slow re-identify of every original) and it got deployed to web servers. Move it to a volatile ./cache/exif directory parallel to ./dist (dirname of DIST_DIR resolves to the working dir in both the staging and direct contexts). Now an unchanged photo skips identify on every regenerate, the cache survives a cleared dist, and it is never deployed. --force clears ./cache/exif once up front (clear_exif_cache) and the run repopulates it, so force still does exactly one identify per photo. Dropped .shuriken-cache from the staging-copy loop. Tests and the audit doc updated for the new cache location. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-rwxr-xr-xbin/shuriken26
-rw-r--r--docs/stats-exif-audit.md4
-rw-r--r--src/lib/album.source.sh24
-rw-r--r--src/lib/config.staging.source.sh2
-rwxr-xr-xtests/cli.sh9
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