Featured image of post Ten lines of XML so a cull shows up in Lightroom

Ten lines of XML so a cull shows up in Lightroom

A cull is worthless if it cannot follow you into Lightroom. Writing XMP sidecars out of krites, and being careful about what not to write.

My wife culls a wedding down from four thousand frames, and then the real work starts… in Lightroom, where roughly 90% of her editing happens and where krites has never been installed and never will be. Which meant the tool had a simple, brutal acceptance test from day one: if a cull’s verdicts can’t follow her into Adobe’s world, the cull didn’t happen.

Your own format is not the centre of the universe

It’s easy, building a tool, to treat your own database as the centre of everything. The cull lives here, in my verdicts file, in my format. And that’s fine… right up until the person you built it for opens her actual workflow, and your carefully-judged Keep/Maybe/Reject is sitting in a JSON file Lightroom has never heard of.

The spec called this out early and in plain terms: finishing in Lightroom is the main path, so the bridge to it is a first-class deliverable and not an afterthought. The only real question was which bridge.

The smallest artefact the other side already reads

Adobe’s ecosystem has a lingua franca for this: XMP sidecars. A sidecar is just a small XML file sitting next to the RAW (_MG_4021.CR3 gets an _MG_4021.xmp), and Lightroom reads it on import as naturally as it reads the photo. Ratings, labels, develop settings… photographers have been passing these between tools for twenty years.

So krites writes one. The entire packet is this template, ten lines of XML:

const sidecarTemplate = `<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/">
 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <rdf:Description rdf:about=""
    xmlns:xmp="http://ns.adobe.com/xap/1.0/"
    xmp:Rating="%d"
    xmp:Label="%s"/>
 </rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
`

(xmp.go at bb16b3b; the whole package, escaping and all, is 67 lines of Go.) The mapping is as boring as I could make it: a Keep is xmp:Rating 5, a Maybe is 3, a Reject is 1, and the label carries the verdict word itself. Hailey sorts by stars in Lightroom, sees exactly what the cull decided, and gets on with the part only she can do.

No SDK, no plug-in, no reverse-engineered catalogue format. The interoperability here isn’t about integrating with Lightroom at all. It’s leaving the smallest possible note somewhere Lightroom already looks.

What it refuses to write

The interesting design decisions are the negative ones. The sidecar never touches the original pixels, obviously (the whole tool is built around originals being sacred; writing XMP is opt-in via krites xmp write, and only ever the .xmp companion). But the spec draws a sharper line too: krites’ looks, retouches and object-removal never go in the sidecar, because XMP can’t faithfully express them, and a half-applied develop mapping is worse than none. Get a rating across the border intact and Hailey trusts it. Send her a develop preset that lands subtly wrong and she stops trusting everything else the file tells her… which is a much worse place to end up.

Two limitations, stated rather than hidden. Writing a sidecar currently overwrites any existing one, no merging; the read-and-round-trip half is spec’d but not yet built, so for now the bridge is one-way and the cull should happen before Lightroom’s own metadata does. And Lightroom’s colour-label set is configurable: it maps label strings to colours, so “Keep” shows up as a label but not necessarily a coloured one, not unless her label set has been taught the words. So the stars are doing the real work here. The labels are a nice-to-have on top, if you’ve bothered to set them up.

The version that asked Hailey to move house

There’s a version of this project that grew its own editing module, its own catalogue, its own everything, and asked Hailey to pack up and move house into it. Bigger codebase, more impressive on paper, and a worse tool. The ten lines above are, per line, the most valuable XML I’ve ever written. They’re the difference between “a program that has opinions about photos” and “a step in a working photographer’s day”.

The wedding gets culled at the kitchen table, the sidecars land beside the RAWs, Lightroom picks them up without being told, and the tool that did the judging politely leaves the room.

Built with Hugo · Theme Stack designed by Jimmy