Add full-text photo library, bulk select/download, duplicate detection, video support, and zero-config folder detection

- Search & Edit becomes a multi-view library: Tagged/Untagged/Failed/Videos/Duplicates
- Bulk select (click, shift-click range, select-all-matching) with bulk recategorize,
  CSV/JSON export, and original-quality ZIP download (verified byte-identical)
- Video browsing/tagging via ffmpeg frame extraction, native playback with range support
- Duplicate detection via a lightweight perceptual-hash index (no new dependencies)
- Full-resolution photo/video viewer with editable metadata, prev/next navigation
- Date range, file-type, and category quick-filters; saved searches (localStorage)
- Appearance settings: accent color picker, grid density, results-per-page; full dark mode
- Auto-detects its photo folder (CLI arg > env var > last used > parent directory),
  so the app can be dropped into any photo collection and just work
This commit is contained in:
drjones
2026-07-18 19:43:26 -07:00
parent adbe4d8f24
commit 5208c5ecef
4 changed files with 1731 additions and 208 deletions

View File

@@ -5,16 +5,35 @@ describe + categorize each photo, and embeds the result as standard metadata
inside the photo file so everything becomes searchable (Spotlight, Photos,
Lightroom, etc.). Nothing is ever deleted, moved, or renamed.
## Run it
## Run it — zero config
Drop this whole folder *inside* the photo collection you want to organize,
then just run it:
```bash
cd "/Users/drjones/photo ollama app organizer"
cd "your-photo-library/photon"
python3 server.py
# then open http://localhost:8765
```
Requires: Ollama running with a vision model, `exiftool` (installed via brew),
macOS (`sips` is used for fast downscaling).
It auto-detects the folder to scan as **the parent of wherever this app
lives** — so if you dropped it into `~/Pictures/Vacation2026/photon`, it
defaults to scanning `~/Pictures/Vacation2026`. The folder it last scanned is
also remembered automatically (`photon_folder.json`), so on every future
launch it just picks up where you left off — no retyping paths.
Want to point it somewhere else? Any of these work, in priority order:
```bash
python3 server.py /path/to/photos # one-off CLI override
PHOTON_FOLDER=/path/to/photos python3 server.py # env var override
```
Or just type a new path into the folder field in the Console tab and hit
**Scan Directory** — that becomes the new remembered default too. Running
two libraries at once? `PHOTON_PORT=8766 python3 server.py` avoids a port clash.
Requires: Ollama running with a vision model, `exiftool` (installed via
brew), macOS (`sips` for fast downscaling), and `ffmpeg`/`ffprobe` (for video
thumbnails and tagging — optional, everything else works without it).
## How it works
@@ -63,5 +82,35 @@ Tested defaults: scout `glm-ocr` (6/6 routing accuracy) → describer
## Searching afterwards
Spotlight: just type a word from a description in Finder search.
Or from terminal: `mdfind -onlyin "/Volumes/sanD/allphotos from phone" "scooter"`
Or from terminal: `mdfind -onlyin "/path/to/your/photos" "scooter"`
Or grep the journal: `grep -i scooter photon_journal.jsonl`
Or use the app itself — the **Search & Edit** tab is a full photo library browser:
- **Tagged** — instant multi-word search across description/category/filename/path,
filter by category (click a chip), date range, or file type (photos/videos),
sort by newest/name/category. Click any result for a full-resolution
viewer + editor with Save / AI Redo / Reveal-in-Finder.
- **Untagged** / **Failed** — see what's left to do or what errored, tag or
retry one at a time without leaving the grid.
- **Videos** — thumbnails via `ffmpeg` frame-grab, native playback, AI tagging
off an extracted frame.
- **Duplicates** — one-time background scan builds a perceptual-hash index and
groups exact visual matches (re-saves, burst duplicates). No auto-delete —
just Reveal-in-Finder per copy so you decide.
**Selecting & downloading photos in bulk:** click **Select** to enter select
mode, then:
- click a photo to toggle it, **shift-click** to select a range
- **Select all loaded** / **Select all N matching** (grabs everything matching
your current search, not just what's rendered) / **Clear selection**
- **Download selected (ZIP)** or **Download all matches (ZIP)** — streams a
real zip of the original files, byte-for-byte, no re-compression (capped at
3000 files per zip)
- **Apply category to selected**, or **Export** the set as CSV/JSON
## Appearance
Settings & Safety → **Appearance**: pick an accent color (native color
picker), grid density (compact/comfortable/large), and results-per-page.
Saved per-browser, doesn't touch anything on disk.