mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-28 20:17:18 -06:00
Issue #92: XMP: merge into an existing sidecar instead of replacing it
Saving (Cmd+S or auto-write) now merges LightCraft's properties into an existing sidecar (lightcraft_meta::merge_xmp): owned properties (rating, label, dc/IPTC/rights fields, lc:*) are replaced or removed, capture time and GPS only when LightCraft has a value; everything else (crs:, xmpMM:History, unknown namespaces, packet wrapper) is kept byte for byte. The result is re-parsed and checked before it is written (temp + fsync + rename). A sidecar that isn't valid XMP is copied to .xmp.bak-<time> (never overwriting) before it is replaced; one that can't be read at all is left alone and the save fails. Shared stems: when several catalogued files map to one stem sidecar (IMG.CR3 + IMG.JPG), it belongs to the raw (else the first by name) and the others write and read <file>.xmp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e99aa61fc4
commit
882a059fac
+1
-1
@@ -210,7 +210,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins.
|
||||
| LR-META-COPYRIGHT-DEFAULT | Default copyright on import | P1 | ✅ | `cmd:library.preferences` (`import.copyright`, `import.creator`), `crates/ui-egui/src/panels/settings.rs` | Settings → Import → Metadata; fills only photos without their own |
|
||||
| LR-META-LOCATION | Location editing | P2 | 🟡 | `cmd:photo.setMeta` (`location`, `city`, `state`, `country`, `gps`) | place fields and GPS (decimal or degrees / minutes / seconds) edited in Info, read/written as IPTC Core / Photoshop / EXIF XMP; Show on Map opens OpenStreetMap; no embedded map, no geocoding |
|
||||
| LR-META-COPYPASTE | Copy / paste metadata | P2 | ✅ | `cmd:photo.copyMetadata`, `cmd:photo.pasteMetadata` | title, caption, alt text, extended description, copyright (notice, status, usage terms, info URL), creator, place fields, keywords; `fields` picks a subset |
|
||||
| LR-META-XMP | XMP read/write | P0 | ✅ | `cmd:photo.saveMetadataToFile`, `cmd:photo.readMetadataFromFile`, `cmd:library.xmpPreferences`, `crates/engine/src/sidecar.rs`, `docs/xmp-interop.md` | sidecar wins for metadata; a sidecar capture time (`exif:DateTimeOriginal` / `photoshop:DateCreated` / `xmp:CreateDate`) fills in only when the file has none |
|
||||
| LR-META-XMP | XMP read/write | P0 | ✅ | `cmd:photo.saveMetadataToFile`, `cmd:photo.readMetadataFromFile`, `cmd:library.xmpPreferences`, `crates/engine/src/sidecar.rs`, `docs/xmp-interop.md` | sidecar wins for metadata; a sidecar capture time (`exif:DateTimeOriginal` / `photoshop:DateCreated` / `xmp:CreateDate`) fills in only when the file has none; saving merges into an existing sidecar (`crates/meta/src/xmp_merge.rs`): another app's `crs:`, `xmpMM:History` and unknown namespaces are kept byte for byte, an unreadable sidecar is backed up first; with stem naming, files sharing a stem (raw + JPEG) get separate sidecars (`<file>.xmp` for all but the raw) |
|
||||
| LR-META-EXIF-FULL | Full EXIF/IPTC [Classic] | P1 | ✅ | `cmd:photo.allMetadata`, `cmd:dialog.allMetadata`, `crates/meta/src/tags.rs`, `crates/meta/src/exif.rs`, `crates/meta/src/iptc.rs` | read and written on export; Info ▸ All Metadata…: every TIFF / EXIF / GPS / interop tag (named, common values spelled out) and the XMP fields, searchable; metadata presets |
|
||||
|
||||
## F. Edit panel — global adjustments (EDIT)
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
- 23 new built-in presets: Portrait, Landscape, Urban, Food, Seasons, Vintage and B&W toners.
|
||||
|
||||
### Reliability
|
||||
- Saving metadata to an XMP sidecar another application wrote no longer replaces it (issue #92): LightCraft merges its
|
||||
fields in and keeps the rest — e.g. that application's develop settings and edit history — byte for byte. A
|
||||
sidecar that isn't valid XMP is copied to `<name>.xmp.bak-<time>` first. With the default stem naming, a raw and a
|
||||
JPEG with the same name (`IMG_0001.CR3` + `IMG_0001.JPG`) no longer share one sidecar: the raw keeps `IMG_0001.xmp`,
|
||||
the JPEG uses `IMG_0001.JPG.xmp`.
|
||||
- Canon CR2 photos from the EOS 7D, 50D, 60D, 550D, 600D, 1200D, 1300D, 5D Mark II and 1D Mark IV (and other
|
||||
models whose sensor starts on a green-blue row) no longer come out magenta (issue #85): the colour-filter
|
||||
layout is read from each file instead of assumed.
|
||||
|
||||
+35
-2
@@ -8,8 +8,8 @@ other tools), and let LightCraft pick up edits made elsewhere.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Name | `<stem>.xmp` by default (`IMG_0001.CR3` → `IMG_0001.xmp`); `<file>.xmp` (`IMG_0001.CR3.xmp`) with `naming: "full"`. Reading accepts either (preferred first) and `.XMP`. |
|
||||
| Write | `photo.saveMetadataToFile {ids?}` (Photo ▸ Save Metadata to File, ⌘S), or automatically after every change with `library.xmpPreferences {autoWrite: true}` (File ▸ Automatically Write Changes into XMP). Slider drags are written once, when the drag ends; undo/redo rewrite the sidecar. Written atomically (temp file + rename). |
|
||||
| Name | `<stem>.xmp` by default (`IMG_0001.CR3` → `IMG_0001.xmp`); `<file>.xmp` (`IMG_0001.CR3.xmp`) with `naming: "full"`. Reading accepts either (preferred first) and `.XMP`. With stem naming, files sharing a stem don't share a sidecar: see *Shared names* below. |
|
||||
| Write | `photo.saveMetadataToFile {ids?}` (Photo ▸ Save Metadata to File, ⌘S), or automatically after every change with `library.xmpPreferences {autoWrite: true}` (File ▸ Automatically Write Changes into XMP). Slider drags are written once, when the drag ends; undo/redo rewrite the sidecar. An existing sidecar is **merged into, never replaced** (see *Saving into an existing sidecar*). Written atomically (temp file, fsync, rename). The result lists `written`, `merged` and `backups`. |
|
||||
| Read | On import (`library.import`, the report counts `sidecars`), and `photo.readMetadataFromFile {ids?}` (one undo step). For raw/DNG files without a sidecar, the XMP embedded in the file is used. |
|
||||
| Preferences | `library.xmpPreferences {autoWrite?, naming?: stem\|full}`, stored in the library's `prefs.json`. |
|
||||
|
||||
@@ -37,6 +37,39 @@ used — on import (it then also files a copied photo in its date folder) and by
|
||||
`xmp:Rating="-1"` (the XMP convention for rejected) sets the reject flag. Develop settings come from
|
||||
`lc:settings` when present (exact); otherwise from the `crs:` fields below (approximate).
|
||||
|
||||
## Saving into an existing sidecar
|
||||
|
||||
A sidecar may already hold another application's data — e.g. its `crs:` develop settings and `xmpMM:History` — often
|
||||
the only copy of those edits outside that application's catalog. Saving never drops it:
|
||||
|
||||
- LightCraft **owns** the properties in the table above: `xmp:Rating`, `xmp:Label`, `dc:title`, `dc:description`,
|
||||
`dc:rights`, `dc:creator`, `dc:subject`, `Iptc4xmpCore:Location`, `Iptc4xmpCore:AltTextAccessibility`,
|
||||
`Iptc4xmpCore:ExtDescrAccessibility`, `photoshop:City`/`State`/`Country`, `xmpRights:Marked`/`UsageTerms`/
|
||||
`WebStatement` and everything in `lc:`. They are replaced on every save, and removed when LightCraft has no value
|
||||
(clearing a title in LightCraft clears it in the file). All of them are read back on import, so the library starts
|
||||
from what the sidecar said.
|
||||
- The **capture time** (`exif:DateTimeOriginal`, `photoshop:DateCreated`) and **GPS** (`exif:GPSLatitude`,
|
||||
`exif:GPSLongitude`) are replaced only when LightCraft has a value; otherwise the file's stay.
|
||||
- **Everything else is kept byte for byte**: other namespaces (`crs:`, `xmpMM:`, `lr:hierarchicalSubject`, unknown
|
||||
ones), `xmp:CreatorTool`, comments, the packet wrapper and padding. LightCraft's properties go into one
|
||||
`rdf:Description` of their own; owned properties written by another application (in attribute or element form) are
|
||||
removed from its description, which otherwise stays as it was.
|
||||
- A sidecar that can't be read as XMP (not well-formed, not UTF-8, no `rdf:RDF`) is first copied to
|
||||
`<name>.xmp.bak-<date><time>` (`-1`, `-2`… if taken — a backup is never overwritten), then replaced. A sidecar that
|
||||
can't be read at all (permissions) is left alone and the save fails.
|
||||
|
||||
Limits: LightCraft writes keywords to `dc:subject` only, so another application's `lr:hierarchicalSubject` is kept as
|
||||
it was and may still list keywords removed in LightCraft. LightCraft doesn't write `crs:`: the other application's
|
||||
develop settings stay as that application left them, next to LightCraft's own (`lc:settings`, which LightCraft reads
|
||||
first).
|
||||
|
||||
## Shared names
|
||||
|
||||
With stem naming, `IMG_0001.CR3` and `IMG_0001.JPG` map to the same `IMG_0001.xmp`. When two or more files in the
|
||||
library share a stem, the stem sidecar belongs to one of them — a raw first, otherwise the first by file name — and
|
||||
the others write and read `<file>.xmp` (`IMG_0001.JPG.xmp`), falling back to the stem sidecar for reading when they
|
||||
have none. So saving one photo never overwrites the other's metadata (`Session::sidecar_naming`).
|
||||
|
||||
## Reading `crs:` develop fields
|
||||
|
||||
Many raw developers store edits as `crs:` properties (`http://ns.adobe.com/camera-raw-settings/1.0/`) in sidecars, in
|
||||
|
||||
Reference in New Issue
Block a user