From 56cc93f4ea67096bf5fec66367c2ae3efdb9d1a5 Mon Sep 17 00:00:00 2001 From: Peet Guercio Date: Tue, 6 Oct 2026 05:42:03 -0700 Subject: [PATCH] interchange: an FCP7 master clip lands in its bin when a sequence used it first Co-Authored-By: Claude Opus 5.5 --- crates/interchange/src/fcp7.rs | 7 +++++++ crates/interchange/tests/fcp7.rs | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/crates/interchange/src/fcp7.rs b/crates/interchange/src/fcp7.rs index dadd1a391..33e8e0b8c 100644 --- a/crates/interchange/src/fcp7.rs +++ b/crates/interchange/src/fcp7.rs @@ -192,6 +192,13 @@ impl<'a, 'i> Imp<'a, 'i, '_> { match file { Some(f) => { let id = self.file(f, child_text(n, "name"), bin); + // A sequence's clip items file their media at the top level, so a sequence earlier in the + // document that used this file first left it there; the master clip's bin is its home. + if let (Some(item), Some(b)) = (id, bin) + && self.b.p.root.parent_of(item) == Some(self.b.p.root.id) + { + self.b.p.move_to_bin(&[item], Some(b)); + } if let Some(item) = id.and_then(|i| self.b.p.item_mut(i)) && let Some(l) = path_text(n, &["labels", "label2"]).and_then(label_from) { diff --git a/crates/interchange/tests/fcp7.rs b/crates/interchange/tests/fcp7.rs index 05ccef0c6..2b72d1e43 100644 --- a/crates/interchange/tests/fcp7.rs +++ b/crates/interchange/tests/fcp7.rs @@ -449,3 +449,37 @@ proptest! { prop_assert_eq!(links(&imp.project, si), links(&p, s)); } } + +#[test] +fn a_master_clip_used_by_an_earlier_sequence_still_lands_in_its_bin() { + // Premiere and other writers may put a stringout sequence ahead of the bins that hold its clips. + let file = format!( + r#"take.movfile://localhost/media/take.mov{RATE}120"# + ); + let doc = format!( + r#" + +Order + Stringouts + All takes120{RATE} + + + + FootageComedy + take120{RATE} + + + +"# + ); + let (imp, _) = import(doc.as_bytes(), Format::Fcp7Xml, None).unwrap(); + let p = &imp.project; + let mut items = Vec::new(); + p.root.all_items(&mut items); + let take = items.iter().copied().find(|i| p.item(*i).unwrap().name == "take.mov").unwrap(); + assert_eq!(items.iter().filter(|i| **i == take).count(), 1); + let bin = p.root.parent_of(take).unwrap(); + assert_ne!(bin, p.root.id, "the take was left loose at the top"); + assert_eq!(p.root.find_bin(bin).unwrap().name, "Comedy"); +}