diff --git a/apps/effectcraft-cli/src/main.rs b/apps/effectcraft-cli/src/main.rs index ea7cc34d1..43e514048 100644 --- a/apps/effectcraft-cli/src/main.rs +++ b/apps/effectcraft-cli/src/main.rs @@ -691,11 +691,10 @@ fn render(args: &Args, json_out: bool) -> Result<(), Failure> { let mut s = session(args)?; if args.opt("--template").is_some() { template::prepare(&mut s, args)?; - } else { - match &args.project { - Some(p) => s.execute("file.open", json!({"path": p})).map_err(|e| Failure::Error(e.to_string()))?, - None => s.execute("file.openDemoProject", json!({})).map_err(|e| Failure::Error(e.to_string()))?, - }; + } else if let Some(p) = &args.project { + s.execute("file.open", json!({"path": p})).map_err(|e| Failure::Error(e.to_string()))?; + } else if args.flag("--demo") || !args.flag("--empty") { + s.execute("file.openDemoProject", json!({})).map_err(|e| Failure::Error(e.to_string()))?; } let err = |e: effectcraft_engine::EngineError| Failure::Error(e.to_string()); if !args.flag("--queue") { diff --git a/apps/effectcraft-cli/tests/cli.rs b/apps/effectcraft-cli/tests/cli.rs index 7472e26bb..30a0271b6 100644 --- a/apps/effectcraft-cli/tests/cli.rs +++ b/apps/effectcraft-cli/tests/cli.rs @@ -539,3 +539,57 @@ fn render_bitrate_applies_to_vp9_webm() { assert!(high > low, "100 kbps: {low} bytes, 8000 kbps: {high} bytes"); let _ = std::fs::remove_dir_all(&root); } + +/// Render uses the same empty/project/demo selection as the other CLI commands. +#[test] +fn render_empty_does_not_load_the_demo() { + let root = tmp("render-empty"); + let _ = std::fs::remove_dir_all(&root); + std::fs::create_dir_all(&root).unwrap(); + let project = root.join("control.ecproj"); + let template = root.join("control.ectemplate"); + let project_path = project.to_str().unwrap(); + let template_path = template.to_str().unwrap(); + let build = r#" +var comp = app.project.items.addComp("Empty control", 64, 36, 1, 1, 30); +var solid = comp.layers.addSolid([0, 1, 0], "Green", 64, 36, 1); +solid.property("ADBE Transform Group").property("ADBE Opacity").addToMotionGraphicsTemplateAs(comp, "Opacity"); +"#; + ok_json(&["script", "--eval", build, "--save-as", project_path]); + let params = json!({"comp": "Empty control", "path": template_path}).to_string(); + let exported = ok_json(&["exec", "essential.exportTemplate", ¶ms, "--project", project_path]); + assert_eq!(exported["controls"], 1, "{exported}"); + let empty = ok_json(&["info", "--empty"]); + assert!(empty["project"]["items"].as_array().unwrap().is_empty(), "{empty}"); + + let render = |options: &[&str], name: &str| { + let output = root.join(name); + assert!(!output.exists()); + let mut args = vec!["render", "--format", "gif", "--start", "0", "--end", "0.1", "--resolution", "quarter", "--out", output.to_str().unwrap()]; + args.extend_from_slice(options); + let reply = ok_json(&args); + assert_eq!(reply["rendered"].as_array().unwrap().len(), 1, "{reply}"); + assert_eq!(PathBuf::from(reply["rendered"][0]["output"].as_str().unwrap()), output); + let bytes = std::fs::read(&output).unwrap(); + assert!(bytes.len() >= 10 && (bytes.starts_with(b"GIF87a") || bytes.starts_with(b"GIF89a")), "{reply}"); + let dimensions = (u16::from_le_bytes([bytes[6], bytes[7]]), u16::from_le_bytes([bytes[8], bytes[9]])); + (reply["rendered"][0]["comp"].clone(), dimensions) + }; + // Defaults still render the demo, and an explicit demo wins over --empty. + let demo = render(&[], "default.gif"); + assert_eq!(render(&["--demo", "--empty"], "demo.gif"), demo); + // A supplied project wins over both flags, while template mode also accepts --empty. + let project_result = render(&["--project", project_path, "--demo", "--empty"], "project.gif"); + assert_eq!(project_result, (json!("Empty control"), (16, 9))); + assert_eq!(render(&["--template", template_path, "--empty"], "template.gif").1, (16, 9)); + + let output = root.join("empty.gif"); + assert!(!output.exists()); + let (code, reply) = + run_json(&["render", "--empty", "--format", "gif", "--start", "0", "--end", "0.1", "--resolution", "quarter", "--out", output.to_str().unwrap()]); + let output_exists = output.exists(); + assert!(!output_exists, "--empty must not render the demo into the requested output"); + assert_eq!(code, 1, "{reply}"); + assert_eq!(reply["error"], "command `renderQueue.add` is not available right now: select or open a composition", "{reply}"); + let _ = std::fs::remove_dir_all(&root); +}