Merge pull request #676 from vbasky/fix/592-import-replace

Add file.import replace mode for empty-project round-trips
This commit is contained in:
Brandon Thomas 2026-10-10 14:54:33 -04:00 committed by GitHub
commit b07df164fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 26 additions and 2 deletions

View File

@ -945,7 +945,7 @@ fn build() -> Vec<CommandSpec> {
Ok(json!({"items": ids.iter().map(|i| i.0).collect::<Vec<_>>()}))
}
),
cmd!("file.import", "Import…", ["File"], Some("Cmd+I"), r#"{"paths":[str],"bin":binId?,"imageSequence":bool?}"#, always, |s, p| {
cmd!("file.import", "Import…", ["File"], Some("Cmd+I"), r#"{"paths":[str],"bin":binId?,"imageSequence":bool?,"replace":bool?}"#, always, |s, p| {
let bin = u64_p(p, "bin").map(filmcraft_project::BinId);
let paths: Vec<String> = match p.get("paths").and_then(Value::as_array) {
Some(a) => a.iter().filter_map(|v| v.as_str().map(str::to_string)).collect(),
@ -954,6 +954,10 @@ fn build() -> Vec<CommandSpec> {
if paths.is_empty() {
return Err(bad("file.import", "need `paths`"));
}
if bool_p(p, "replace").unwrap_or(false) {
let name = s.project.name.clone();
s.execute("file.newProject", json!({ "name": name }))?;
}
let mut ids = Vec::new();
let mut errors = Vec::new();
let mut sequences = Vec::new();

View File

@ -11,6 +11,24 @@ use serde_json::{Value, json};
use crate::Session;
use crate::media_test_util::tmp_dir;
#[test]
fn import_replace_starts_from_an_empty_project() {
let dir = tmp_dir("import-replace");
let wav = dir.join("tone.wav");
std::fs::write(&wav, filmcraft_media::wav::write_wav16(&[0.1; 4800], 2, 48_000)).unwrap();
let mut s = demo();
let demo_items = s.project.items.len();
assert!(demo_items > 1);
assert!(!s.state.open_sequences.is_empty());
s.execute("file.import", json!({"paths": [wav.to_string_lossy()], "replace": true})).unwrap();
assert!(s.state.open_sequences.is_empty());
assert_eq!(s.project.items.len(), 1);
assert!(s.project.items.values().any(|it| it.name == "tone.wav"));
let mut merge = demo();
merge.execute("file.import", json!({"paths": [wav.to_string_lossy()]})).unwrap();
assert!(merge.project.items.len() > demo_items);
}
fn demo() -> Session {
let mut s = Session::default();
s.execute("file.openDemoProject", json!({})).unwrap();

View File

@ -696,7 +696,9 @@ Video encoders implement `export::VideoEncoder`. Codec crates plug in with `regi
Timelines can be exchanged as EDL, FCP7 XML, FCPXML, OTIO, AAF or OMF. `file.import` detects these
formats and merges the result into the project as one undoable step (AAF / OMF embedded audio is
written next to the document first), and `file.exportInterchange`, `file.exportEdl`,
written next to the document first). `file.import {replace: true}` starts from an empty project
with the same name (`file.newProject`) so a headless import→export round-trip contains only the
imported timeline. `file.exportInterchange`, `file.exportEdl`,
`file.exportFcpxml`, `file.exportOtio`, `file.exportAaf` and `file.exportOmf` write them.
`file.exportInterchange {format}` takes `edl`, `xml` (the default), `fcpxml`, `otio`, `aaf` or `omf`;
any other value is a parameter error and nothing is written. For AAF and