Usage¶
photo-tagger is a single command-line tool. You point it at photos, it sends each one to a local vision-language model, and it writes Lightroom-compatible keywords, a title, and a description back to your images.
Basic invocation¶
Every run is one command with one or more -i/--input paths plus options:
-i/--input is repeatable and accepts both files and directories. Add -r/--recursive to
descend into subdirectories, and --ext to control which extensions are picked up when scanning
directories (default cr3,jpg, case-insensitive). The tool reads RAW and standard formats (CR3,
CR2, NEF, DNG, JPG, PNG, and more).
Before your first run, make sure ExifTool is on your PATH and a model server is running. See
Installation for the full prerequisites, and
Configuration for env vars and the TOML config file.
What a run produces¶
By default photo-tagger leaves your originals untouched: for each image it writes an XMP sidecar
(for example IMG_1234.xmp) next to the file. Pass --embed-in-photo to write the metadata into
the image file itself instead.
Either way the metadata is written through ExifTool. The fields generated are:
| Field | Written when | Notes |
|---|---|---|
| Keywords | always | Flat keywords plus a Lightroom hierarchy. |
| Title | unless --no-write-title |
Short title for the photo. |
| Description | unless --no-write-description |
One-line caption. |
Note
Existing metadata is merged, not clobbered. New keywords are combined with the ones already on the
file, preserving Lightroom hierarchies and deduplicating case-insensitively. Use
--overwrite-keywords if you would rather replace existing keywords instead of merging.
Logs, progress, and NDJSON¶
photo-tagger keeps machine output and human output on separate streams so a run stays scriptable:
- Logs and the live progress bar go to stderr. The progress bar is shown on a TTY and is
auto-disabled when stderr is redirected; disable it explicitly with
--no-progress. - With
--json, one NDJSON line per processed photo goes to stdout (file, status, from_cache, retry, title, description, keywords, token counts, seconds). Because logs stay on stderr, stdout pipes cleanly into tools likejq.
End-to-end example¶
Tag every CR3 and JPG under a folder, recursing into subfolders, against an Ollama server, and write XMP sidecars (the default):
Each photo gets an .xmp sidecar with the generated keywords, title, and description, ready to
import into Lightroom. Logs and a progress bar appear on stderr; nothing is written to stdout
because --json was not passed.
Tip
Add --dry-run to run the model and log the proposed metadata without writing anything. It is the
safest way to preview results before committing to a real run.
Prefer a window?
photo-tagger ships an optional desktop GUI (photo-tagger gui) for a review-before-write
workflow: drag in photos, generate proposals, then review and edit each title, description, and
keyword set before saving. Install it with the gui extra.
Next steps¶
- CLI reference: every flag, its default, and the matching environment variable.
- Desktop GUI: the optional point-and-click frontend.
- Recipes: ready-made command lines for common workflows.