---
name: b-roll-assembler
description: Lay generated b-roll over a talking-head / avatar / podcast video (A-roll) or build a whole edit from b-roll over a voice-over. B-roll is placed at given seconds, at the moment a keyword is spoken in the SRT, or spread into the free gaps; full-frame cutaways or picture-in-picture, with short fades, the A-roll's audio kept. Validates the timeline with a dry run, then renders with a preview sheet and a timeline CSV. Use when the user wants cutaways, b-roll, illustrative shots over narration, or a faceless video made from a script.
compatibility: Needs the Kenerate MCP server to make the b-roll (and A-roll / voice-over) and Python 3.9+ with Pillow and ffmpeg.
metadata:
  author: kenerate
  version: "1.1"
  kenerate-tools: "generate_video generate_image talking_avatar generate_speech pick_model pick_voice estimate_cost wait_for_generation"
---

# B-roll assembler

Kenerate makes the shots; `scripts/broll.py` times and composites them over the A-roll locally. Rules:
[references/KENERATE.md](references/KENERATE.md); models and voices: [references/MODELS.md](references/MODELS.md).

## Ask the user
1. The A-roll (talking head, `talking_avatar` result, podcast video) or a voice-over track. No voice-over yet? Ask
   whether they want one - never add it on your own. If yes: `generate_speech(text, engine="elevenlabs-eleven-v3",
   voice=<from pick_voice(use_case=...)>)` (always pass a voice).
2. The script / SRT, and which lines deserve b-roll (or "pick the best 4-6 moments").
3. Cutaway (full frame) or picture-in-picture; how long each (2-3 s reads well).

## 1. Plan the shots, then the cost
Pick moments: nouns and claims ("our beans", "in 30 seconds") make the best keywords. For each: a prompt and a
length. Silent b-roll: `generate_video(prompt=..., model="kenerate/wan-3.0", audio=false, aspect_ratio=<A-roll's>, resolution="720p", duration=3-5)`
(or `pick_model(task="silent b-roll")`); stills are fine too (`generate_image`, they get a slow push-in).
`estimate_cost` for one clip x N, show the total, get a yes above ~100 credits, start all, wait for each.

## 2. Dry run the timeline (free)
Write `plan.json`:
```
{"aroll": "<A-roll url>", "srt": "talk.srt",
 "broll": [{"src": "<clip url>", "keyword": "beans", "dur": 2.5},
           {"src": "<clip url>", "at": 7.2, "dur": 3, "in": 0.5},
           {"src": "<image url>", "mode": "pip"}]}
```
`python <skill-dir>/scripts/broll.py --plan plan.json --dry-run` prints every shot's start / length and warns about
keywords it couldn't find, overlaps and shots it had no room for. Fix the plan until it's clean.

## 3. Render
`python <skill-dir>/scripts/broll.py --plan plan.json [--mode pip --pip-pos tr] [--skip-start 2]`
- The first `--skip-start` seconds stay on the A-roll (the hook / face). B-roll is muted; the A-roll's sound plays.
- Voice-over only (`"aroll": "voiceover.mp3"`): shots run back to back over the whole track (9:16 by default).
- Output: `broll_edit.mp4`, `*_preview.jpg` (one frame per cutaway, show it), `*_timeline.csv`.
- Captions next: `captions-pro` on the result.

## Progress and output
Steps: A-roll → fetch + normalise b-roll → base track → composite (percent) → preview. `RESULT_JSON.shots` is the
final timeline.

## If something fails
A failed b-roll job is refunded: render without it (drop it from the plan) or retry on approval. A b-roll clip
shorter than its slot holds its last frame.

## Limits
B-roll never overlaps itself (later shots trim earlier ones). Keyword matching is plain text in the SRT.
