Say where an uploaded original lands

FR-NC-7 specifies how bytes travel and deliberately has no opinion about
where they go, and nothing else said either — so the one thing a photograph
needs on arrival, a folder, was unwritten.

FR-NC-7a gives it `{yyyy}/{yyyy}-{mm}-{dd}`, and two consequences the
transport mechanics do not supply on their own: expansion is a pure function
of capture metadata, so two devices computing a destination for the same
frame agree; and the template governs placement on upload only, because the
remote library is reachable by other clients and restructuring it under them
is not ours to do.

FR-NC-7b joins import to upload. A move-import must not erase the card until
the upload is confirmed — a card erased against an in-flight transfer is the
one failure in this application with no undo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-22 14:00:13 +02:00
co-authored by Claude Opus 5
parent 610f679881
commit 5945f7420a
+29
View File
@@ -634,6 +634,35 @@ resume or `DELETE` stranded uploads on startup.
Small files (sidecars) use **bulk upload** via `POST /remote.php/dav/bulk` with a Small files (sidecars) use **bulk upload** via `POST /remote.php/dav/bulk` with a
`multipart/related` body, allowing hundreds of edit-graph sidecars in a single request. `multipart/related` body, allowing hundreds of edit-graph sidecars in a single request.
**FR-NC-7a — Remote layout.** FR-NC-7 says how bytes travel; this says where they land. An
uploaded original shall be placed under the account's library root in a directory expanded from a
date template, defaulting to `{yyyy}/{yyyy}-{mm}-{dd}` — one directory per year, one per capture
day beneath it. The template is configurable per account and uses the same token vocabulary as
export naming (FR-EXP-6), so `{date}` means the image's **capture** date and never today's; an
image with no readable capture time falls back to file mtime, and the fallback is visible in the
import report rather than silent.
Two consequences the mechanics do not give for free:
- **The path is derived, not remembered.** The same image uploaded twice from two devices must
compute the same destination, so expansion is a pure function of capture metadata and the
template — never of local library layout, which differs per device.
- **Existing trees are not restructured.** An image already present remotely stays where it is.
The template governs placement on upload only; DarkRoom shall not move server-side files to
conform, because the remote library is also reachable by other clients (§1.3).
Sidecars follow their image, not the template: they are named from `oc:fileid` (FR-NC-8) and live
beside the file they describe.
**FR-NC-7b — Ingest to remote.** Import (FR-CAT-10) and upload (FR-NC-7) shall compose: a card
import may target a Nextcloud library, in which case files are copied locally first, verified by
checksum, and only then queued for upload. The local copy is not deleted on a move-import until
the upload is confirmed — a card erased against an in-flight upload is unrecoverable, which is the
one failure in this app with no undo.
Duplicate detection (FR-CAT-11) runs against the catalog before upload, so re-inserting a card
that was already imported transfers nothing.
**FR-NC-8 — Edit metadata sync.** Edit graphs sync bidirectionally as sidecars, one per image, **FR-NC-8 — Edit metadata sync.** Edit graphs sync bidirectionally as sidecars, one per image,
named deterministically from `oc:fileid`. Each sidecar carries a monotonic revision counter, a named deterministically from `oc:fileid`. Each sidecar carries a monotonic revision counter, a
per-device UUID, and a last-edit timestamp. per-device UUID, and a last-edit timestamp.