mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-01 22:20:00 -06:00
Merge remote-tracking branch 'origin/main' into HEAD
This commit is contained in:
@@ -254,8 +254,11 @@ fn queue_frame(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
if let Some(path) = str_p(p, "path") {
|
||||
it.output.output = path.to_string();
|
||||
} else if !it.output.format.is_sequence() {
|
||||
it.output.output = "[compName]_frame.[fileExtension]".into();
|
||||
} else {
|
||||
if !it.output.format.is_sequence() {
|
||||
it.output.output = "[compName]_frame.[fileExtension]".into();
|
||||
}
|
||||
s.avoid_sources(&mut it);
|
||||
}
|
||||
let id = s.edit("Save Frame As", None, |proj, _| {
|
||||
it.id = proj.render_queue.iter().map(|i| i.id).max().unwrap_or(0) + 1;
|
||||
|
||||
@@ -1000,8 +1000,7 @@ fn list(_: &mut Session, p: &Value) -> Result<Value> {
|
||||
let mut out = json!({"id": p.id, "name": p.name, "type": p.default.kind_name(), "default": p.default.to_json()});
|
||||
match &p.ui {
|
||||
effectcraft_project::ParamUi::Popup { options } => {
|
||||
// Engine/MCP popup values are zero-based; JSX exposes one-based values.
|
||||
out["options"] = json!(options.iter().enumerate().map(|(value, label)| json!({"value": value, "label": label})).collect::<Vec<_>>());
|
||||
out["options"] = super::popup_options(options);
|
||||
}
|
||||
effectcraft_project::ParamUi::Slider { min, max, slider_min, slider_max, decimals } => {
|
||||
out["min"] = json!(min);
|
||||
|
||||
@@ -58,6 +58,12 @@ pub(crate) fn selected_leaf_props(s: &Session) -> Vec<(LayerId, effectcraft_proj
|
||||
out
|
||||
}
|
||||
|
||||
/// A popup's `[{value, label}]`, as `effect.list`, `prop.get` and `layer.tree` report it.
|
||||
/// Engine/MCP popup values are zero-based; JSX exposes one-based values.
|
||||
pub(crate) fn popup_options(options: &[String]) -> Value {
|
||||
serde_json::json!(options.iter().enumerate().map(|(value, label)| serde_json::json!({"value": value, "label": label})).collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
// ---------- params ----------
|
||||
|
||||
pub(crate) fn bad(cmd: &str, msg: impl Into<String>) -> EngineError {
|
||||
|
||||
@@ -491,6 +491,9 @@ fn get(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
"time": t.seconds(), "value": raw.to_json(), "animated": !pr.keys.is_empty(),
|
||||
"keys": keys, "expression": pr.expr.as_ref().map(|e| e.text.clone()),
|
||||
});
|
||||
if let Some(o) = l.popup_options(pr) {
|
||||
out["options"] = super::popup_options(&o);
|
||||
}
|
||||
// With an expression, `value` is the keyframed (pre-expression) value; also report what the
|
||||
// expression makes of it (what renders), or why it fails. A disabled one renders `value`; one
|
||||
// disabled by its syntax error (prop.setExpression) reports that error.
|
||||
|
||||
@@ -191,6 +191,8 @@ fn create_proxy(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
it.output.channels = Channels::Rgba;
|
||||
if let Some(o) = str_p(p, "path").or(str_p(p, "output")) {
|
||||
it.output.output = o.to_string();
|
||||
} else {
|
||||
s.avoid_sources(&mut it);
|
||||
}
|
||||
it.post_render = PostRenderAction::SetProxy;
|
||||
let id = s.edit("Create Proxy", None, |proj, _| {
|
||||
|
||||
@@ -127,6 +127,9 @@ fn group_json(g: &PropGroup, path: &str, l: &Layer, t: effectcraft_time::Tick, d
|
||||
if let Some(e) = &p.expr {
|
||||
o["expression"] = json!(e.text);
|
||||
}
|
||||
if let Some(opts) = l.popup_options(p) {
|
||||
o["options"] = super::popup_options(&opts);
|
||||
}
|
||||
o
|
||||
}
|
||||
Node::Group(g) => group_json(g, &cpath, l, t, depth + 1, max_depth),
|
||||
|
||||
@@ -550,6 +550,9 @@ fn add(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
object.remove("template");
|
||||
}
|
||||
apply_output(&mut it.output, &templates, s.state.region_of_interest, &output_params, "renderQueue.add")?;
|
||||
if str_p(p, "output").or(str_p(p, "path")).is_none() {
|
||||
s.avoid_sources(&mut it);
|
||||
}
|
||||
if let Some(l) = enum_p(p, "log", "renderQueue.add", RenderLog::parse, "errorsOnly|plusSettings|plusPerFrameInfo")? {
|
||||
it.log = l;
|
||||
}
|
||||
@@ -696,8 +699,18 @@ fn render(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
{
|
||||
log::warn!("{e}");
|
||||
}
|
||||
let queued: std::collections::HashSet<u64> = s.project.render_queue.iter().filter(|i| i.is_queued()).map(|i| i.id).collect();
|
||||
let ids = s.start_render(wait).map_err(EngineError::Other)?;
|
||||
let items: Vec<Value> = s.project.render_queue.iter().enumerate().filter(|(_, i)| ids.contains(&i.id)).map(|(k, i)| item_json(s, i, k)).collect();
|
||||
// Source refusals fail before the exporter starts. Report them just like encoding
|
||||
// failures, so CLI/MCP callers do not mistake a partly refused queue for full success.
|
||||
let items: Vec<Value> = s
|
||||
.project
|
||||
.render_queue
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, i)| ids.contains(&i.id) || (queued.contains(&i.id) && matches!(i.status, RenderStatus::Failed(_))))
|
||||
.map(|(k, i)| item_json(s, i, k))
|
||||
.collect();
|
||||
Ok(json!({"rendering": s.is_rendering(), "items": items}))
|
||||
}
|
||||
|
||||
@@ -788,6 +801,9 @@ fn pre_render(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
it.output = templates.default_output_module(TemplateSlot::PreRender);
|
||||
it.output.output = "[compName]_prerender.[fileExtension]".into();
|
||||
apply_output(&mut it.output, &templates, s.state.region_of_interest, p, "render.preRender")?;
|
||||
if str_p(p, "output").or(str_p(p, "path")).is_none() {
|
||||
s.avoid_sources(&mut it);
|
||||
}
|
||||
it.post_render = PostRenderAction::ImportAndReplace;
|
||||
let id = s.edit("Pre-render", None, |proj, _| {
|
||||
it.id = proj.render_queue.iter().map(|i| i.id).max().unwrap_or(0) + 1;
|
||||
@@ -813,6 +829,9 @@ fn save_current_preview(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let mut item = RenderQueueItem::new(0, cid);
|
||||
item.output = OutputModule::for_format(format);
|
||||
item.output.output = path.clone();
|
||||
if let Some(refusal) = s.check_outputs(&[vec![(item.clone(), path.clone())]]).first() {
|
||||
return Err(EngineError::Other(refusal.reason.clone()));
|
||||
}
|
||||
let job = crate::ExportJob {
|
||||
project: &s.project,
|
||||
footage: s.footage.as_ref(),
|
||||
|
||||
Reference in New Issue
Block a user