From c6b700fe83b8ecd11ceb6e22f00194469540286e Mon Sep 17 00:00:00 2001 From: ArchersIntel Date: Sun, 18 Oct 2026 02:17:33 -0500 Subject: [PATCH] feat(cli): `render --progress` reports frames on stderr when it is not a terminal A terminal already gets a live `[1/1] frame 16/180 ... ~14.2s left` line while `effectcraft-cli render` runs. A program that starts the CLI (FilmCraft runs it to render a composition for a clip) reads stderr through a pipe, so it saw nothing until the render ended. `--progress` prints one line per update instead, at most four a second: progress It has no effect on a terminal, which keeps its live line. Without the flag nothing changes. Documented in the usage text, the module header and docs/agents.md. Tests: cargo fmt --check and cargo clippy --all-targets -D warnings pass; the effectcraft-cli tests pass except render_out_is_relative_to_the_working_directory, which already fails on Windows (it compares a canonicalize()d `\?\` path with the path the CLI prints) and is unrelated to this change. Co-Authored-By: Claude Sonnet 5.5 --- apps/effectcraft-cli/src/main.rs | 18 ++++++++++++++++++ docs/agents.md | 1 + 2 files changed, 19 insertions(+) diff --git a/apps/effectcraft-cli/src/main.rs b/apps/effectcraft-cli/src/main.rs index fe523807..dd530dbe 100644 --- a/apps/effectcraft-cli/src/main.rs +++ b/apps/effectcraft-cli/src/main.rs @@ -16,6 +16,8 @@ //! [--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) +//! [--progress] report progress on stderr when it is not a terminal, up to four lines a second: +//! `progress ` //! effectcraft-cli render --template T.ectemplate [--values V.json] --out FILE //! effectcraft-cli render-frame --template T.ectemplate [--values V.json] [--time S] [--out F.png] //! effectcraft-cli render F.ecproj --queue render the project's Render Queue @@ -106,6 +108,8 @@ const USAGE: &str = "usage: effectcraft-cli ) render/render-frame --template T.ectemplate [--values V.json] render one template instance 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; @@ -189,6 +193,7 @@ const FLAGS: &[&str] = &[ "--small", "--adv3d", "--autosave", + "--progress", ]; struct Args { @@ -742,8 +747,21 @@ fn render(args: &Args, json_out: bool) -> Result<(), Failure> { } s.execute("renderQueue.render", json!({"wait": false})).map_err(err)?; let tty = std::io::IsTerminal::is_terminal(&std::io::stderr()); + // `--progress`: one line per update for a program reading stderr (a terminal gets the live line). + let lines = args.flag("--progress") && !tty; + let (mut last_line, mut last_done) = (std::time::Instant::now(), u64::MAX); while s.is_rendering() { std::thread::sleep(std::time::Duration::from_millis(100)); + if lines + && last_line.elapsed() >= std::time::Duration::from_millis(250) + && let Some(p) = s.render_progress() + && p.done != last_done + { + last_line = std::time::Instant::now(); + last_done = p.done; + let left = p.remaining.map_or_else(|| "-".to_string(), |r| format!("{r:.1}")); + note!("progress {} {} {:.1} {left}", p.done, p.total, p.item_elapsed); + } if tty && let Some(p) = s.render_progress() { let left = p.remaining.map(|r| format!(", ~{r:.1}s left")).unwrap_or_default(); let _ = write!( diff --git a/docs/agents.md b/docs/agents.md index 1bec71d5..fb9a4c20 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -667,6 +667,7 @@ effectcraft-cli render-frame main.ecproj --time 1 --max-side 640 --out f.png --j effectcraft-cli exec layer.newNull --bridge 9877 # same commands, against the live app effectcraft-cli exec file.exportLottie '{"comp":"Main","path":"main.json","includeExpressions":true}' main.ecproj --json effectcraft-cli exec file.importLottie '{"path":"anim.json"}' main.ecproj --save +effectcraft-cli render main.ecproj --comp Main --out main.mov --format prores --progress # progress lines on stderr for a program that reads them ``` Scripts written against After Effects' documented scripting API run with `script`: