From da131fcd57d1380904e606b74cef7f90d0cdfe37 Mon Sep 17 00:00:00 2001 From: Nigel Ihza Firdausy <106657905+Ni-zav@users.noreply.github.com> Date: Sat, 10 Oct 2026 15:51:14 +0700 Subject: [PATCH] fix(app): open .ecprojx project copies passed on the command line (#528) --- apps/effectcraft/src/main.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/apps/effectcraft/src/main.rs b/apps/effectcraft/src/main.rs index 72afab5a..52782349 100644 --- a/apps/effectcraft/src/main.rs +++ b/apps/effectcraft/src/main.rs @@ -36,6 +36,15 @@ fn app_icon() -> egui::IconData { eframe::icon_data::from_png_bytes(png).unwrap_or_default() } +/// Both native JSON projects and Save a Copy As XML projects are openable on launch. +/// Keep this classification shared by project opening and the subsequent media-import pass. +fn is_project_file(path: &str) -> bool { + std::path::Path::new(path) + .extension() + .and_then(|ext| ext.to_str()) + .is_some_and(|ext| ext.eq_ignore_ascii_case("ecproj") || ext.eq_ignore_ascii_case("ecprojx")) +} + /// The command line. #[derive(Debug, Default, PartialEq)] struct Args { @@ -143,7 +152,7 @@ fn main() -> eframe::Result { } session.load_settings(); let recovery = if control_port.is_none() { session.begin_recovery() } else { None }; - let project = files.iter().find(|f| f.ends_with(".ecproj")).cloned(); + let project = files.iter().find(|f| is_project_file(f)).cloned(); if let Some(p) = project { if let Err(e) = session.execute("file.open", json!({"path": p})) { eprintln!("effectcraft: {e}"); @@ -151,7 +160,7 @@ fn main() -> eframe::Result { } else if demo { let _ = session.execute("file.openDemoProject", json!({})); } - let media: Vec = files.iter().filter(|f| !f.ends_with(".ecproj")).cloned().collect(); + let media: Vec = files.iter().filter(|f| !is_project_file(f)).cloned().collect(); if !media.is_empty() && let Err(e) = session.execute("file.import", json!({"paths": media})) { @@ -353,6 +362,23 @@ mod tests { assert_eq!(super::device_limits(big).max_texture_dimension_2d, 16384); } + /// A positional .ecprojx is a project file, not an unsupported media import (#528). + #[test] + fn launch_classifies_xml_and_json_projects_as_projects() { + for path in ["Copy.ecprojx", "/tmp/Scene.ecproj", "C:\\\\work\\\\Shot.ECPROJX"] { + assert!(super::is_project_file(path), "{path}"); + } + for path in ["scene.ecprojx.png", "scene.mp4", "scene", "copy.ecprojx.bak"] { + assert!(!super::is_project_file(path), "{path}"); + } + let files = ["scene.ecprojx".to_string(), "clip.mov".to_string()]; + assert_eq!(files.iter().find(|f| super::is_project_file(f)).map(String::as_str), Some("scene.ecprojx")); + assert_eq!( + files.iter().filter(|f| !super::is_project_file(f)).map(String::as_str).collect::>(), + vec!["clip.mov"], + ); + } + /// Launching without a project opens an empty project, not the demo (#204). #[test] fn no_arguments_start_an_empty_project() {