diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-17 22:53:32 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-17 22:53:32 +0300 |
| commit | 7c94be57437ecdc9cadeb95bc4c26a25040163a9 (patch) | |
| tree | 72f8fbcc6766d7b9f9c57322fe01d58e5e2abbbd /src/lib/album-metadata.source.sh | |
| parent | 140e39466d2f3a69339a00492b0e9061c56ac192 (diff) | |
pn0 decouple stats from album internals
Stats reached directly into album-module internals: the private global
ALBUM_VIEW_PAGE_BY_PHOTO and the EXIF cache reader
cached_photo_identify_output. Introduce a clean boundary, behaviour and
generated HTML byte-identical.
- Promote the EXIF identify cache primitive (cached_photo_identify_output
plus its private helpers photo_cache_signature and
print_cached_photo_identify_output) out of album-metadata.source.sh into
a new shared src/lib/metadata-cache.source.sh, sourced before both album
and stats (right after metadata-label in LIB_SOURCES). It is a low-level
metadata primitive used by both consumers, so it no longer belongs to
album internals. Signature/behaviour unchanged.
- Add album_view_page_for_photo accessor in album-render.source.sh as the
documented public API; keep ALBUM_VIEW_PAGE_BY_PHOTO as the album's
private backing store. stats-filter-album.source.sh now calls the
accessor instead of indexing the global, so a change to album page
naming/caching stays contained in the album module.
- Add test_album_stats_decoupling_boundary asserting the accessor returns
the backing-store value and that the assembled bin/shuriken keeps the
cache primitive in the shared module and no longer indexes the global
from the stats filter section. Existing stats/album tests unchanged.
Verified: 3-image fixture (STATS_PAGE=yes, fixed seed) diff -r of stashed
original vs new build is byte-identical across all 47 dist files (only the
inherent generated_at timestamp normalized). just test, just shellcheck,
just check-generated and git diff --check all pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'src/lib/album-metadata.source.sh')
| -rw-r--r-- | src/lib/album-metadata.source.sh | 91 |
1 files changed, 6 insertions, 85 deletions
diff --git a/src/lib/album-metadata.source.sh b/src/lib/album-metadata.source.sh index cc500ae..01bf826 100644 --- a/src/lib/album-metadata.source.sh +++ b/src/lib/album-metadata.source.sh @@ -1,88 +1,9 @@ -photo_cache_signature() { - local -r photo="$1"; shift - local -r photo_path="$1"; shift - local stat_output - - stat_output=$(stat -c '%s:%Y' "$photo_path") - printf '%s:%s\n' "$photo" "$stat_output" -} - -print_cached_photo_identify_output() { - local -r cache_file="$1"; shift - local line - local skipped_signature=no - - while IFS= read -r line || [ -n "$line" ]; do - if [ "$skipped_signature" = no ]; then - skipped_signature=yes - continue - fi - printf '%s\n' "$line" - done < "$cache_file" -} - -cached_photo_identify_output() { - local -r photo="$1"; shift - local -r photo_path="$1"; shift - local cache_dir - local cache_file - local cached_signature='' - local current_signature - local identify_status - - # 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 - print_cached_photo_identify_output "$cache_file" - return - fi - fi - - mkdir -p "$cache_dir" - printf '%s\n' "$current_signature" > "$cache_file" - - # Capture the identify exit status instead of swallowing it with `|| true`. - # Errors are still hidden from stdout (so a corrupt photo does not pollute - # the EXIF output), but a non-zero status now drives a warning + no-cache - # rather than silently leaving a signature-only cache entry behind. - identify_status=0 - imagemagick_identify -verbose "$photo_path" >> "$cache_file" 2>/dev/null \ - || identify_status=$? - - if [ "$identify_status" -ne 0 ]; then - # Failed identify (corrupt photo, timeout, missing binary, ...): warn - # naming the photo and remove the cache file. Removing it is essential: - # a file holding only the signature line is a valid-looking cache hit, - # so the next run would silently reuse the empty result forever -- never - # retrying identify and never warning again (the original data-loss bug). - # Deleting it makes the next run retry and warn. - # - # We deliberately do NOT abort: this runs inside backgrounded render jobs - # under `set -euo pipefail`, and one unreadable photo must not kill the - # whole generation. The photo still renders, just with empty tooltip and - # stats, now accompanied by a warning. - rm -f "$cache_file" - log_warning \ - "could not read EXIF for $photo (ImageMagick identify failed);" \ - "tooltip/stats will be missing" - return 0 - fi - - print_cached_photo_identify_output "$cache_file" -} +# The EXIF identify cache primitive (cached_photo_identify_output and its +# private helpers photo_cache_signature / print_cached_photo_identify_output) +# was promoted to the shared metadata-cache.source.sh module (task pn0): it is a +# low-level metadata primitive consumed by both this album module and the stats +# aggregator, so it no longer belongs to album internals. The helpers below call +# cached_photo_identify_output through that shared module. photo_exif_details_html() { local -r photo="$1"; shift |
