6 Commits
Author SHA1 Message Date
nmemmertandClaude Sonnet 4.6 51c752602a feat: Albums view, auto albums, EXIF details, download, bulk select
Albums tab with manual album CRUD (create/delete, cover photo grid)
and Smart Albums auto-generated by year, city, and camera model using
SQLite window functions — no extra storage, computed from existing data.

Lightbox gets a download-original button (streams raw file with
Content-Disposition: attachment).

DetailPanel Camera section expands to show ISO, aperture, shutter speed,
focal length, and lens model extracted at ingest time; five new DB columns
added via safe ALTER TABLE migration.

Bulk selection: hover a photo to reveal a circle checkbox; selecting
any photo activates a floating action bar for star/unstar and add-to-album
across the whole selection. PhotoCard wrapped in React.memo to limit
re-renders to only affected cards.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-18 15:02:52 -04:00
nmemmertandClaude Sonnet 4.6 3c0cb2b68d fix: GPS extraction broken by exifr pick filter — use exifr.gps() separately
The pick option creates an allowlist for the merged EXIF output, which
drops latitude/longitude even when gps:true is set. Fixed by calling
exifr.gps() as a separate step (bypasses the pick filter entirely).

Also adds a startup backfill that re-extracts GPS for already-indexed
photos where gps_lat IS NULL, including reverse geocoding for place names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-18 11:48:11 -04:00
nmemmertandClaude Sonnet 4.6 6b4bd88943 feat: video support + Memories music choices
Videos:
- Worker ingests .mp4 .mov .m4v .avi .mkv .webm .3gp via ffmpeg/ffprobe
  (frame extract → sharp resize, same thumbnail pipeline as photos)
- DB: media_type + duration columns with migration
- GET /api/videos returns all indexed videos
- GET /api/photos/:id/video streams original file with Range support
  for browser seek/scrub
- VideosView: 16:9 thumbnail grid, play-button overlay, duration badge
- Full-screen player: native <video controls>, ESC to close
- Library tab now excludes videos (dedicated Videos tab)
- Dockerfile: adds ffmpeg to runtime stage

Memories music choices:
- Three styles: ⚡ Upbeat (G-major, default), 🌙 Dreamy (A-minor),
  🎬 Cinematic (D-minor, dual-oscillator richness)
- Shared Web Audio engine (reverb, LPF, pad + melody)
- Style picker in header; active style icon shown in slideshow bar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-18 11:01:30 -04:00
nmemmertandClaude Sonnet 4.6 3429ec1d80 feat: places (reverse geocoding) + folder browser
Places:
- worker/geocode.js: Nominatim reverse geocoder, 1 req/sec rate limit,
  in-memory cache keyed to 3-decimal-place coordinates
- Ingest calls reverseGeocode() for any photo with GPS data; stores
  place_city and place_country on the photo row
- DB migrations add place_city / place_country columns + index on place_city
- Stats API returns cities grouped by count
- buildFilter() supports ?city= parameter
- FilterSidebar shows a Places section with city list + country sublabel

Folder browser:
- GET /api/fs/browse?path=… lists subdirectories (path-normalized to prevent traversal)
- Settings panel: 📁 button opens inline directory browser below the add
  input — navigate with up arrow / directory clicks, Select fills the input

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-18 08:27:41 -04:00
nmemmertandClaude Sonnet 4.6 d18c855571 feat: settings panel + multi-folder scanning
Settings panel (⚙ in header):
- Watch Folders: add/remove paths, toggle enabled; worker polls every 30s
  and calls watcher.add/unwatch without restart
- Ingestion: thumb/preview size and quality, applied per-file at ingest time
- Duplicate Detection: configurable Hamming distance threshold
- Danger Zone: clear all dismissed duplicate pairs

Multi-folder scanning:
- watch_folders table replaces single PHOTO_DIR env var
- PHOTO_DIR env var seeds the first entry on startup
- settings table with defaults for all tunable values
- Worker reads settings from DB on each drain cycle so changes are live

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-18 07:58:44 -04:00
nmemmertandClaude Sonnet 4.6 e0f4e8ff0d feat: initial Luma implementation
Full self-hosted photo manager with:
- Ingestion worker (chokidar + sharp + exifr, dHash perceptual hashing)
- SQLite schema with WAL mode (photos, tags, collections, duplicate_pairs)
- Express API with keyset pagination, tag/collection/search/date filters
- React + Vite UI: timeline, lightbox, detail panel, tag/collection editors
- Duplicate detection (Union-Find grouping) with review + dismiss UI
- Map view (Leaflet + OpenStreetMap, marker clustering)
- Docker multi-stage build + Compose with inline env config
- GitHub Actions: CI build check + ghcr.io image publish on main/tags

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 16:33:55 -04:00