Commit Graph
142 Commits
Author SHA1 Message Date
flanandClaude Sonnet 4.6 eea7baa19f refactor: rename project to winnow
- Rename Python package directory if_curator/ → winnow/
- Update all imports, entry points, and CLI references
- Update pyproject.toml: name, scripts, package list, repository URL
- Update Dockerfile, compose.yml, entrypoint.sh, scheduler.py
- Update GitHub Actions workflow image names
- Update README

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 01:07:46 +00:00
flanandClaude Sonnet 4.6 ca7b90f8ef docs: rewrite README from scratch
Describes the full pipeline (fetch → filter → embed → cluster → crop →
upload), explains why diversity matters and how the K-Medoids + FPS
selection works, covers both face and object modes in detail, and
documents all env vars in one place.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 01:02:28 +00:00
flanandClaude Sonnet 4.6 ed8487807b chore: establish authorship — Holden Salomon
- LICENSE: add copyright line for Holden Salomon (retain original)
- pyproject.toml: update authors to Holden Salomon <holden@arch.fyi>
- README: lead with what the software does rather than fork attribution;
  merge split env var tables into one unified reference; add lint/test
  badges; move attribution to footer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 01:00:17 +00:00
flanandClaude Sonnet 4.6 5634dd9e0c ci: add uv/docker/github-actions dependabot + run lint/test on dev
- dependabot.yml: add uv ecosystem (updates pyproject.toml + uv.lock
  together), github-actions ecosystem (keeps action versions current),
  and group all Python deps into one weekly PR
- lint.yml, test.yml: extend triggers to dev branch so ruff and pytest
  run on push/PR to dev, not just main

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 00:44:44 +00:00
flanandClaude Sonnet 4.6 eedbd3180a fix: resolve all linting errors, add tests, bump base image
Linting (ruff):
- cli.py: sort relative imports (I001)
- jobs.py: remove unused get_people import (F401), wrap long line (E501)
- executor.py: remove unused success variable (F841), wrap 4 long lines (E501)

Tests (24 passing):
- tests/test_config.py: config singleton defaults + env var overrides
- tests/test_upload_tracker.py: mark/filter/reset/summary logic
- tests/test_immich_api.py: filter_recent_assets date boundary cases
- tests/test_jobs.py: _resolve_strategy with LIMIT env var and fallbacks
- pyproject.toml: add [tool.pytest.ini_options] testpaths=["tests"] so
  pytest doesn't scan .venv in CI

Security:
- Dockerfile: bump CUDA base from 12.6.3 to 12.9.2 to pick up patched
  Ubuntu packages (fixes Dependabot low-severity alert)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 00:42:38 +00:00
flanandClaude Sonnet 4.6 c861d56a53 feat: expose all interactive CLI options via env vars
- config.py: wire BLUR_THRESHOLD, MIN_CONFIDENCE, MAX_AUTO_IMAGES,
  FACE_MARGIN, USE_FULL_RESOLUTION, ENABLE_FACE_ALIGNMENT to env vars
  (were hardcoded class defaults, inaccessible in AUTO_MODE)
- jobs.py: add LIMIT env var for custom image count in auto mode;
  overrides STRATEGY preset (mirrors interactive Custom Count option)
- compose.yml: document all env vars with inline comments grouped by
  concern — mode/strategy, people filtering, image quality, caching,
  tracker overrides, scheduling

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 00:37:16 +00:00
flanandClaude Sonnet 4.6 52c245a677 fix: correct output volume mount path to match OUTPUT_DIR default
Volume was mounted at /app/output but OUTPUT_DIR defaults to
./frigate_train (i.e. /app/frigate_train). Mount was never being used.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 00:32:39 +00:00
flanandClaude Sonnet 4.6 a14428e860 fix: skip Frigate API upload for object-mode jobs, preserve object output dir
- upload_to_frigate() now only processes face-mode jobs; object crops are
  saved to the output directory as the deliverable (Frigate has no API for
  object classifier training data — it must be copied manually)
- execute_jobs() only wipes the output dir for face mode; object crops
  accumulate across runs as intended

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 00:31:57 +00:00
flanandClaude Sonnet 4.6 0f3e67d5cc fix: clean output dir per run, fix cron inline comment, clarify volume placeholder
- executor.py: wipe person output dir before each run so stale crops
  from previous runs don't accumulate on disk
- compose.yml: move inline comment off CRON_SCHEDULE value (croniter
  would parse the comment text as part of the expression)
- compose.yml: add note clarifying <pool> placeholder in volume paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 00:27:52 +00:00
flanandClaude Sonnet 4.6 b5911c6338 refactor: split cli.py into cli, jobs, and executor modules
- cli.py: main() entry point and orchestration only
- jobs.py: configuration phase (auto/interactive, strategy, diversity selection)
- executor.py: execution phase (image download/crop, Frigate upload)

No logic changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 00:25:29 +00:00
flanandClaude Sonnet 4.6 2c77532d35 refactor: slim image with proper multi-stage build and --no-dev deps
- Dockerfile: separate runtime stage from build stage so g++,
  python3.12-dev, curl, gnupg are excluded from the final image
- uv sync --no-dev: drop ruff/pytest from production image
- Remove build.sh (replaced by CI) and root-level upload_tracker.py (stale duplicate)
- .gitignore: add *.log

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 00:21:01 +00:00
flanandClaude Sonnet 4.6 86db51e00f fix: catch JSONDecodeError in API calls, use get_headers() consistently
- get_people() and fetch_all_assets() only caught RequestException, leaving
  JSONDecodeError unhandled if Immich returns a non-JSON 200 response
- cli.py thumbnail fetch used a raw header dict instead of get_headers()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 00:09:11 +00:00
flanandClaude Sonnet 4.6 63cfe62340 fix: move uv sync before source COPY for faster incremental builds
uv sync was after COPY if_curator/, so any source change invalidated
the 800MB dependency cache and re-downloaded everything. Now deps are
installed first and stay cached across code-only changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 00:04:28 +00:00
flanandClaude Sonnet 4.6 d3f3f2b4e2 fix: upload only files created in current run, not entire output dir
upload_to_frigate() was scanning the output directory with os.listdir(),
picking up leftover files from previous runs and re-uploading them.
Use asset_map (populated by execute_jobs) to upload only current-run files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 00:03:29 +00:00
flanandClaude Sonnet 4.6 e2005fb8ef fix: apply EXIF orientation when loading images from Immich
PIL opens JPEGs without applying EXIF rotation. Immich computes face
bounding boxes on orientation-corrected images, so portrait photos
produced misaligned crops (showing chins/eyes/foreheads instead of
whole faces). ImageOps.exif_transpose() normalizes orientation before
any coordinate math.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 23:08:37 +00:00
flanandClaude Sonnet 4.6 d7d0431a36 fix: use INSIGHTFACE_HOME env var for model persistence
Hardcoded root="~/.insightface" ignored the INSIGHTFACE_HOME env var,
causing Buffalo_L to download into the container on every run instead
of persisting to the mounted volume.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 22:56:47 +00:00
flanandClaude Sonnet 4.6 d106d30060 feat: RGBA fix, per-person tracking, dry-run, retry-rejected, reset-person
- Fix RGBA→JPEG error: convert all images to RGB before saving
- Upload tracker: per-person breakdown in JSON, reset_person(), get_person_summary()
- mark_rejected() only fires on face-detection failures (not all HTTP 400s)
- New env vars: DRY_RUN, RETRY_REJECTED, RESET_PERSON
- Tracker summary printed at startup showing uploaded/rejected counts per person
- Document new env vars in compose.yml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 22:39:11 +00:00
flan 11ff3752d3 nonetype 2026-06-11 13:21:35 -04:00
flan 7b83dbd114 fix nonetype error 2026-06-11 12:51:59 -04:00
flan f5a8125b1e dev 2026-06-11 12:31:11 -04:00
flan 6cc2aff89c moved location 2026-06-11 11:58:36 -04:00
flan 30c70fb00b track uploaded files to prevent dupes. uses immich ID of original image 2026-06-11 11:34:13 -04:00
flan a04d1a5d9b fix: force onnxruntime-gpu over CPU + preload CUDA DLLs 2026-06-11 10:34:31 -04:00
flan dac2bc8242 fix: revert to CUDA 12.6 base for onnxruntime-gpu compatibility 2026-06-11 10:00:51 -04:00
flan b0ee06ee51 fix: revert to CUDA 12.6 base for onnxruntime-gpu compatibility 2026-06-11 09:59:53 -04:00
root 86c85103ae automated update: 2026-06-11 08:28:55 2026-06-11 08:29:15 -04:00
root 30d7d79873 automated update: 2026-06-11 08:22:00 2026-06-11 08:24:45 -04:00
flan 3556ec54b8 Update cli.py 2026-06-11 01:32:36 -04:00
flan e7ab034356 Update cli.py 2026-06-11 01:26:16 -04:00
root 90653f4d0d automated update: 2026-06-11 00:40:59 2026-06-11 00:41:40 -04:00
github-actions[bot] 72f672bd74 chore: update uv.lock 2026-06-11 04:35:40 +00:00
root 54960a0229 automated update: 2026-06-11 00:34:20 2026-06-11 00:34:20 -04:00
root 82c0190064 workflows 2026-06-11 00:15:42 -04:00
root 7210df27c8 automated update: 2026-06-10 23:50:06 2026-06-10 23:50:06 -04:00
root 4670ad2646 automated update: 2026-06-10 23:27:48 2026-06-10 23:27:48 -04:00
root 4909ca3c2e automated update: 2026-06-10 23:19:46 2026-06-10 23:19:46 -04:00
Holden 8e253dc991 fix: add README.md to COPY, move croniter to deps, remove readme field 2026-06-10 22:50:25 -04:00
root fb78f955b8 automated update: 2026-06-10 22:45:29 2026-06-10 22:45:29 -04:00
Holden b16488b771 fix: switch to cu126 index, move torch to [object] extra, update CUDA 12.6 base 2026-06-10 22:41:48 -04:00
root cdf10452fb automated update: 2026-06-10 22:31:20 2026-06-10 22:31:20 -04:00
root b9b734507b automated update: 2026-06-10 22:25:47 2026-06-10 22:25:47 -04:00
root 78ce09adaa automated update: 2026-06-10 22:07:46 2026-06-10 22:07:46 -04:00
root 82f0adea3f automated update: 2026-06-10 22:06:22 2026-06-10 22:06:22 -04:00
root 9307efef05 automated update: 2026-06-10 22:00:38 2026-06-10 22:00:38 -04:00
root a4129615df automated update: 2026-06-10 21:35:21 2026-06-10 21:35:21 -04:00
root 74351512a7 automated update: 2026-06-10 21:30:50 2026-06-10 21:30:50 -04:00
root 0dac947519 automated update: 2026-06-10 21:26:35 2026-06-10 21:26:35 -04:00
root f14000be04 automated update: 2026-06-10 21:24:16 2026-06-10 21:24:16 -04:00
root cd5f0f8849 automated update: 2026-06-10 21:20:20 2026-06-10 21:20:20 -04:00
root 31b0885e00 automated update: 2026-06-10 21:15:42 2026-06-10 21:15:42 -04:00