Commit Graph
13 Commits
Author SHA1 Message Date
nmemmertandClaude Sonnet 4.6 a072090017 fix: make /photos writable on server so icloudpd can create download folders
Worker stays read-only; server needs write access to sync iCloud photos.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-18 10:18:53 -04:00
nmemmertandClaude Sonnet 4.6 586b783239 fix: remove invalid --no-progress flag from icloudpd command
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-18 10:11:27 -04:00
nmemmertandClaude Sonnet 4.6 4ff5a6c8ca feat: photo sync (iCloud + Amazon import), larger gear icon, Luma branding
- Settings: iCloud Photos section with icloudpd-backed sync — Apple ID,
  password, download path, cookie dir, live log output via SSE
- Settings: Amazon Photos one-time import — path to extracted data export,
  Add & Index button wires it directly into Watch Folders
- scripts/amazon_sync.py: stdlib-only downloader using Netscape cookie file
- server: POST /api/sync/icloud, /api/sync/amazon, GET /api/sync/stream
  (SSE log streaming matching ingest stream pattern)
- db: icloud/amazon setting keys; getSettings handles bool + string fields
- Gear icon: fontSize 16→20, button 38×38, color #555→#888
- favicon.svg: 6-blade aperture icon in Luma blue
- Browser tab title: shpm → Luma
- Dockerfile: installs icloudpd via pip3, copies scripts/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-18 09:30:43 -04:00
nmemmertandClaude Sonnet 4.6 5ff7f38b85 fix: correct volume types and photo path in docker-compose
- Use explicit type: volume for the luma_data named volume so compose
  managers (CasaOS etc.) don't misinterpret it as a bind mount
- Use type: bind / read_only: true for the photo directory
- Both server and worker now reference /media/Plex/Gallery consistently
- Worker was previously left with the placeholder path, causing it to
  watch a non-existent directory and index nothing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-18 08:31:49 -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 64f7906b22 fix: create starred index after column migration, not before
The CREATE INDEX on photos(starred) ran inside the main db.exec() block,
which fires before the ALTER TABLE migration that adds the starred column.
On databases created before the starred feature, this crashed the process
on startup with "no such column: starred" — causing the worker container
to exit silently with no logs.

Move the index creation to after the migration so the column always exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-18 08:11:14 -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 8c5c36857f perf: skip already-indexed files before queuing on worker startup
Load all known filepaths into an in-memory Set on startup (one bulk query).
Chokidar's initial 'add' burst now hits an O(1) Set lookup instead of going
through the serial drain queue with a per-file DB round-trip — a 100k-photo
library restarts in seconds rather than re-processing every file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-18 07:53:42 -04:00
nmemmertandClaude Sonnet 4.6 f0e237ab2b feat: filter sidebar, ingest progress, starred photos
- Left filter sidebar: All Photos / Favorites / Date by Year / Tags / Collections
  with counts, See more/less toggles, and blue active-row highlight
- SSE ingest badge in header: pulsing dot + filename + indexed count during scan;
  auto-refreshes the library when a scan finishes
- Starred/Favorites: starred column on photos, star button in lightbox toolbar,
  Favorites row in sidebar, ?starred=1 filter on /api/photos
- GET /api/library/stats endpoint (totals, years, tags, collections)
- GET /api/ingest/stream SSE endpoint (polls ingest_status table every 1.5s)
- Worker writes ingest_status (active, current file, scanned count) to SQLite

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 17:04:01 -04:00
nmemmertandClaude Sonnet 4.6 4df32f9f32 chore: pull image from ghcr.io instead of building locally
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 16:36:52 -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
nmemmert 03e6d6b628 Update TODO.md 2026-08-17 12:59:20 -04:00
nmemmertandClaude Sonnet 4.6 309a6723d7 Add architecture TODO and gitignore
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 11:01:47 -04:00