63 lines
3.5 KiB
Markdown
63 lines
3.5 KiB
Markdown
# webapp — Photo Analyzer web UI
|
|
|
|
A local, browser-based UI for the `photo_analyzer.py` pipeline. Browse and
|
|
full-text-search the analyzed library, watch analysis runs live, and start/stop
|
|
runs — reusing the `nsfwtag` review-app design (dark OLED, stdlib HTTP server, one
|
|
token-injected HTML page). Nothing leaves the machine.
|
|
|
|
```bash
|
|
python -m webapp # DB + library default from photo_analyzer.env
|
|
python -m webapp --db photo_analysis.db --library pictures/
|
|
python -m webapp --no-open # don't auto-open a browser
|
|
```
|
|
|
|
See `../WEBAPP_CONCEPT.md` for the full UX concept and rationale.
|
|
|
|
## Views
|
|
|
|
- **Library** — FTS5 search over description/tags/mood/location + facet filters
|
|
(setting, time of day, season, people, year range, has-place, status), a folder
|
|
sidebar (album = leaf folder), an infinite-scroll grid, and a lightbox with an
|
|
**Analysis** panel (all DB fields) and a raw **EXIF** panel (incl. Google Maps
|
|
link for geotagged photos).
|
|
- **Analyze** — run controls mapping every CLI flag (dry-run / reanalyze / no-exif /
|
|
exif-only / group-variants), a live overall progress bar, per-album progress, and a
|
|
recent feed. Start/Stop drive `photo_analyzer.py` as a subprocess; Stop is the
|
|
analyzer's own SIGINT drain. Balance / quota-check buttons. A **Duplicates &
|
|
maintenance** row runs the content-hash actions (backfill hashes / find duplicates /
|
|
mark duplicates) — no API calls; output streams to the Activity log and marked
|
|
duplicates appear under the `duplicate` status filter. (Move/rename reconciliation
|
|
and perceptual hashing also run automatically at the start of every normal run.)
|
|
- **Stats** — KPI cards + CSS breakdown bars (setting/time/season/people, by-year
|
|
histogram, top tags) + an error list. No chart library.
|
|
|
|
## Architecture
|
|
|
|
- `server.py` — stdlib `ThreadingHTTPServer` on `127.0.0.1:<free-port>`; routes below.
|
|
- `query.py` — read-only DB access (FTS5 search, facets, stats, one-photo).
|
|
- `runner.py` — drives `photo_analyzer.py` as a subprocess; DB-derived progress.
|
|
- `page.py` / `analyzer.html` — the single frontend page (inherits nsfwtag's tokens).
|
|
- SQLite stays the source of truth; the server only reads it (+ a `busy_timeout` so a
|
|
concurrent analysis write never errors a read). `/img` and `/exif` paths are
|
|
validated against the DB — the server can't be pointed at arbitrary files.
|
|
|
|
| Route | Purpose |
|
|
|-------|---------|
|
|
| GET `/` | the page |
|
|
| GET `/search?q=&setting=&tod=&season=&people=&year_min=&year_max=&has_location=&status=&album=&sort=&offset=` | paged results (FTS5 when `q`) |
|
|
| GET `/facets` · `/stats` | filter options · aggregates |
|
|
| GET `/photo?path=` · `/exif?path=` · `/img?path=` | one record · raw EXIF · thumbnail |
|
|
| GET `/progress` · `/log` | live run state · activity log |
|
|
| POST `/run` · `/stop` | start / stop a run — body `{library, ...flags}` or `{library, action}` where action ∈ backfill-phash \| list-dupes \| dedupe |
|
|
| GET `/balance` · `/quota-check` | provider balance · one-shot quota probe |
|
|
|
|
## Known ceilings (ponytail)
|
|
|
|
- `/img` serves **full-resolution originals**, not resized thumbnails — a 120-card
|
|
page can move a lot of bytes. Fine locally; add a Pillow thumbnail+cache endpoint if
|
|
the grid feels slow. (Same limitation as nsfwtag.)
|
|
- HEIC files won't render in the browser (broken thumb); metadata/search still work.
|
|
- The Analyze **tokens/ETA** readout is omitted — those live only in the terminal
|
|
dashboard's memory; the subprocess model surfaces DB-derived progress instead. Parse
|
|
run stdout for token counts if you want them back.
|