mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
CLI: render --out is relative to the working directory (#300)
`effectcraft-cli render --out proj/out/d.mp4 --project proj/x.ecproj` wrote `proj/proj/out/d.mp4`: the Render Queue resolves a relative output against its own base, while `--project` and every other CLI path are relative to the working directory. The CLI now makes `--out` absolute against the working directory before queueing it, so the file lands where the shell user expects; an absolute path is unchanged, and `rendered[].output` in the JSON result is the absolute path. Render Queue outputs set inside a project (and `render --queue`) resolve as before: tokens expanded, then `export.defaultOutputFolder`, the project's folder, the working directory. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
d60cb71e29
commit
a560830b64
3 files changed
+59
No files matched your search
@@ -15,6 +15,7 @@
|
||||
//! [--jpeg-quality N] [--bitrate KBPS] [--prores proxy|lt|standard|hq|4444|4444xq] [--audio auto|on|off]
|
||||
//! [--profile main|main10] [--level auto|4.1] [--rate-control bitrate|quality] [--video-quality 1-100]
|
||||
//! [--keyint FRAMES] [--webm-codec vp9|av1] [--audio-bitrate KBPS] [--opus-app audio|voice]
|
||||
//! (a relative --out is relative to the working directory; --queue outputs follow the project's own settings)
|
||||
//! effectcraft-cli render F.ecproj --queue render the project's Render Queue
|
||||
//! effectcraft-cli bench [--comp C] [--time S] [--scale K] [--n N] [--play N] [--gpu [--adv3d]] render timings
|
||||
//! (--gpu: CPU vs GPU ms/frame for every comp at Full and Half)
|
||||
@@ -100,6 +101,7 @@ const USAGE: &str = "usage: effectcraft-cli <info|commands|exec|run|props|get|se
|
||||
[--keyint FRAMES] [--webm-codec vp9|av1] [--audio-bitrate KBPS] [--opus-app audio|voice] | --queue
|
||||
(formats h264|hevc|av1|prores|webm|png|jpeg|tiff|exr|gif|wav|aiff;
|
||||
--profile..--keyint: HEVC / AV1, --audio-bitrate/--opus-app: WebM Opus)
|
||||
a relative --out is relative to the working directory
|
||||
bench [--comp C] [--time S] [--scale K] [--n N] [--play N] [--gpu [--adv3d]] per-layer/effect render timings;
|
||||
--play N renders N consecutive frames with/without the layer cache;
|
||||
--gpu compares CPU and GPU ms/frame for every comp at Full and Half
|
||||
@@ -587,6 +589,12 @@ fn with_saved(v: Value, saved: Option<String>) -> Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// A relative `--out` is relative to the working directory, like `--project` and every other CLI
|
||||
/// path. The Render Queue alone would resolve it against the project's folder.
|
||||
fn from_cwd(path: &str) -> Result<String, Failure> {
|
||||
std::path::absolute(path).map(|p| p.to_string_lossy().into_owned()).map_err(|e| Failure::Error(format!("render: cannot resolve --out {path}: {e}")))
|
||||
}
|
||||
|
||||
/// `render`: queue `--comp` (or the active comp) with the given settings unless `--queue`, then
|
||||
/// render the queue with a progress line on stderr. Fails if any item fails.
|
||||
fn render(args: &Args, json_out: bool) -> Result<(), Failure> {
|
||||
@@ -602,6 +610,7 @@ fn render(args: &Args, json_out: bool) -> Result<(), Failure> {
|
||||
let err = |e: effectcraft_engine::EngineError| Failure::Error(e.to_string());
|
||||
if !args.flag("--queue") {
|
||||
let Some(out) = args.opt("--out") else { return usage_err("render: --out FILE is required (or --queue)") };
|
||||
let out = &from_cwd(out)?;
|
||||
let mut p = json!({"output": out});
|
||||
if let Some(c) = args.opt("--comp") {
|
||||
p["comp"] = json!(c);
|
||||
|
||||
@@ -247,3 +247,52 @@ fn script_file_and_eval() {
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(err.contains(".jsxbin scripts are not supported: run the .jsx source") && !err.contains("SyntaxError"), "{err}");
|
||||
}
|
||||
|
||||
/// `render --out` (#300): a relative path is relative to the working directory like `--project`,
|
||||
/// not to the project's folder (which doubled a path that already named it).
|
||||
#[test]
|
||||
fn render_out_is_relative_to_the_working_directory() {
|
||||
let root = tmp("render-out");
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
let (proj, elsewhere) = (root.join("proj"), root.join("elsewhere"));
|
||||
std::fs::create_dir_all(&proj).unwrap();
|
||||
std::fs::create_dir_all(&elsewhere).unwrap();
|
||||
let project = proj.join("x.ecproj");
|
||||
let p = project.to_str().unwrap();
|
||||
ok_json(&[
|
||||
"run",
|
||||
"comp.new",
|
||||
r#"{"name":"T","width":64,"height":36,"duration":1}"#,
|
||||
"layer.newSolid",
|
||||
r#"{"width":64,"height":36}"#,
|
||||
"--empty",
|
||||
"--save-as",
|
||||
p,
|
||||
]);
|
||||
assert!(project.exists());
|
||||
let render = |cwd: &std::path::Path, project: &str, out: &str| {
|
||||
let o = bin()
|
||||
.current_dir(cwd)
|
||||
.args(["render", "--project", project, "--format", "gif", "--start", "0", "--end", "0.1", "--resolution", "quarter", "--out", out, "--json"])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(o.status.success(), "{out}: {}", String::from_utf8_lossy(&o.stderr));
|
||||
let v: Value = serde_json::from_slice(&o.stdout).unwrap();
|
||||
PathBuf::from(v["rendered"][0]["output"].as_str().unwrap())
|
||||
};
|
||||
// A path that already names the project folder is not doubled.
|
||||
let done = render(&root, "proj/x.ecproj", "proj/out/d.gif");
|
||||
assert_eq!(done, root.join("proj/out/d.gif"));
|
||||
assert!(done.exists() && !proj.join("proj").exists());
|
||||
// Another working directory: the file lands there, not beside the project.
|
||||
let done = render(&elsewhere, project.to_str().unwrap(), "out/card.gif");
|
||||
assert_eq!(done, elsewhere.join("out/card.gif"));
|
||||
assert!(done.exists() && !proj.join("out/card.gif").exists());
|
||||
// `./f.gif` too.
|
||||
assert_eq!(render(&elsewhere, project.to_str().unwrap(), "./f.gif"), elsewhere.join("./f.gif"));
|
||||
// An absolute path is unchanged.
|
||||
let abs = root.join("abs.gif");
|
||||
assert_eq!(render(&elsewhere, project.to_str().unwrap(), abs.to_str().unwrap()), abs);
|
||||
assert!(abs.exists());
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
Reference in new issue
Block a user