summaryrefslogtreecommitdiffstats
path: root/app.rb
AgeCommit message (Collapse)AuthorFilesLines
12 hoursFix bulk action 400: Rack maps entries[] to params['entries'] not ↵HEADmainKen D'Ambrosio1-3/+3
params['entries[]'] Rack::Utils.parse_nested_query maps form fields named 'entries[]' to params['entries'] (without brackets). The route was reading params['entries[]'] which was always nil, making entries always empty and triggering the halt 400 guard. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
12 hoursMove bulk selection to admin cluster detail page (correct page)Ken D'Ambrosio1-0/+40
The bulk selection panel now lives on /admin/people/:uuid — the face crop grid page — which is what was actually requested. A sticky left panel shows the cluster name, the name form, a selection counter, and bulk action controls. Clicking a face crop toggles selection; clicking the photo link still opens the album. Bulk actions: move selected faces to a named person, move to pool, or blacklist. The per-face individual dropdowns are replaced by the panel. Merge-entire-cluster and Blacklist-cluster moved to collapsible/button in the panel too. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
12 hoursAdd bulk photo selection panel to person page (admin)Ken D'Ambrosio1-1/+112
On /people/:slug, admins see a sticky left panel and selectable photo tiles. Clicking a tile (or its checkbox overlay) toggles selection; clicking without modifier still opens the photo in a new tab. The panel shows the selection count and two actions: - Reassign to person: moves the selected photos' face entries from the current person's cluster to the chosen person. - Move to album: moves the photo files on disk and updates album.json, faces.json, and people.json rel paths accordingly. Album paths are offered via a datalist autocomplete. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 daysFix New Person flow: redirect to new cluster, show hero face, detect ↵Ken D'Ambrosio1-1/+12
duplicate names After moving a face to "New Person", the user is now taken directly to that cluster's detail page. If it's a single unnamed cluster, the face is shown prominently at the top. Typing an existing name on the name form triggers a confirm dialog: OK merges into the existing person's cluster, Cancel saves as a new separate person with the same name. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 daysAdd face pool, blacklisting, and action explanations to people adminKen D'Ambrosio1-11/+41
Removed faces now go to an "Unidentified pool" cluster rather than disappearing. Deleting a cluster blacklists all its members so they are skipped by future re-clustering runs. Pool faces can be assigned to a named person or individually blacklisted. A plain-English info box on the detail page explains what each action does and that no photo files are ever modified. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 daysAdd face and cluster deletion to people adminKen D'Ambrosio1-1/+14
On the cluster detail page: "Remove face" option in each face's move dropdown removes it from the cluster entirely; "Delete cluster" button (red, with confirmation) removes the whole cluster from people.json. Moving the last face out of a cluster also auto-deletes it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 daysAdd people cluster detail page with face move/merge and hover previewKen D'Ambrosio1-0/+77
Each cluster in /admin/people now links to a detail page showing all faces in a grid. From there you can rename the cluster, move individual faces to another named person (or spin off a new cluster), or merge the entire cluster into another. Hovering any face crop shows the original full photo for context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 daysAdd photo search with Boolean operatorsKen D'Ambrosio1-0/+157
- Server-side search index built from all album.json files + people.json, cached in memory for 5 minutes. Each photo document includes filename, album path words, title, caption, camera, date parts (year/month-name/ full date), and person names. - Recursive-descent Boolean parser: AND (explicit or implicit between consecutive terms), OR, NOT, with standard precedence. - GET /search?q=... returns a photo grid (max 300 results) linking each photo back to its album lightbox. - Search box added to the site header; hidden on mobile. - Results show filename, date, person names, and album path per photo. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 daysAdd people/face clustering featureKen D'Ambrosio1-0/+177
- scripts/cluster_faces.py: greedy centroid clustering (numpy) with 3 refinement passes; preserves existing UUID/name mappings across re-runs; writes MEDIA_ROOT/people.json atomically. - app.rb: GET /face/* serves cropped+padded face thumbnails (100x100, cached under cache/faces/); GET|POST /admin/people for cluster management; POST /admin/people/recluster runs cluster_faces.py as a background job; POST /admin/people/:uuid saves names+slugs; GET /people public grid of named people; GET /people/:slug photos for one person. - views/admin/people.erb: lists all clusters (named first, then by size), face crop samples, inline name form, re-cluster button with live log. - views/people.erb: public grid of named people. - views/person.erb: photo grid for one person, linking back to album lightbox for each photo. - views/layout.erb: People link in nav (conditional on FACES_ENABLED). - public/css/style.css: styles for people admin list and public tiles. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 daysSeparate face detection into standalone daemonKen D'Ambrosio1-1/+13
- 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>
2026-05-22Speed up update.rb and fix UI always forcing full rescanKen D'Ambrosio1-1/+71
- update.rb: skip exiftool on images marked exif_absent (set after first failed attempt); prevents repeated slow scans of old photos with no EXIF - update.rb: explicit directory argument now implies force — passing a path always rescans that subtree regardless of sentinel mtime - app.rb: /admin/update no longer hardcodes --force; sentinel-based skipping is used by default, making UI updates finish in seconds instead of minutes - admin/album.erb: add "Force rescan all" checkbox to Run Update button; checked state passes force=1 to the server and restores --force behavior - README.md, DESIGN.md: document sentinel skipping, exif_absent flag, and explicit-directory force behavior Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14Hide transcoded originals from non-admins; mark them visually for adminsKen D'Ambrosio1-13/+15
update.rb records transcoded_to in album.json (even on re-runs where the MP4 already exists) so the marker survives across scans. app.rb filters files with transcoded_to from non-admin views. album.erb renders them greyed-out with an amber "⚠ original" badge in admin mode. admin/album.erb marks the edit-table row and shows the target filename under the original. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14Add per-photo delete checkbox to admin edit formKen D'Ambrosio1-1/+12
Checking Delete and saving permanently removes the file and its thumbnail; a JS confirm dialog gates the submit. Deleted files are stripped from params before save_edits so they don't linger in album.json. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13Add folder rename and background update trigger to admin UIKen D'Ambrosio1-0/+67
- Admin edit form: "Folder name" field renames the directory on save; also moves the thumbnail cache subtree to match the new path - Admin edit page: "Run Update" button spawns update.rb in a background thread, streams output into a terminal-style log panel via 1.5s polling; shows Done/Error status when complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12Use albumen.png as OG image for root URLKen D'Ambrosio1-3/+3
Root album leaves @og_image_rel nil so layout.erb falls back to /img/albumen.png; photo-based OG image only applies to sub-albums. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12Show slideshow button for folders with media anywhere in hierarchyKen D'Ambrosio1-3/+1
- slideshow_view now uses all_media_entries for non-root dirs so the slideshow traverses sub-albums instead of only direct files - album.erb condition simplified: show launcher when albums OR media entries exist (previously hidden for folders with only sub-albums) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12Add photo counts, EXIF details, video duration badges, slideshow launcher UIKen D'Ambrosio1-0/+30
- Album cards show recursive photo count (bubbles up through sub-albums). - Lightbox info panel shows camera, aperture, shutter speed, and ISO; update.rb now extracts and stores these EXIF fields. - Video thumbnail cards show a duration badge (e.g. "1:23"). - Slideshow launcher redesigned: button on its own line, with Shuffle / Full screen / Interval options on a second line, all inside a rounded border to make the grouping clear. - Fixed album-actions alignment so Interval sits level with the checkboxes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11v1.01: replace bcrypt with PBKDF2-SHA256; update README and DESIGN docsv1.01Ken D'Ambrosio1-2/+11
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11Fix OG image previews: use ?photo= param so server can set correct og:imagev1.0Ken D'Ambrosio1-11/+18
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11Add Open Graph meta tags for social media link previewsKen D'Ambrosio1-0/+14
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11Replace cover dropdown with Random checkbox and per-file cover radioKen D'Ambrosio1-3/+6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11Limit slideshow to filtered albums when search filter is activeKen D'Ambrosio1-3/+12
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11Reverse sub-album listing order on admin edit pageKen D'Ambrosio1-1/+1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10Add slideshow: root-level, shuffle, fullscreen, click-to-albumKen D'Ambrosio1-2/+20
- Root slideshow: all_media_entries walks the full media tree so /slideshow/ shows every photo across all albums; Slideshow button always appears on the root album page - Shuffle and Full screen checkboxes sit next to the Slideshow button on the album page; options pass as ?shuffle=1&fullscreen=1 URL params - Fullscreen uses a tap-to-activate overlay (browsers block auto-entry on page load); webkit-prefixed for Safari; ⛶ button and F key for mid-session toggle - Fullscreen mode hides controls, counter, caption bar, and site header - Exiting fullscreen auto-pauses so the current photo stays visible - Click/tap anywhere in the stage navigates to the photo's album lightbox; reads the live src attribute instead of ssIdx to avoid a race where ssIdx advances during the cross-fade while the old photo is still on screen - layout.erb excluded from slideshow (layout: false) so the site header never appears there - CSS cache-busted with ?v=2 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09Add info overlay to lightbox showing photo metadataKen D'Ambrosio1-0/+2
Tap "ℹ Info" in the caption bar to toggle a semi-transparent panel at the bottom of the image showing filename (if different from title), date taken, and pixel dimensions. Panel resets to hidden on each photo change. Button is hidden automatically when no metadata is available. Width/height are now included in the ENTRIES payload. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09Search subdirs recursively for album cover candidatesKen D'Ambrosio1-6/+11
Albums with no top-level photos (only sub-albums) now find a cover by walking the full directory tree. Extracted cover_candidates() does a recursive glob and returns paths relative to the album dir so the existing thumb URL construction works unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09Replace dynamic cover checkbox with random image dropdown optionKen D'Ambrosio1-8/+9
Adds "— random image —" as a selectable cover option (stored as __random__ sentinel). album_cover() now picks a random eligible file when that value is set, giving a fresh cover on each page load. Removes the cover_dynamic field that was saved but never actually used in display logic. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09Initial commit — Albumen photo albumKen1-0/+371
Ruby/Sinatra self-hosted photo album with directory hierarchy, per-photo captions and visibility, lightbox, slideshow, admin UI, and Let's Encrypt HTTPS via Apache reverse proxy on prouter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>