Broll overlay skill

Broll overlay — Poster frame: a crumpled plastic bottle with sad eyes lying in a wet gutter under a street lamp (from the novoads/agent-skills README)

Illustration · not a resultFrom the novoads/agent-skills README — shows the whole collection, not only this skill.source ↗

Overlays silent b-roll cutaway clips onto a finished base video — the base audio keeps running underneath while the picture cuts away and returns, and the final duration always equals the base duration.

by novoads·MIT license·★ 23 Stars on the repo·GitHub ↗

Use now
Broll overlay — Poster frame: a crumpled plastic bottle with sad eyes lying in a wet gutter under a street lamp (from the novoads/agent-skills README)

Illustration · not a resultFrom the novoads/agent-skills README — shows the whole collection, not only this skill.view on GitHub

Files of Broll overlay

novoads/main1 file shown
SKILL.md
Show the full text164 lines

B-roll overlay

Lays silent cutaway clips over a finished base video: the base audio runs untouched underneath, the picture cuts away and returns, and the final duration equals the base duration exactly. That is the whole contract — overlay, never concatenation. A previous run concatenated: 24s for a 15s base, voice dead 12s.

Assembly mechanics live entirely in scripts/broll_overlay.py; this file is judgment only — when to enter, where the windows go, what the user sees first.

Entry condition

Two things must already exist. This skill never generates the base — if there isn't one yet, say so and stop.

  1. A finished, approved base video with exactly one audio stream. Approved means the user has watched it and signed off — b-roll is a polish pass over a locked cut. The script stream-copies one voice track under the picture, so a two-stream base is rejected: ffmpeg -i base -map 0:v -map 0:a:0 -c copy.

  2. Its transcript, with timings. One call to POST /v1/transcripts with the base's jobId (or its assetId if it was uploaded). It returns text, word-level words[] and segments[] — timings in SECONDS, which is what the EDL below takes — plus an srt you can ignore here. Price it with the transcript arm of POST /v1/estimates and quote that number, never one from this file. The meter is per minute of source, rounded up from a one-minute minimum, and a repeat of the same source is free, so asking twice costs once. No local install.

    (The captions endpoint still cannot supply this — it burns subtitles into a new MP4 and returns no text or timings, verified 2026-08-04 and re-checked when transcripts shipped. That finding is correct and is kept here so nobody re-discovers it and files a bug against captions.)

    Offline fallback: a local whisper pass (whisper-cli or openai-whisper) still works and is the path when there is no API key or no network. Note whisper reports timings in milliseconds where the API reports seconds, and whisper-cli with no model downloaded returns an EMPTY transcript rather than an error — which reads exactly like a bad render. See README for the model download.

    Placement is read from the transcript, not eyeballed.

REST key required. A Novoads MCP connector is not a substitute. If NOVOADS_API_KEY is missing or still the placeholder, stop before any generation work and tell the user: "Before continuing, create an API key at https://novoads.ai/dashboard/settings?tab=api and paste it into .env." That holds even when mcp__novoads__* tools are connected and authenticated in the session. Never call mcp__novoads__* tools from this repo's workflows: they are a different surface with different behavior, including the units they quote costs in. Repo installs verify with ./scripts/check-novoads-env.sh; a solo install checks NOVOADS_API_KEY in the environment.

Pack version. Every /v1 response carries X-Novoads-Pack-Version; mention a newer pack at https://github.com/novoads/agent-skills only when that header names a version NEWER than this file's metadata.packVersion — equal or older is nothing to say, and it is never a reason to stop.

Workflow

  1. Read the transcript. Note what is said and when — the words drive every decision below.
  2. Generate the b-roll clips, only after base approval. One clip per moment you intend to cover, each rendered silent (audioEnabled: false). Overlay audio is ignored by the script regardless, and generating before approval spends credits on a cut that may still change. Price the set live first — POST /v1/estimates this session, the total shown against the user's balance, an explicit yes. Four cutaways is four charges, and a remembered price is not a price. Cast the base actor in every cutaway that shows a person: pass the same referenceAssetIds the base used — Seedance re-casts every render, and a stranger's hands or skin breaks the ad (observed live 2026-08-04: Black base actor, five white-cast cutaways). With no identity ref, keep people out of frame entirely — objects and environments, not "face out of frame".
  3. Propose an EDL. JSON with base, output, and overlays (file, start, end, covers). covers is mandatory and quotes the spoken line the window illustrates — a window you can't quote is one you're guessing at.
  4. Show the EDL to the user and wait for approval. Rendering before the user has seen the plan is precisely the failure this step prevents.
  5. Render: python3 scripts/broll_overlay.py edl.json
  6. Report the script's verification output verbatim — the measured durations, the audio check and the window count, not a paraphrase. Then offer 2–3 cut variations (different windows, fewer cutaways, a tighter opener) to react to.

Placement judgment

Default cadence, measured 2026-08-04 frame by frame from the reference edit this pack reproduces — ~15.5s, 10–11 shots, a cut every ~1.4s:

  • Alternate A-B-A-B: the face returns between every cutaway. Never two overlay windows back to back; the talking head is the spine.
  • 4–6 windows per 15s, ~1–1.5s each, ~40–45% coverage. Short and frequent. This retires our older "fewer, longer windows" rule — taste, reversed by measurement; our own two renders ran 2 windows of 2.5s/2.0s, ~30% coverage.
  • The b-roll may travel; the base never does. The reference visits 4 distinct b-roll settings across 2–3 rooms; the talking head never moves.
  • Never cover the opening hook beat. The first beat is a face making a claim; cutting away there spends the retention the hook just bought.
  • End on the person, not on b-roll. The closing beat is the verdict, and it only lands if the viewer is looking at whoever delivers it.
  • Put product shots where the product is being spoken about. B-roll that illustrates the current sentence reads as evidence; anything else is filler.

Default shot plan. The reference's five cutaways tell one arc: problem → stress → product macro → dose/usage → relief — 2 product shots to 3 emotional beats. Adapt the imagery to the script, keep the ratio.

Evidence: one measured edit (n=1) — a default, not a law; depart deliberately and say why (evals.md OV3/OV6). Every run prints its own cadence against this envelope; --stats prints it without rendering.

Hard rule: assembly always goes through the script

Never hand-write overlay or concat ffmpeg for this task. broll_overlay.py owns validation, rendering and verification, and it fails loudly on exactly what fails silently by hand: duration drift, re-encoded audio, overlapping or zero-frame windows, a clip shorter than its window, a rotated phone base composited at the wrong geometry, a window that quietly composited nothing. Improvised ffmpeg is how the founding failure happened.

It needs ffmpeg 7.1 or newer. The script scales each overlay against the base branch with scale=w=rw:h=rh, and those reference constants arrived in 7.1. On an older build — Ubuntu 24.04's apt ships 6.1 — the graph does not parse and the error names rw, not a version. Check with ffmpeg -version before blaming the EDL; on Debian/Ubuntu the fix is a static build, not apt install ffmpeg.

Escape hatch: if a request genuinely doesn't fit the EDL model — a speed ramp, a picture-in-picture inset, audio that actually needs editing — say so and ask the user how to proceed. Do not improvise around the script.

Other modes

  • python3 scripts/broll_overlay.py edl.json --dry-run — validate the EDL and print the plan, rendering nothing. Cheap way to check window geometry.
  • python3 scripts/broll_overlay.py edl.json --stats — window count, lengths, coverage, base-return gaps, each marked against the envelope. Never an error.
  • python3 scripts/broll_overlay.py --verify-only FINAL --base BASE [--edl edl.json] — re-check any output against its base, including one from an old session. Pass --edl whenever you have it: without it the check is duration + audio only, which a plain copy of the base also passes; with it each window's midpoint frame is compared against the base, so a window that composited nothing fails. A run without --edl says so out loud.

Exit codes: 2 validation, 3 render, 4 verification. A nonzero exit is a real failure — surface it, don't retry blindly. Renders go to a hidden temp file beside the output, renamed only after verification passes: a failed run never leaves a half-written or unverified file at the output path.

Evals

evals.md defines the six scenarios this skill is held to (OV1–OV6). scripts/test_broll_overlay.py implements the mechanical ones against synthetic fixtures — run it after touching the script.

1---
2name: broll-overlay
3metadata: {packVersion: 1.3.0}
4description: >-
5 Overlays silent b-roll cutaway clips onto a finished base video — the base
6 audio keeps running underneath while the picture cuts away and returns, and
7 the final duration always equals the base duration. Use when the user asks to
8 add b-roll, add cutaways, or overlay clips on a video: "add b-roll to this",
9 "cut away to a product shot here", "overlay these clips on my UGC ad". Entered
10 from an approved base video plus its transcript, which comes from
11 POST /v1/transcripts and needs no local install; it never generates the base
12 itself and it never extends a video.
13---
14 
15# B-roll overlay
16 
17Lays silent cutaway clips **over** a finished base video: the base audio runs
18untouched underneath, the picture cuts away and returns, and the final duration
19equals the base duration exactly. That is the whole contract — overlay, never
20concatenation. A previous run concatenated: 24s for a 15s base, voice dead 12s.
21 
22Assembly mechanics live entirely in `scripts/broll_overlay.py`; this file is
23judgment only — when to enter, where the windows go, what the user sees first.
24 
25## Entry condition
26 
27Two things must already exist. This skill never generates the base — if there
28isn't one yet, say so and stop.
29 
301. **A finished, approved base video with exactly one audio stream.** Approved
31 means the user has watched it and signed off — b-roll is a polish pass over a
32 locked cut. The script stream-copies one voice track under the picture, so a
33 two-stream base is rejected: `ffmpeg -i base -map 0:v -map 0:a:0 -c copy`.
342. **Its transcript, with timings.** One call to **`POST /v1/transcripts`** with
35 the base's `jobId` (or its `assetId` if it was uploaded). It returns `text`,
36 word-level `words[]` and `segments[]` — **timings in SECONDS**, which is what
37 the EDL below takes — plus an `srt` you can ignore here. **Price it with the
38 transcript arm of `POST /v1/estimates` and quote that number**, never one from
39 this file. The meter is per minute of source, rounded up from a one-minute
40 minimum, and **a repeat of the same source is free**, so asking twice costs
41 once. No local install.
42 
43 *(The **captions** endpoint still cannot supply this — it burns subtitles into
44 a new MP4 and returns no text or timings, verified 2026-08-04 and re-checked
45 when transcripts shipped. That finding is correct and is kept here so nobody
46 re-discovers it and files a bug against captions.)*
47 
48 **Offline fallback:** a local whisper pass (`whisper-cli` or `openai-whisper`)
49 still works and is the path when there is no API key or no network. Note
50 whisper reports timings in **milliseconds** where the API reports seconds, and
51 `whisper-cli` with no model downloaded returns an EMPTY transcript rather than
52 an error — which reads exactly like a bad render. See README for the model
53 download.
54 
55 Placement is read from the transcript, not eyeballed.
56 
57> **REST key required. A Novoads MCP connector is not a substitute.** If
58> `NOVOADS_API_KEY` is missing or still the placeholder, stop before any
59> generation work and tell the user: "Before continuing, create an API key at
60> <https://novoads.ai/dashboard/settings?tab=api> and paste it into `.env`."
61> That holds even when `mcp__novoads__*` tools are connected and authenticated in
62> the session. Never call `mcp__novoads__*` tools from this repo's workflows: they
63> are a different surface with different behavior, including the units they quote
64> costs in. Repo installs verify with `./scripts/check-novoads-env.sh`; a solo
65> install checks `NOVOADS_API_KEY` in the environment.
66 
67**Pack version.** Every `/v1` response carries `X-Novoads-Pack-Version`; mention a newer pack at <https://github.com/novoads/agent-skills> only when that header names a version NEWER than this file's `metadata.packVersion` — equal or older is nothing to say, and it is never a reason to stop.
68 
69## Workflow
70 
711. **Read the transcript.** Note what is said and when — the words drive every
72 decision below.
732. **Generate the b-roll clips, only after base approval.** One clip per moment
74 you intend to cover, each rendered silent (`audioEnabled: false`). Overlay
75 audio is ignored by the script regardless, and generating before approval
76 spends credits on a cut that may still change.
77 **Price the set live first — `POST /v1/estimates` this session, the total
78 shown against the user's balance, an explicit yes.** Four cutaways is four
79 charges, and a remembered price is not a price.
80 **Cast the base actor in every cutaway that shows a person:** pass the same
81 `referenceAssetIds` the base used — Seedance re-casts every render, and a
82 stranger's hands or skin breaks the ad (observed live 2026-08-04: Black base
83 actor, five white-cast cutaways). With no identity ref, keep people out of
84 frame entirely — objects and environments, not "face out of frame".
853. **Propose an EDL.** JSON with `base`, `output`, and `overlays` (`file`,
86 `start`, `end`, `covers`). `covers` is mandatory and **quotes the spoken line
87 the window illustrates** — a window you can't quote is one you're guessing at.
884. **Show the EDL to the user and wait for approval.** Rendering before the user
89 has seen the plan is precisely the failure this step prevents.
905. **Render:** `python3 scripts/broll_overlay.py edl.json`
916. **Report the script's verification output verbatim** — the measured durations,
92 the audio check and the window count, not a paraphrase. Then offer 2–3 cut
93 variations (different windows, fewer cutaways, a tighter opener) to react to.
94 
95## Placement judgment
96 
97Default cadence, measured 2026-08-04 frame by frame from the reference edit this
98pack reproduces — ~15.5s, 10–11 shots, a cut every ~1.4s:
99 
100- **Alternate A-B-A-B: the face returns between every cutaway.** Never two
101 overlay windows back to back; the talking head is the spine.
102- **4–6 windows per 15s, ~1–1.5s each, ~40–45% coverage.** Short and frequent.
103 This retires our older "fewer, longer windows" rule — taste, reversed by
104 measurement; our own two renders ran 2 windows of 2.5s/2.0s, ~30% coverage.
105- **The b-roll may travel; the base never does.** The reference visits 4
106 distinct b-roll settings across 2–3 rooms; the talking head never moves.
107- **Never cover the opening hook beat.** The first beat is a face making a
108 claim; cutting away there spends the retention the hook just bought.
109- **End on the person, not on b-roll.** The closing beat is the verdict, and it
110 only lands if the viewer is looking at whoever delivers it.
111- **Put product shots where the product is being spoken about.** B-roll that
112 illustrates the current sentence reads as evidence; anything else is filler.
113 
114**Default shot plan.** The reference's five cutaways tell one arc: problem →
115stress → product macro → dose/usage → relief — 2 product shots to 3 emotional
116beats. Adapt the imagery to the script, keep the ratio.
117 
118Evidence: one measured edit (n=1) — a default, not a law; depart deliberately and
119say why (evals.md OV3/OV6). Every run prints its own cadence against this
120envelope; `--stats` prints it without rendering.
121 
122## Hard rule: assembly always goes through the script
123 
124Never hand-write overlay or concat ffmpeg for this task. `broll_overlay.py` owns
125validation, rendering and verification, and it fails loudly on exactly what
126fails silently by hand: duration drift, re-encoded audio, overlapping or
127zero-frame windows, a clip shorter than its window, a rotated phone base
128composited at the wrong geometry, a window that quietly composited nothing.
129Improvised ffmpeg is how the founding failure happened.
130 
131**It needs ffmpeg 7.1 or newer.** The script scales each overlay against the base
132branch with `scale=w=rw:h=rh`, and those reference constants arrived in 7.1. On an
133older build — Ubuntu 24.04's apt ships 6.1 — the graph does not parse and the
134error names `rw`, not a version. Check with `ffmpeg -version` before blaming the
135EDL; on Debian/Ubuntu the fix is a static build, not `apt install ffmpeg`.
136 
137**Escape hatch:** if a request genuinely doesn't fit the EDL model — a speed
138ramp, a picture-in-picture inset, audio that actually needs editing — say so and
139ask the user how to proceed. Do not improvise around the script.
140 
141## Other modes
142 
143- `python3 scripts/broll_overlay.py edl.json --dry-run` — validate the EDL and
144 print the plan, rendering nothing. Cheap way to check window geometry.
145- `python3 scripts/broll_overlay.py edl.json --stats` — window count, lengths,
146 coverage, base-return gaps, each marked against the envelope. Never an error.
147- `python3 scripts/broll_overlay.py --verify-only FINAL --base BASE [--edl edl.json]`
148 — re-check any output against its base, including one from an old session.
149 **Pass `--edl` whenever you have it:** without it the check is duration +
150 audio only, which a plain copy of the base also passes; with it each window's
151 midpoint frame is compared against the base, so a window that composited
152 nothing fails. A run without `--edl` says so out loud.
153 
154Exit codes: `2` validation, `3` render, `4` verification. A nonzero exit is a
155real failure — surface it, don't retry blindly. Renders go to a hidden temp file
156beside the output, renamed only after verification passes: a failed run never
157leaves a half-written or unverified file at the output path.
158 
159## Evals
160 
161`evals.md` defines the six scenarios this skill is held to (OV1–OV6).
162`scripts/test_broll_overlay.py` implements the mechanical ones against synthetic
163fixtures — run it after touching the script.
164 

Discussion

Alternatives

Higgsfield Social Hook SkillCrafts viral social media hook video prompts for TikTok, Instagram Reels, and YouTube Shorts via Higgsfield. Routes to kling3_0 (primary) or grok_video (fallback). Use when the user wants scroll-stopping hooks, viral short-form video, attention-grabbing openers, TikTok content, Reels content, Shorts content, or any social-media-optimized video.Creator · MITGab, o editor (/ogab)Gab, o editor de vídeo com IA (comando /ogab). Skill PRINCIPAL para qualquer trabalho de vídeo: criar um vídeo novo (Reels, YouTube Shorts/longo, Meta Ads, anúncio, institucional, vídeo de cliente) OU editar um vídeo/projeto/timeline no DaVinci Resolve (cortar, montar, legendar, colocar música, takes, lettering, motions, cor, áudio, exportar). Leva do roteiro até a timeline pronta no DaVinci: roteiro para aprovação, voz por IA, música, efeitos sonoros, takes de banco ou gerados, lettering animado, motions e gráficos com HyperFrames, legenda com métrica e conferência automática. Use SEMPRE que o usuário pedir para criar/fazer/editar um vídeo, mexer no DaVinci, reels, anúncio, pauta ou roteiro, mesmo sem citar a skill; as outras skills de vídeo (hyperframes, motion-graphics etc.) entram como ferramentas DENTRO deste fluxo. Comando: /ogab <pedido> ou /ogab <subcomando> (ajuda, config, update, pauta, cliente). Também cuida do Instagram: legenda do post, carrossel pronto em imagem (PNG/PDF na marca do cliente), gancho de Reel viral, hashtags, tirar cara de IA do texto, semana de conteúdo, perfil e dados do nicho.Creator · MITMuapi AI clippingTurn a long video into N viral-ready short clips with a single managed API call. Wraps muapi.ai's `/ai-clipping` endpoint, which handles transcription, highlight ranking through a virality framework (hook / emotional peak / opinion bomb / revelation / conflict / quotable / story peak / practical value), overlap dedupe, and vertical face-tracking auto-crop server-side. No local Whisper, no local LLM, no GPU.Creator · MITMuapi youtube shortsAuto-generate viral 9:16 YouTube Shorts (or TikTok / Reels clips) from a long-form video. Thin platform-aware wrapper around the AI Clipping skill — picks sensible defaults for short-form social platforms (9:16, 30–60s sweet spot) and delegates the actual highlight extraction + crop to muapi.ai's `/ai-clipping` endpoint.Creator · MIT