feat(immich): wall-frame curation job + point kiosk at curated album (no LLM) #107

Merged
kblack0610 merged 2 commits from feat/immich-wall-curation into master 2026-07-09 06:23:46 +00:00
Owner

Delivers a curated family-photo feed to the wall tablets. Merging this takes the wall live (swaps the whole-library random feed for Katie's curated album) and ships the dormant nightly CronJob.

Two commits

  1. Curation machinery - immich-curate CronJob (curate.py + manifests, apps/immich/). python:3.12-slim + configMapGenerator (per spotify-concerts) in a CronJob (per flux-audit); pure stdlib. Ships DRY_RUN=true and needs the (out-of-band) immich-curation-secret to do anything, so it is dormant until set up per README.
  2. Kiosk go-live - sets KIOSK_ALBUM on immich-kiosk to the curated Wall Best (Katie) album UUID.

No LLM

Calibrated live against the 2.6.3 API. The Google-Takeout import has 0% camera EXIF / 0% face-recognition / 0 favorites, but ~36% carry a People/<name> tag (Takeout face groups) - the primary keeper signal. CLIP smart-search is a soft negative (no score exposed; ranks real photos highly for junk queries, so no hard cutoff). Hard excludes limited to filename/tiny/aspect.

Verified

  • kubectl kustomize builds both apps/immich and apps/immich-kiosk; curate.py compiles.
  • Ran the real curation for Katie: Wall Best (Katie) = 8000 assets, 100% People-tagged, 7660 HEIC + 340 JPEG, ZERO PNG/screenshots. Shared read-only to the kiosk user so it reads by UUID.

Out of scope / follow-up

  • Ken's side (his account has 0 albums) - skipped for now per decision.
  • Secret + DRY_RUN=false to enable the nightly maintenance job (see README -> Curation).
Delivers a curated family-photo feed to the wall tablets. **Merging this takes the wall live** (swaps the whole-library random feed for Katie's curated album) and ships the dormant nightly CronJob. ### Two commits 1. **Curation machinery** - `immich-curate` CronJob (`curate.py` + manifests, `apps/immich/`). python:3.12-slim + configMapGenerator (per spotify-concerts) in a CronJob (per flux-audit); pure stdlib. Ships `DRY_RUN=true` and needs the (out-of-band) `immich-curation-secret` to do anything, so it is dormant until set up per README. 2. **Kiosk go-live** - sets `KIOSK_ALBUM` on immich-kiosk to the curated `Wall Best (Katie)` album UUID. ### No LLM Calibrated live against the 2.6.3 API. The Google-Takeout import has 0% camera EXIF / 0% face-recognition / 0 favorites, but ~36% carry a `People/<name>` tag (Takeout face groups) - the primary keeper signal. CLIP smart-search is a **soft** negative (no score exposed; ranks real photos highly for junk queries, so no hard cutoff). Hard excludes limited to filename/tiny/aspect. ### Verified - `kubectl kustomize` builds both apps/immich and apps/immich-kiosk; `curate.py` compiles. - Ran the real curation for Katie: **Wall Best (Katie) = 8000 assets, 100% People-tagged, 7660 HEIC + 340 JPEG, ZERO PNG/screenshots.** Shared read-only to the kiosk user so it reads by UUID. ### Out of scope / follow-up - Ken's side (his account has 0 albums) - skipped for now per decision. - Secret + `DRY_RUN=false` to enable the nightly maintenance job (see README -> Curation).
Builds a per-account "Wall Best (...)" album so the wall tablets (immich-kiosk)
show great family photos instead of the whole library (screenshots, memes,
documents, receipts). immich-kiosk then points at the album UUIDs via
KIOSK_ALBUM, so both the screensaver and Photos tab inherit the curated feed.

No LLM. Signals were calibrated against the live 2.6.3 API on the Google-Takeout
import, which carries NO camera EXIF and NO facial-recognition people:
- People/<name> tags (Takeout face groups) are the primary keeper signal (~36%
  of the library); plus favorites/ratings/album-membership.
- GPS + mimetype order; Immich's own CLIP smart-search is a SOFT negative for
  screenshot/meme/document-shaped assets (top-N per query; no hard cutoff since
  smart-search exposes no score and ranks real photos highly for junk queries).
- Hard excludes limited to reliable signals: screenshot-y filename, tiny,
  extreme aspect. All weights/thresholds are env-tunable.

Pattern: python:3.12-slim + curate.py via configMapGenerator (per
apps/spotify-concerts) in a CronJob (per apps/flux-audit); pure stdlib, no pip.
Keys via optional secretKeyRef to immich-curation-secret (SOPS; created
out-of-band per README, like spotify-concerts). Ships DRY_RUN=true.

Verified: kubectl kustomize builds; curate.py compiles; a local DRY_RUN against
the live server curated 10,653/22,642 images (People-tag dominant, 110 hard-junk,
3,287 CLIP-flagged) with no writes.
Sets KIOSK_ALBUM to the curated album UUID so the tablets show 8000 family
photos (100% People-tagged, zero screenshots) instead of the whole library at
random. The album is owned by Katie (ktnynas) and shared read-only with the
kiosk user (kblack0610) so it's readable by UUID. Ken's UUID gets appended here
once that side is curated.
kblack0610 changed title from feat(immich): nightly wall-frame curation job (People-tag + CLIP, no LLM) to feat(immich): wall-frame curation job + point kiosk at curated album (no LLM) 2026-07-09 06:22:56 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kblack0610/home-config!107
No description provided.