$npx -y skills add gooseworks-ai/goose-skills --skill render-podcast-skitAssemble a two-host fake-podcast skit ad from a config — per-line lipsync clips hard-concatenated in script order, scaled/padded to 1080×1920, WHITE bottom-center captions (up to 5 words per cue, broken on sentence punctuation, word-wrapped to stay in-frame, held at least 0.9s) b
| 1 | # render-podcast-skit |
| 2 | |
| 3 | Assemble a **two-host fake-podcast skit** ad from a config: a skeptic and a believer at an |
| 4 | absurd themed podcast desk do a snappy back-and-forth about the product (the set is |
| 5 | deliberately unrelated — that is the joke). Each line is its own lipsync clip so the edit can |
| 6 | cut on the dialogue beat (~1.8s avg); this capability is the **FREE, deterministic assembly** |
| 7 | that concatenates those clips, renders the WHITE captions, and appends the brand end card. |
| 8 | |
| 9 | `scripts/config.example.json` is the worked example (Ladder run-02 "Laundromat 2am", ~49s |
| 10 | 1080×1920 9:16, ~22 lines); `scripts/PIPELINE.md` maps every config block to its source step |
| 11 | and `scripts/README.md` documents the free assembly. |
| 12 | |
| 13 | ## Run |
| 14 | |
| 15 | This is the **FREE, deterministic** assembly stage — it spends nothing. The paid inputs are |
| 16 | separate capabilities: one ElevenLabs **with-timestamps** VO per line (one voice per host) via |
| 17 | `create-vo-elevenlabs`; two photoreal base stills at the themed desk plus ~10 expression variants |
| 18 | (mouths NEUTRAL/CLOSED, **gpt-image-2 quality=high**, not nano-banana) via |
| 19 | `create-image-gpt-image-fal`; and one lipsync clip per (still, VO) pair via `create-video-fal`. |
| 20 | Given the per-line clips + their VO timestamps + the brand wordmark SVG, `render-podcast-skit` |
| 21 | walks the scenes in script order, builds the global caption timeline, renders the WHITE captions, |
| 22 | hard-concats the clips, auto-appends the end card, and final-encodes crf28 → the master. Re-cuts |
| 23 | reuse the existing VOs / stills / clips and cost **$0**. |
| 24 | |
| 25 | ## Contract (the free assembly) |
| 26 | |
| 27 | - **Dialogue-carried, no music bed by default.** The per-line VO is the audio; a podcast skit |
| 28 | needs no music (an optional low ambience is a taste call, off by default). |
| 29 | - **One line = one scene = one hard cut, in script order.** Hard-concat the per-line clips in |
| 30 | order (scale/pad to 1080×1920, re-encode) — no dissolves. |
| 31 | - **Captions from the VO's OWN char-level timestamps, not Whisper (script-window).** Build a |
| 32 | global `words.json` by offsetting each line's char-level word timings by the cumulative clip |
| 33 | start, group into **≤5-word cues broken on sentence-final punctuation**, and render **WHITE |
| 34 | `#FFFFFF`** bottom-center captions (black outline), **word-wrapped to stay in-frame** and held |
| 35 | **≥0.9s** — PIL PNG overlays when the host ffmpeg lacks libass (common), else ASS. (Yellow 3-word |
| 36 | karaoke was the old style, rejected in testing.) Whisper on the rendered clips mistimes; the VO |
| 37 | timestamps are ground truth. |
| 38 | - **End card via Playwright/PIL from the real wordmark — never AI-render brand text.** The |
| 39 | lockup is a deterministic HTML → PNG → 2.5s mp4 from the brand's real wordmark SVG (black bg, |
| 40 | brand wordmark, CTA pill, URL), auto-appended after the last line. A diffusion model garbles |
| 41 | a wordmark. |
| 42 | - **FFmpeg composite, deterministic, FREE.** Concat the clips, overlay the WHITE caption PNGs (or |
| 43 | burn ASS via libass), append the end-card mp4, and **final-encode `-preset slow -crf 28` + aac |
| 44 | 96k** → a 1080×1920 h264+aac master (~6MB for ~28s; the old `-crf 20` produced ~16MB). No paid |
| 45 | calls, no keys. |