Repository navigation
Secondary artefacts during render: blocked on ResolvedScenario's construction site and the encode loops #179
Description
The "secondary artefacts emitted during render" gap (High/M) from the re-scored Remotion differential. Investigated alongside #178 and refused, with the reasoning recorded here.
What is missing
grep -rn "artifacts" crates/rustmotion-core/src/schema/scenario.rs returns nothing. still and captions are independent subcommands invoked separately from the video render, so producing a thumbnail and a subtitle file next to a video takes three invocations that re-render or re-decode the same content.
Three independent blockers
Each is sufficient on its own; all were found by reading the code.
1. ResolvedScenario has exactly one construction site, and it is not where you would add the field.
ResolvedScenario — the type render, still, info and batch actually consume — is built only at crates/rustmotion/src/include.rs:76-82; every path through loader.rs goes through include::resolve_includes. A field added to Scenario in schema/scenario.rs therefore stays invisible to render.rs unless include.rs is touched too. And crates/rustmotion-studio/src/scenario/model.rs:89 (empty_scenario()) would stop compiling regardless.
2. "Do not re-render" is architecturally blocked, which is the whole point of the feature.
Per-frame RGBA buffers only materialise inside the per-format encode loops — h264.rs:244 inside a rayon::par_iter closure, and the same shape in ffmpeg.rs and formats.rs. render_frame_task(...) is called there, converted to YUV and dropped immediately. Nothing hands a finished frame to a general-purpose hook.
The only function able to produce an arbitrary frame outside those loops is render_frame_task_scaled, which is the render. Calling it a second time for a thumbnail reproduces exactly the cost of the separate rustmotion still invocation the feature exists to replace — so a naive implementation would deliver the syntax without the benefit.
3. Captions cannot be reused cleanly as they stand.
cmd_captions shells whisper.cpp (absent from the environment this was investigated in, so unverifiable there) and writes its output with a bare std::fs::write at captions.rs:422 — not atomic, contradicting the discipline PR #145 (helper commands must not destroy files they do not own) and PR #151 (write to a sibling, promote on success) established.
The honest path, for later
Extract the thumbnail from the already-produced video file, with ffmpeg -ss <t> -i output.mp4, after the encode finishes. Zero additional Skia work, and it reuses the sub-process pattern already in assets.rs. It sidesteps blocker 2 entirely.
That does not help until blocker 1 is resolved: the declaration still has to reach render.rs, which means include.rs and the studio's empty_scenario() move together with schema/scenario.rs.
Captions would need captions.rs's write made atomic first — worth doing on its own merits regardless of this gap.
Open questions for whoever picks this up
- Should a failed artefact fail the video, or be reported and skipped? The render is the expensive part; losing it because a thumbnail could not be written seems wrong, but silently producing fewer outputs than declared is the failure mode this repository keeps eliminating.
- Where do artefact paths resolve from — the scenario's directory, or the output's?
--fix's refusal rules and PR fix(cli): stop the helper commands destroying files they do not own #145's file-ownership guards are the precedent to follow.
Activity