CLI: --format hevc / av1 write HEVC / AV1; an unknown comp is named (#154, #155)

#154: renderQueue.add (and render.addOutputModule) applied `format`, then
dropped it before applying the output path, so `--format hevc --out
x.mp4` saw the .mp4 extension and switched the module back to H.264
(and ProRes / VP9 for .mov / .webm). An explicit format now wins and a
mismatched extension follows it (x.webm → x.mp4 for AV1), as choosing a
format in the Output Module renames Output To; an extension that agrees
with the format keeps the name as given. The format error lists hevc and
av1 everywhere (one FORMATS string).

#155: a `comp` that names no composition (name or id) failed with "no
active composition". comp_id now reports it as `no composition "Nope"`
(EngineError::NoSuchComp), and comp_for_queue passes that on instead of
collapsing it.

Tests: an_explicit_format_wins_over_the_output_extension and
an_unknown_comp_is_named_in_the_error (engine). Checked with the CLI:
the files are hevc/hvc1 and av1/av01 and decode cleanly with ffmpeg.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bflatastic
2026-10-07 12:28:12 +07:00
co-authored by Claude Opus 5.5
parent 21c0d33597
commit c4f8e50201
5 changed files with 77 additions and 28 deletions
+46
View File
@@ -51,3 +51,49 @@ fn hevc_and_av1_output_module_options() {
assert!(f["hevcLevels"].as_array().unwrap().contains(&json!("4.1")));
assert_eq!(f["webmCodecs"], json!(["vp9", "av1"]));
}
/// An explicit format wins over the output file's extension, which follows it (#154:
/// `effectcraft-cli render --format hevc --out x.mp4` wrote H.264). Without a format, the extension
/// still picks one.
#[test]
fn an_explicit_format_wins_over_the_output_extension() {
let mut s = session();
let cases = [
("hevc", "/out/x.mp4", OutputFormat::Hevc, "/out/x.mp4"),
("av1", "/out/x.mp4", OutputFormat::Av1, "/out/x.mp4"),
("hevc", "/out/x.mov", OutputFormat::Hevc, "/out/x.mp4"),
("av1", "/out/x.webm", OutputFormat::Av1, "/out/x.mp4"),
("prores", "/out/x.mp4", OutputFormat::ProRes, "/out/x.mov"),
];
for (k, (format, out, want, path)) in cases.into_iter().enumerate() {
s.execute("renderQueue.add", json!({"format": format, "output": out})).unwrap();
let om = &s.project.render_queue[k].output;
assert_eq!((om.format, om.output.as_str()), (want, path), "{format} → {out}");
// A second output module of the item behaves the same.
s.execute("render.addOutputModule", json!({"index": k + 1, "format": format, "output": out})).unwrap();
let extra = &s.project.render_queue[k].extra_outputs[0];
assert_eq!((extra.format, extra.output.as_str()), (want, path), "addOutputModule {format} → {out}");
}
// No format: the extension picks it, as before. A format the extension agrees with keeps
// the name as given (the CLI passes the extension as the format).
s.execute("renderQueue.add", json!({"output": "/out/y.mov"})).unwrap();
assert_eq!(s.project.render_queue.last().unwrap().output.format, OutputFormat::ProRes);
s.execute("renderQueue.add", json!({"format": "png", "output": "/out/still.png"})).unwrap();
let om = &s.project.render_queue.last().unwrap().output;
assert_eq!((om.format, om.output.as_str()), (OutputFormat::PngSequence, "/out/still.png"));
}
/// A `comp` that names no composition says so, rather than "no active composition" (#155).
#[test]
fn an_unknown_comp_is_named_in_the_error() {
let mut s = session();
for (cmd, p) in [
("renderQueue.add", json!({"comp": "Nope", "output": "/out/x.mp4"})),
("layer.newSolid", json!({"comp": "Nope"})),
("comp.settings", json!({"comp": 99_999, "width": 10})),
] {
let e = s.execute(cmd, p.clone()).unwrap_err().to_string();
let name = p["comp"].to_string();
assert!(e.contains(&format!("no composition {name}")), "{cmd}: {e}");
}
}