Skip to content

docs: Lightroom round-trip guide and the photos endpoint it reads - #12

Draft
Luca-Timo wants to merge 2 commits into
PicPeak:mainfrom
Luca-Timo:docs/lightroom-roundtrip
Draft

docs: Lightroom round-trip guide and the photos endpoint it reads#12
Luca-Timo wants to merge 2 commits into
PicPeak:mainfrom
Luca-Timo:docs/lightroom-roundtrip

Conversation

@Luca-Timo

@Luca-Timo Luca-Timo commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Documents the Lightroom round-trip. Companion to PicPeak/picpeak#1165 (backend) and PicPeak/plugin-lightroom#3 (plugin).

New guide — /guides/lightroom-roundtrip

Covers the whole loop: upload unedited camera JPGs → client proofs → pull the picks onto the matching RAWs in Lightroom Classic → publish the edits back over the proofs.

Written around the parts that are easy to get wrong, not just the happy path:

  • Colour labels are off by default on new events. This is the first question a stuck user will ask, so it's answered before they ask it.
  • The conflict/merge modes, and why green ranks first (in proofing, green means first choice — the pick that has to survive a disagreement).
  • The multi-camera naming schemecam11234.jpg / cam21234.jpg, the longest-digit-run rule that makes it work, and the truncated delivery name (Smith_Wedding_1234.jpg) that silently breaks it again.
  • The plugin-free path — XMP sidecars, CSV/JSON, plain text lists, plus the Lightroom limitation that it only reads a standalone .xmp for RAW/DNG masters and needs Metadata → Read Metadata from File triggered by hand.
  • A troubleshooting section keyed to the actual failure messages.

Added to the guides nav after "Managing Photos".

Photos API reference

Two changes:

replaces_photo_id on upload — replace a photo's file while keeping its identity: the ID, the client's ratings and colour labels, its comments and its gallery position all survive, and the share link stays valid.

"List photos for an event" rewritten. ⚠️ Worth a look: the previous version documented offset-based pagination, limit max 200, and category_id / is_hidden / sort parameters — for an endpoint that did not exist in the v1 API at all. It exists as of #1165, with a different contract (page-based pagination, limit max 100, mark filters), so the section now describes what the code actually does, including which field to match on (source_filename, not original_filename) and what the merged color_label / rating fields mean.

The six phantom endpoints — now fixed

The page documented chunked upload, get single photo, hide/unhide, delete, bulk delete and repair dimensions under /v1/… URLs. None of those exist in the public API. All six are real, but they live under /api/admin/photos and take an admin session JWTadminAuth is JWT-only, so an API token sent there is rejected. Anyone following the old page got a 401 with no indication why.

The page is now split into Public API endpoints and Admin API endpoints, with a table up top saying which auth each takes, and stating plainly that there is no API-token equivalent for delete, hide, chunked upload or dimension repair.

Corrections beyond the URLs, each verified against the route handler:

Was documented Actually
Hide/unhide { "is_hidden": true } { "visibility": "hidden" } — a string, visible or hidden
Bulk delete { "photo_ids": [...] } { "photoIds": [...] }, camelCase; ids outside the event are ignored
Repair dimensions per-event, returns {scanned, repaired, failed} instance-wide; returns {message, count} immediately, runs in background, 409 if already running, and has a /status endpoint the page never mentioned
Get single photo /v1/photos/:id /:eventId/photo/:photoId — singular photo, where the list route is plural
Chunked upload one endpoint, {filename, size, mime_type} four endpoints; init takes {filename, fileSize, mimeType}

Every route now names its required permission (photos.upload, photos.edit, photos.delete, photos.view).

Two more that fell out of the same check

The public upload section described a plural photos=@ field accepting several files per request. The v1 route is multer.single('photo'): one image per request, images only, 100 MB cap. The documented response was also wrong. Both fixed, including the different shape a replace returns.

The video section claimed videos use "the same upload endpoint" — which contradicts an image-only MIME filter. Video is admin-side; corrected.

The scope note claimed photos.read / photos.write. The real scopes are read / write / admin, hierarchical, with RBAC permissions layered on top.

Verification

npm run build passes; /guides/lightroom-roundtrip and /api/photos both prerender. Every endpoint contract above was read off the route handler on picpeak/main rather than inferred. No screenshots — text-only docs change.

New guide at /guides/lightroom-roundtrip covering the whole loop: upload
unedited camera JPGs, let the client proof, pull the picks onto the
matching RAWs in Lightroom Classic, publish the edits back over the
proofs.

Documents the parts that are easy to get wrong rather than just the
happy path:

- colour labels are OFF by default on new events, so the first question
  a stuck user asks is answered before they ask it
- the conflict/merge modes, and why green ranks first
- the multi-camera naming scheme (cam11234.jpg / cam21234.jpg), the
  longest-digit-run rule that makes it work, and the truncated delivery
  name that silently breaks it again
- the plugin-free path: XMP sidecars, and the Lightroom limitation that
  it only reads a standalone .xmp for RAW/DNG masters

Also rewrites "List photos for an event" in the Photos API reference.
The previous version documented offset-based pagination, a limit of 200,
and category/is_hidden/sort parameters for an endpoint that did not
exist in v1 at all. It exists now, with page-based pagination, a limit
of 100, and mark filters — so the section describes the real contract,
including which field to match on and why the merged color_label/rating
fields are there.

Adds replaces_photo_id to the upload section.
The page documented chunked upload, get-single-photo, hide/unhide,
delete, bulk delete and repair-dimensions under /v1/... URLs. None of
those exist in the public API — the v1 router has seven routes and none
of them is on that list. All six are real, but they live under
/api/admin/photos and take an admin session JWT, which apiTokenAuth
tokens are not. Anyone following the old page got a 401 and no
indication why.

The page is now split into "Public API endpoints" and "Admin API
endpoints", with a table at the top saying which auth each takes and
stating plainly that there is no API-token equivalent for delete, hide,
chunked upload or dimension repair.

Corrections beyond the URLs, all verified against the route handlers:

- hide/unhide takes visibility: "visible" | "hidden" as a STRING, not
  is_hidden as a boolean
- bulk delete's body key is photoIds, camelCase, and ids outside the
  event in the URL are ignored
- repair-dimensions is instance-wide, not per event; it returns
  immediately with { message, count }, runs in the background, 409s if
  already running, and has a status endpoint the page never mentioned
- get-single-photo is /:eventId/photo/:photoId — singular "photo",
  where the list route is plural
- chunked upload is four routes, not one, and its init body is
  filename/fileSize/mimeType
- each route's required permission is now named

Also fixes the public upload section, which described a plural
"photos=@" field accepting several files per request. The v1 route is
multer .single('photo'): one image per request, images only, 100 MB cap.
Documents the real 201 response and the different shape a replace
returns. The video section said videos use "the same upload endpoint",
which contradicts an image-only filter — video is admin-side.

The scope note at the top claimed photos.read / photos.write. The real
scopes are read / write / admin and they are hierarchical, with RBAC
permissions layered on top.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants