mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-27 03:28:22 -06:00
#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:
co-authored by
Claude Opus 5.5
parent
21c0d33597
commit
c4f8e50201
@@ -19,7 +19,7 @@ use crate::{EngineError, Result, Session, cmd, query};
|
||||
|
||||
fn can_add(s: &Session) -> std::result::Result<(), String> {
|
||||
not_rendering(s)?;
|
||||
if s.comp_for_queue(&Value::Null).is_some() { Ok(()) } else { Err("select or open a composition".into()) }
|
||||
if s.comp_for_queue(&Value::Null).is_ok() { Ok(()) } else { Err("select or open a composition".into()) }
|
||||
}
|
||||
fn not_rendering(s: &Session) -> std::result::Result<(), String> {
|
||||
if s.is_rendering() { Err("the render queue is rendering".into()) } else { Ok(()) }
|
||||
@@ -202,6 +202,9 @@ fn apply_om_template(om: &mut OutputModule, t: &OutputModule) {
|
||||
om.name = t.name.clone();
|
||||
}
|
||||
|
||||
/// The `format` parameter's values.
|
||||
const FORMATS: &str = "format: h264|hevc|av1|prores|webm|png|jpeg|tiff|exr|gif|wav|aiff";
|
||||
|
||||
/// Apply Output Module parameters (`template` first). `roi`: the viewer's region of interest
|
||||
/// (captured by `crop.useRoi`).
|
||||
fn apply_output(om: &mut OutputModule, templates: &RenderTemplates, roi: Option<[f64; 4]>, p: &Value, cmd: &str) -> Result<bool> {
|
||||
@@ -213,7 +216,7 @@ fn apply_output(om: &mut OutputModule, templates: &RenderTemplates, roi: Option<
|
||||
}
|
||||
let before = om.clone();
|
||||
if let Some(f) = str_p(p, "format") {
|
||||
let f = OutputFormat::from_name(f).ok_or_else(|| bad(cmd, "format: h264|hevc|av1|prores|webm|png|jpeg|tiff|exr|gif|wav|aiff"))?;
|
||||
let f = OutputFormat::from_name(f).ok_or_else(|| bad(cmd, FORMATS))?;
|
||||
om.set_format(f);
|
||||
}
|
||||
if let Some(c) = str_p(p, "channels") {
|
||||
@@ -357,13 +360,17 @@ fn apply_output(om: &mut OutputModule, templates: &RenderTemplates, roi: Option<
|
||||
}
|
||||
if let Some(o) = str_p(p, "output").or(str_p(p, "path")) {
|
||||
om.output = o.to_string();
|
||||
if p.get("format").is_none()
|
||||
&& let Some(f) = OutputFormat::from_path(o).filter(|f| *f != om.format && !o.contains("[fileExtension]"))
|
||||
{
|
||||
// `out.mov` with an H.264 module → ProRes, like picking a file type in Output To.
|
||||
let keep = om.output.clone();
|
||||
om.set_format(f);
|
||||
om.output = keep;
|
||||
if let Some(f) = OutputFormat::from_path(o).filter(|f| *f != om.format && !o.contains("[fileExtension]")) {
|
||||
if p.get("format").is_some() {
|
||||
// An explicit format wins and the extension follows it, as choosing a format in
|
||||
// the Output Module renames Output To (#154: `--format hevc --out x.mp4`).
|
||||
om.set_format(om.format);
|
||||
} else {
|
||||
// `out.mov` with an H.264 module → ProRes, like picking a file type in Output To.
|
||||
let keep = om.output.clone();
|
||||
om.set_format(f);
|
||||
om.output = keep;
|
||||
}
|
||||
}
|
||||
}
|
||||
if *om != before {
|
||||
@@ -496,24 +503,20 @@ fn item_json(s: &Session, it: &RenderQueueItem, index: usize) -> Value {
|
||||
}
|
||||
|
||||
fn add(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cid = s.comp_for_queue(p).ok_or(EngineError::NoComp)?;
|
||||
let cid = s.comp_for_queue(p)?;
|
||||
let mut it = RenderQueueItem::new(0, cid);
|
||||
// New items start from the Movie Default templates (Edit ▸ Templates).
|
||||
let templates = s.project.render_templates.clone();
|
||||
it.settings = templates.default_render_settings(TemplateSlot::Movie);
|
||||
it.output = templates.default_output_module(TemplateSlot::Movie);
|
||||
if let Some(f) = str_p(p, "format") {
|
||||
let f = OutputFormat::from_name(f).ok_or_else(|| bad("renderQueue.add", "format: h264|prores|webm|png|jpeg|tiff|exr|gif|wav|aiff"))?;
|
||||
let f = OutputFormat::from_name(f).ok_or_else(|| bad("renderQueue.add", FORMATS))?;
|
||||
if f != it.output.format {
|
||||
it.output = OutputModule::for_format(f);
|
||||
}
|
||||
}
|
||||
apply_settings(&mut it.settings, &templates, p, "renderQueue.add")?;
|
||||
let mut op = p.clone();
|
||||
if let Some(o) = op.as_object_mut() {
|
||||
o.remove("format");
|
||||
}
|
||||
apply_output(&mut it.output, &templates, s.state.region_of_interest, &op, "renderQueue.add")?;
|
||||
apply_output(&mut it.output, &templates, s.state.region_of_interest, p, "renderQueue.add")?;
|
||||
if let Some(l) = enum_p(p, "log", "renderQueue.add", RenderLog::parse, "errorsOnly|plusSettings|plusPerFrameInfo")? {
|
||||
it.log = l;
|
||||
}
|
||||
@@ -704,7 +707,7 @@ fn add_output_module(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let n = it.extra_outputs.len() + 2;
|
||||
let mut om = it.output.clone();
|
||||
if let Some(f) = str_p(p, "format") {
|
||||
let f = OutputFormat::from_name(f).ok_or_else(|| bad("render.addOutputModule", "format: h264|prores|webm|png|jpeg|tiff|exr|gif|wav|aiff"))?;
|
||||
let f = OutputFormat::from_name(f).ok_or_else(|| bad("render.addOutputModule", FORMATS))?;
|
||||
om = OutputModule::for_format(f);
|
||||
}
|
||||
if str_p(p, "output").is_none() && str_p(p, "path").is_none() {
|
||||
@@ -714,11 +717,7 @@ fn add_output_module(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
None => format!("{}_{n}", om.output),
|
||||
};
|
||||
}
|
||||
let mut op = p.clone();
|
||||
if let Some(o) = op.as_object_mut() {
|
||||
o.remove("format");
|
||||
}
|
||||
apply_output(&mut om, &s.project.render_templates, s.state.region_of_interest, &op, "render.addOutputModule")?;
|
||||
apply_output(&mut om, &s.project.render_templates, s.state.region_of_interest, p, "render.addOutputModule")?;
|
||||
s.edit("Add Output Module", None, |proj, _| {
|
||||
let it = &mut proj.render_queue[i];
|
||||
it.extra_outputs.push(om);
|
||||
@@ -733,7 +732,7 @@ fn add_output_module(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
/// Composition ▸ Pre-render…: queue the comp with a lossless-with-alpha module whose post-render
|
||||
/// action imports the result and replaces the comp's uses.
|
||||
fn pre_render(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cid = s.comp_for_queue(p).ok_or(EngineError::NoComp)?;
|
||||
let cid = s.comp_for_queue(p)?;
|
||||
let mut it = RenderQueueItem::new(0, cid);
|
||||
let templates = s.project.render_templates.clone();
|
||||
it.settings = templates.default_render_settings(TemplateSlot::PreRender);
|
||||
|
||||
Reference in New Issue
Block a user