diff options
| author | Ken D'Ambrosio <ken@jots.org> | 2026-06-08 18:36:07 +0000 |
|---|---|---|
| committer | Ken D'Ambrosio <ken@jots.org> | 2026-06-08 18:36:07 +0000 |
| commit | 625b3d5176f2c274e91fcf28bda8e45cc0477722 (patch) | |
| tree | 6ca16ad6f4a830b65dcddbd78ad7e7a2f1655682 /app.rb | |
| parent | ecc872a1fd43c0863e3171a1faf533adc3e3a4c5 (diff) | |
Separate face detection into standalone daemon
- Strip all face code from update.rb; add shared log helper writing to
/opt/albumen/log/albumen.log with [update] prefix. update.rb now owns
only album.json; face_daemon.rb owns faces.json.
- New scripts/face_daemon.rb: polls MEDIA_ROOT for unprocessed images,
calls faces.py in batches, writes per-directory faces.json sidecars
atomically. Graceful SIGTERM/SIGINT shutdown between directories.
- New config/face_daemon.service: systemd unit running as albumen user,
Restart=on-failure, logs via SyslogIdentifier=albumen-faces.
- app.rb: add FACES_ENABLED constant; load_faces() helper reads faces.json;
album_files() merges face data into each entry as :faces field.
- Update README.md and DESIGN.md to document the new daemon architecture,
faces.json schema, and service management commands.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'app.rb')
| -rw-r--r-- | app.rb | 14 |
1 files changed, 13 insertions, 1 deletions
@@ -23,7 +23,8 @@ 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 -APP_CONFIG = (File.exist?(CONFIG_PATH) ? YAML.load_file(CONFIG_PATH, symbolize_names: true) : {}).freeze +APP_CONFIG = (File.exist?(CONFIG_PATH) ? YAML.load_file(CONFIG_PATH, symbolize_names: true) : {}).freeze +FACES_ENABLED = (APP_CONFIG.dig(:faces, :enabled) == true).freeze # ── Sinatra config ───────────────────────────────────────────────────────────── @@ -83,7 +84,17 @@ helpers do { 'files' => {}, 'visible' => true } end + def load_faces(dir) + path = File.join(dir, 'faces.json') + return {} unless File.exist?(path) + JSON.parse(File.read(path)) + rescue JSON::ParserError + {} + end + def album_files(dir, data) + face_data = FACES_ENABLED ? load_faces(dir) : {} + files = Dir.children(dir) .sort .select { |n| MEDIA_EXTS.include?(File.extname(n).downcase.delete_prefix('.')) } @@ -107,6 +118,7 @@ helpers do shutter: meta['shutter'], iso: meta['iso'], transcoded_to: meta['transcoded_to'], + faces: face_data[name], } end |
