3.4 KiB
Overview
Photo Pipeline sorts a photo library. It finds duplicates before anything expensive
happens to them, asks a human which pictures may leave the machine, describes the
ones that may, proposes album names from what it found, renames folders under a
crash-safe journal, uploads through immich-go, and can archive a finished album off
active storage without ever forgetting it existed.
It runs locally. It talks to exactly three things outside itself: a vision provider,
an Immich server, and exiftool — and it will tell you before it uses any of them.
The workflow
Each stage is a gate, not a tab. A later stage can always be looked at; its actions stay disabled until what they depend on is true.
flowchart TD
I["0 · Inventory<br/>discover, hash, cluster duplicates"] --> S["1 · Safety<br/>score and human decision"]
S -->|sfw| A["2 · Analysis<br/>vision provider, EXIF checkpoint"]
S -->|nsfw| U
A --> B["3 · Albums<br/>proposal, then guarded rename"]
B --> U["4 · Upload<br/>immich-go, verified bytes"]
U --> R["5 · Archive<br/>copy, verify, then reclaim space"]
| Stage | What it decides | What it changes |
|---|---|---|
| Inventory | which file is the canonical copy of a picture | nothing — it only reads |
| Safety | whether a photo may be sent to a cloud provider | one sfw/nsfw EXIF keyword |
| Analysis | what a photo shows | a managed caption segment and additive keywords |
| Albums | what a folder should be called | folder names, through a journaled rename |
| Upload | which exact bytes reach Immich | nothing locally; assets appear in Immich |
| Archive | which album leaves active storage | files move to the archive, after verification |
What it will not do
These are enforced in code, not by convention, and each one is why some action you expected is sometimes refused.
- Nothing under
_IGNORE/is ever read. Not scanned, not counted, not thumbnailed, not sent anywhere. - A path is not an identity. Every picture has a stable id, so moving or renaming it loses no history.
- Only a confirmed-SFW photo reaches the vision provider. A photo marked NSFW is still uploadable to Immich; it simply never leaves for analysis.
- Metadata is verified, not hoped for. Every stage that writes EXIF reads it back and proves that what it did not own is unchanged.
- One writer at a time. A library lock, held by one process, is what makes a crash recoverable instead of ambiguous.
- Nothing irreversible happens without a preview and an explicit approval that names the exact count.
Where things live
| the photo library | wherever you point PHOTO_PIPELINE_LIBRARY_ROOTS; mounted read-write |
| the database, cache, logs, backups | the data directory, never inside the library |
| secrets | the environment, never the database and never a log line |
| these documents | docs/ in the repository, served at /docs by the application |
Running it
The short version, for a host installation:
python -m photo_pipeline migrate
python -m photo_pipeline serve # the API and this browser application
python -m photo_pipeline worker # the process that does the long work
The full procedure, the container path, and every setting are in the installation manual; if you would rather start using it, take the guided first pass.