Read the sidecars other editors write, and write them back on request
Benchmarks / CPU and I/O (per commit) (push) Successful in 10m59s
Benchmarks / Frame budget (on demand) (push) Skipped
Build and test / Desktop (Linux) (push) Successful in 1h33m36s
Build and test / Layer separation (push) Successful in 1m2s
Traceability / Requirement traces (push) Successful in 1m25s
🐳 Android image / Build and push (push) Successful in 9s
Build and test / android-image (push) Successful in 9s
Build and test / Android (aarch64) (push) Successful in 56m59s
Benchmarks / CPU and I/O (per commit) (push) Successful in 10m59s
Benchmarks / Frame budget (on demand) (push) Skipped
Build and test / Desktop (Linux) (push) Successful in 1h33m36s
Build and test / Layer separation (push) Successful in 1m2s
Traceability / Requirement traces (push) Successful in 1m25s
🐳 Android image / Build and push (push) Successful in 9s
Build and test / android-image (push) Successful in 9s
Build and test / Android (aarch64) (push) Successful in 56m59s
FR-CAT-13 asked for standard XMP and `core/dr-xmp` answered the file: it
has read and written `dc:subject`, `xmp:Rating`, `xmp:Label` and the IPTC
core since 5fa4c07, under an ownership rule that leaves everything else in
the document untouched. What nothing did was call it. No scan found an
`.xmp` beside a raw, no catalog row was filled from one, no judgement
wrote one back, and the "external modification detected, reload offered"
clause had no mechanism. A library imported from Lightroom came in and
could not go back out.
The scan collects `.xmp` beside `.drsc` from the listings it was already
paying for, and the pull reads each one whose ETag has moved. Both
namings resolve: darktable's `IMG_0001.CR3.xmp` names its file exactly,
Lightroom's `IMG_0001.xmp` names the stem, and under the stem the JPEG
beside a RAW is the same photograph and takes the same document, as
DarkRoom's own sidecar already does. Each is reconciled with the catalog
winning — keywords union, a rating or label taken only where the catalog
has none — because a standard XMP carries nothing that could say whether
its value is newer. A genuine disagreement is not resolved; it is written
to a table, and the settings page offers the sidecars' values against it.
That button is the reload the requirement asks to be offered, and the
ETag that moved is the detection it asks for: an `.xmp` edited elsewhere
is exactly a file the pull's ordinary incrementality re-reads.
Writing goes the other way behind a setting that starts off, since NFR-R4
makes writes beside somebody's originals theirs to switch on. With it on,
a judgement or a keyword rewrites the sidecar of whichever spelling
exists, or creates Lightroom's. The record is read from the catalog
whole at that moment rather than carried from the gesture, so a rating
and a keyword a second apart are two writes of one file that agree. And
the file's own title, caption, copyright and hierarchy come through the
rewrite: the catalog has no columns for them, `rewrite` replaces the
owned set wholesale, and a record that said nothing about them would have
deleted them from a Lightroom sidecar on every star.
The rating's two axes cross the format's one field both ways: a
rejection is Adobe's `-1` and stars are stars, and stars arriving on a
rejected frame lift the rejection, since the file said it was worth a
number. An unrated file says nothing and clears nothing, on the rule the
`.drsc` merge keeps. `versions.label` finally has a reader and a writer,
with the code table moved out of the query so the two cannot drift.
This commit is contained in:
+16
-14
@@ -308,7 +308,7 @@ well optimised — ETag pruning under FR-NC-4 turns an unchanged 50k library int
|
||||
gap is narrower than it reads. It is the *first* build against a large remote library that pays, and
|
||||
that is the moment a new user meets.
|
||||
|
||||
**FR-CAT-13 — XMP interoperability, built but not yet wired.** `core/dr-xmp` now reads and writes
|
||||
**FR-CAT-13 — XMP interoperability, wired on 2026-09-12.** `core/dr-xmp` reads and writes
|
||||
standard XMP sidecars: `dc:subject` and `lr:hierarchicalSubject`, `xmp:Rating` and `xmp:Label`, and
|
||||
the IPTC core fields, in both the attribute and the element form and whatever RDF container a file
|
||||
happened to use. It states the ownership rule in one place — DarkRoom owns the properties in
|
||||
@@ -316,20 +316,22 @@ happened to use. It states the ownership rule in one place — DarkRoom owns the
|
||||
and enforces it by rewriting a packet event by event rather than serialising over it, so another
|
||||
application's `crs:` settings, comments and processing instructions survive a write byte for byte.
|
||||
|
||||
**What remains is the wiring, and it is the larger half.** Nothing above the crate calls it: no scan
|
||||
finds a `.xmp` beside a raw, no catalog row is populated from one, no edit writes one back, and the
|
||||
external-modification detection the requirement also asks for does not exist. Two smaller gaps go
|
||||
with it — GPS is not carried (`exif:GPSLatitude` is a format of its own, and `dr-decode` produces no
|
||||
location for it to carry yet, which `dr-export`'s metadata module says about its own half), and the
|
||||
filename convention is left to the caller, because Lightroom writes `IMG_0001.xmp` and darktable
|
||||
writes `IMG_0001.CR3.xmp` and finding a file is not this crate's business.
|
||||
**The wiring is `ui/dr-ui/src/xmp_sync.rs`.** The scan collects `.xmp` beside `.drsc` from the
|
||||
listings it was already making; the pull reads each one whose ETag has moved and reconciles it
|
||||
against the catalog with the catalog winning — keywords union, and a rating, label or caption taken
|
||||
only where the catalog holds none. Both naming conventions resolve: `IMG_0001.CR3.xmp` names its
|
||||
file, `IMG_0001.xmp` the stem, and the JPEG beside a RAW is the same photograph. A genuine
|
||||
disagreement is written to `xmp_conflicts` and the settings page offers "Take the sidecars' values",
|
||||
which is the reload the requirement asks for; the detection it asks for is the ETag that moved. The
|
||||
write in the other direction is behind a setting that starts off (NFR-R4): a judgement or a keyword
|
||||
then also rewrites the sidecar beside the original, keeping the file's own caption, copyright and
|
||||
hierarchy, which the catalog has no columns for and would otherwise have deleted.
|
||||
|
||||
The precedence question is settled conservatively rather than fully: keywords union, following
|
||||
`dr_catalog::merge`, and every other field is taken only where DarkRoom holds none, following
|
||||
`Version::merge`'s judgement rule — because a standard XMP carries no revision and no device, so
|
||||
FR-NC-9's ordering cannot be performed against it. What is *not* settled, and is written down in the
|
||||
crate rather than guessed at, is when a reload may happen without asking; see the module
|
||||
documentation's "The open question".
|
||||
**What remains.** GPS is not carried — `exif:GPSLatitude` is a format of its own and `dr-decode`
|
||||
produces no location for it to carry yet. Title, description and copyright are read and reconciled
|
||||
but the catalog has nowhere to put them, so they pass through a rewrite rather than being editable.
|
||||
An XMP write made offline is not queued: the catalog and the `.drsc` are authoritative, and the next
|
||||
judgement online writes the file whole again.
|
||||
|
||||
`dr-preset-xmp` remains what it always was and is still not the counter-example it looks like: a
|
||||
reader of Lightroom *presets* under FR-DEV-6, a different file for a different purpose.
|
||||
|
||||
Reference in New Issue
Block a user