diff options
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/update.rb | 36 |
1 files changed, 31 insertions, 5 deletions
diff --git a/scripts/update.rb b/scripts/update.rb index 9953505..822405f 100644 --- a/scripts/update.rb +++ b/scripts/update.rb @@ -1,9 +1,12 @@ #!/usr/bin/env ruby # frozen_string_literal: true # -# Usage: ruby update.rb [relative/path] -# Without argument: process entire MEDIA_ROOT tree. -# With argument: process only that subdirectory (and its children). +# Usage: ruby update.rb [--force] [relative/path] +# Without argument: process entire MEDIA_ROOT tree, skipping directories +# whose mtime hasn't changed since the last scan. +# With argument: process only that subdirectory (and its children), +# always scanning regardless of mtime (explicit request). +# --force: scan entire tree ignoring all mtime sentinels. # # Resilience guarantees: # - album.json is written atomically (temp-file + rename), so a crash @@ -11,6 +14,8 @@ # - Thumbnails are checked before generation; already-done work is skipped. # - EXIF and dimension extraction are skipped if already recorded. # - Safe to re-run at any time; all operations are idempotent. +# - Unchanged directories are skipped via a .albumen_scanned sentinel file; +# pass --force to bypass. require 'json' require 'yaml' @@ -27,12 +32,25 @@ VIDEO_EXTS = %w[mp4 mov avi mkv webm m4v ogv].freeze AUDIO_EXTS = %w[mp3 flac ogg wav m4a aac].freeze MEDIA_EXTS = (IMAGE_EXTS + VIDEO_EXTS + AUDIO_EXTS).freeze TRANSCODE_EXTS = %w[avi mkv mov].freeze # not universally browser-playable; convert to MP4 +SENTINEL_FILE = '.albumen_scanned'.freeze + +# Explicit directory argument implies force — you asked for it, it should run. +FORCE_UPDATE = !!(ARGV.delete('--force') || ARGV[0]) # ── Directory processing ─────────────────────────────────────────────────────── def process_dir(dir) rel = dir.delete_prefix(MEDIA_ROOT).delete_prefix('/') label = rel.empty? ? '(root)' : rel + + unless FORCE_UPDATE + sentinel = File.join(dir, SENTINEL_FILE) + if File.exist?(sentinel) && File.mtime(sentinel) >= File.mtime(dir) + puts "Skipping #{label} (unchanged)" + return + end + end + puts "Scanning #{label}" json_path = File.join(dir, 'album.json') @@ -102,13 +120,15 @@ def process_dir(dir) end atomic_write_json(json_path, data) + FileUtils.touch(File.join(dir, SENTINEL_FILE)) end # ── Metadata enrichment ──────────────────────────────────────────────────────── def enrich_image(full, name, meta) - needs_exif = meta['taken_at'].nil? || meta['camera'].nil? || - meta['aperture'].nil? || meta['shutter'].nil? || meta['iso'].nil? + needs_exif = !meta['exif_absent'] && + (meta['taken_at'].nil? || meta['camera'].nil? || + meta['aperture'].nil? || meta['shutter'].nil? || meta['iso'].nil?) if needs_exif begin exif = MiniExiftool.new(full, numerical: false) @@ -133,6 +153,12 @@ def enrich_image(full, name, meta) rescue StandardError => e warn " #{name}: EXIF error — #{e.message}" end + + # If exiftool found nothing at all, record that so we don't retry on every re-scan. + if meta['taken_at'].nil? && meta['camera'].nil? && + meta['aperture'].nil? && meta['shutter'].nil? && meta['iso'].nil? + meta['exif_absent'] = true + end end # Dimensions (skip if already recorded) |
