Video Render Batch (A-S7.5 in-process render)
The renderer that turns a video plan into actual clips
- render_report_json
- clip_manifest_json
OTR_VideoRenderBatch is where the OldTimeRadio pipeline stops planning and starts burning GPU time. Everything upstream - the script, the voices, the frozen audio master, the per-role video policy from OTR_VideoDirector, the shot plan from OTR_ShotLock, the episode stills - has already happened. This node walks the engine registry and renders one real per-beat clip per shot, then hands the finished clips downstream to OTR_SilentComposite for assembly. It's the workhorse, and it's why this pack needs a GPU at all if you dial past the procedural floor.
The one widget that matters is mode. episode is the production path - render a real episode from patched_ledger_json, the ShotLock-planned, image-genned ledger. soak and single are diagnostic harness modes for the author's own QA rig; most users will never touch them, and the widgets that look like they matter there (beats, oom_index, frame_count, engine) are only read in those modes. In episode mode they're inert - the beat count and frame budget come from the planned ledger, not your widgets. So when you're looking at this node in a real workflow, set mode to episode and leave the rest alone.
Wiring, for when you're building the graph by hand rather than loading the canonical workflow:
patched_ledger_json- the ShotLock-planned ledger. Non-negotiable inepisodemode.master_audio_path- path to the frozen master mix fromOTR_EpisodeAssembler.output_path. Beats that lack their own per-line audio get it sliced from this file, read-only, byffmpeg. The master is never mutated. Leave it unset and the node degrades loudly on missing audio.image_done- an ordering gate fromOTR_ImageGenDispatcher.image_done. Consuming that opaque token forces the image phase (every episode still on disk) to finish before video rendering starts. It's never parsed - just a handshake.
It returns render_report_json (a structured report of what each engine delivered) and clip_manifest_json (the beat-ordered manifest that feeds OTR_SilentComposite). It's an output node, so it can sit at the end of a render-gate workflow with nothing wired downstream and still run.
The mechanism is the interesting part. This node must run inside ComfyUI's executor thread (a normal /prompt queue) rather than a background route - only there does ComfyUI's model_management evict the umt5/whisper encoders between encode and sample, which is what keeps the heavy in-process forward under the VRAM ceiling. It holds a single resident engine at a time, and any per-beat detach/reclaim happens inside the selected engine wrapper. The frozen audio section stays read-only the whole time. That "one engine resident, explicit reclaim" discipline is the same philosophy as the rest of the pack: no surprises, and when an engine OOMs or a model is missing, it stops with a named error rather than quietly swapping in something else.
Two troubleshooting notes worth keeping. First, install via ComfyUI Manager ("ComfyUI-OldTimeRadio") or clone from GitHub into custom_nodes/, check out v2.0-alpha, restart - then place the Stable Audio / voice / image weights yourself, because nothing auto-downloads and a missing weight fails this node loudly. Second, if you're experimenting: mode=soak with oom_index set will inject a simulated OOM at a chosen beat index to test the fallback trail - it's a genuinely nice diagnostic, but it's for understanding failure behavior, not for making episodes. And know the pack's honest known limitation going in: occasionally an episode hands a line to the wrong character, and nothing fixes that. The render still completes; it just isn't perfect.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| mode | COMBO | soak | Run mode. 'episode' is the production path (renders one REAL per-beat clip per shot from patched_ledger_json). 'soak' and 'single' are diagnostic harness modes -- some widgets below are read ONLY in a specific mode (see engine / oom_index). |
| beats | INT | 401–400 | Diagnostic harness only (mode=soak): synthetic beat count. Ignored in mode=episode (beats come from the planned ledger). |
| oom_index | INT | 200–399 | Diagnostic harness only -- read ONLY in mode=soak (the beat index at which to inject a simulated OOM for fallback-trail testing). Inert in mode=single and mode=episode. |
| frame_count | INT | 251–240 | Diagnostic harness only: per-clip frame count, consumed by mode=single AND mode=soak. Ignored in mode=episode (the per-shot frame budget is planned upstream). |
| engineopt | STRING | viz_camera | Diagnostic harness only -- read ONLY in mode=single (forces the single engine to render). Inert in mode=soak and mode=episode (episode routes each beat by its planned per-role engine). |
| portrait_pathopt | STRING | Diagnostic harness only (mode=single and mode=soak): conditioning still for the harness render. Inert in mode=episode, where each beat's still comes from the planned ledger. | |
| audio_pathopt | STRING | Diagnostic harness only (mode=single and mode=soak): driving audio for the harness render. Inert in mode=episode, where per-beat audio comes from the episode's own clips. | |
| patched_ledger_jsonopt | STRING | {} | mode=episode: the OTR_ShotLock-planned (and image-genned) ledger JSON. run_real_episode renders one REAL per-beat clip per shot; the emitted clip manifest feeds OTR_SilentComposite. |
| master_audio_pathopt | STRING | mode=episode: path to the FROZEN master mix (MP4 or WAV). Beats whose ledger line has no per-line *_wav_path get audio_ref filled by slicing [start_s, start_s+dur_s] from this file (read-only ffmpeg; master is NEVER mutated). Wire from OTR_EpisodeAssembler.output_path (the frozen master WAV). Leave unset to degrade LOUD on missing per-line wavs. | |
| image_doneopt | STRING | Image-done gate (mirrors audio_done; ST-0.2 still-spine). Wire from OTR_ImageGenDispatcher.image_done so the video render cannot start before every episode still exists on disk (W4). Opaque STRING; the token is never parsed. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| render_report_json | STRING | — |
| clip_manifest_json | STRING | — |