Merge PR #1959: Photo Merge: Merge a photo named twice only once (#1947)

This commit is contained in:
aiongg
2026-10-22 02:39:26 +00:00
3 changed files with 33 additions and 2 deletions
+4 -2
View File
@@ -404,10 +404,12 @@ pub fn parse(id: &str, p: &Value) -> Result<(MergeKind, MergeFinish)> {
}
impl Session {
/// Plan a merge of `ids` (catalog order of the given ids is kept).
/// Plan a merge of `ids` (catalog order of the given ids is kept). A photo named more than once is merged once
/// (issue #1947): it adds nothing to the merge, and each copy would be decoded and held in memory.
pub fn plan_merge(&self, kind: MergeKind, finish: MergeFinish, ids: &[PhotoId], preview: bool) -> Result<MergeJob> {
let mut sources = Vec::new();
for id in ids {
let mut seen = std::collections::HashSet::new();
for id in ids.iter().filter(|id| seen.insert(**id)) {
let p = self.catalog.photo(*id).ok_or(lightcraft_catalog::CatalogError::NoPhoto(*id))?;
match &p.source {
Source::File { path } => sources.push((*id, path.clone())),
+24
View File
@@ -226,3 +226,27 @@ fn panorama_size_is_held_to_memory() {
assert_eq!(mp(fit_in_memory(&asked, photos, gb(16))), 20.0, "a size asked for is kept when it fits");
assert_eq!(fit_in_memory(&full, 1e6, None), fit_in_memory(&full, 1e6, gb(8)), "memory unknown: taken as 8 GB");
}
/// A photo named twice is merged once (issue #1947): `[a, a, a]` is one photo, too few to merge, and `[a, b, a, b]`
/// merges two frames rather than holding four in memory.
#[test]
fn a_photo_named_twice_is_merged_once() {
let dir = temp_dir("dup-ids");
let mut paths = Vec::new();
for (i, b) in lightcraft_merge::synth::bracket_dngs(96, 64, &[-1.0, 1.0]).unwrap().into_iter().enumerate() {
let p = dir.join(format!("IMG_{i}.dng"));
std::fs::write(&p, b).unwrap();
paths.push(p.to_string_lossy().to_string());
}
let mut s = Session::new().with_fs();
let ids = import(&mut s, paths);
let (a, b) = (ids[0], ids[1]);
for cmd in ["merge.hdr", "merge.panorama"] {
let e = s.execute(cmd, &json!({"ids": [a, a, a], "preview": true})).unwrap_err();
assert!(e.to_string().contains("at least 2"), "{cmd}: {e}");
}
let (kind, finish) = crate::merge::parse("merge.hdr", &json!({})).unwrap();
let job = s.plan_merge(kind, finish, &[a, b, a, b, a].map(lightcraft_catalog::PhotoId), true).unwrap();
assert_eq!(job.sources.iter().map(|(p, _)| p.0).collect::<Vec<_>>(), vec![a, b]);
let _ = std::fs::remove_dir_all(&dir);
}
+5
View File
@@ -26,6 +26,11 @@
## 2026-10-15
### Photo Merge takes each photo once
- A merge that names the same photo more than once (through `ids` on the control channel, MCP or the CLI) now merges
it once (issue #1947). Naming one photo three times no longer "merges" it with itself, and a long repeated list no
longer decodes the photo again for every entry.
### Constrain Crop tightens the crop you see
- Geometry ▸ Constrain Crop now changes the crop itself, as in Lightroom (issue #1927). Ticking it, or moving a
Geometry slider, Upright or a lens correction while it is ticked, shrinks the crop about its centre until the blank