diff --git a/crates/engine/src/render_queue.rs b/crates/engine/src/render_queue.rs index 56754e7c..74b33315 100644 --- a/crates/engine/src/render_queue.rs +++ b/crates/engine/src/render_queue.rs @@ -362,8 +362,15 @@ impl Session { // Settings ▸ Export ▸ Default Output Folder, else the project's folder. let folder = self.prefs.export.default_output_folder.trim(); let base = (!folder.is_empty()).then(|| std::path::PathBuf::from(folder)); - let base = - base.or_else(|| self.path.as_deref().and_then(|s| std::path::Path::new(s).parent().map(|d| d.to_path_buf())).filter(|d| !d.as_os_str().is_empty())); + // A project saved under a bare filename has the empty path as its parent. It sits in the + // working directory, so resolve against that rather than falling through to the + // unsaved-project default below (#1427). + let base = base.or_else(|| { + self.path + .as_deref() + .and_then(|s| std::path::Path::new(s).parent()) + .map(|d| if d.as_os_str().is_empty() { std::path::PathBuf::from(".") } else { d.to_path_buf() }) + }); // Unsaved project: ~/Movies, else the home folder (as FilmCraft's exports do). The working directory // is `/` for an app started from the macOS Finder, where nothing can be written (#446). let home = std::env::var_os("HOME").or_else(|| std::env::var_os("USERPROFILE")).map(std::path::PathBuf::from); diff --git a/crates/engine/src/tests_m145.rs b/crates/engine/src/tests_m145.rs index e52e3ffb..93125af1 100644 --- a/crates/engine/src/tests_m145.rs +++ b/crates/engine/src/tests_m145.rs @@ -280,6 +280,28 @@ fn import_export_helpers() { assert_eq!(p.recent_fonts_shown(), ["A".to_string(), "C".to_string()]); } +#[test] +fn render_queue_resolves_a_bare_project_filename_against_the_working_directory() { + use effectcraft_project::render_queue::{OutputFormat, RenderQueueItem}; + let mut s = session(); + let cid = s.active_comp_id().unwrap(); + let mut item = RenderQueueItem::new(1, cid); + item.output.output = "clip.mp4".into(); + item.output.format = OutputFormat::H264; + + // `--project ./proj.ecproj`: the parent is `.`, so the output goes beside the project. + s.path = Some("./proj.ecproj".into()); + let dotted = s.resolve_output(&item).unwrap(); + + // `--project proj.ecproj`: the parent is the empty path, which must mean the same folder + // rather than the unsaved-project default (#1427). + s.path = Some("proj.ecproj".into()); + let bare = s.resolve_output(&item).unwrap(); + + assert_eq!(PathBuf::from(&bare), PathBuf::from(&dotted), "a bare filename resolved to {bare} instead of {dotted}"); + assert_eq!(PathBuf::from(&bare), PathBuf::from("./clip.mp4")); +} + #[test] fn render_queue_uses_default_folder_bit_depth_names_and_segments() { use effectcraft_project::render_queue::{OutputFormat, RenderQueueItem};