The TWV video pipeline got its biggest quality jump yet — real audio, human-paced narration, and karaoke captions — without touching the deterministic core.
A 3-agent audit + viral-channel research produced an 8-phase roadmap. This session shipped Phases 0, 1, 2 and most of 3: reliability fixes, real Envato SFX/music at broadcast loudness, role-based voiceover pacing, word-by-word captions, and three new short-form layouts. The single loudest amateur tell (synthesized sine-wave SFX + one music bed at −25 LUFS) is gone. Two items were deliberately deferred because they risk the number↔voice sync gate the whole engine is built on.
6-layer thinking chainChuỗi tư duy 6 tầng
"bạn rà soát lại skill tạo video của TWV xem có thể nâng cấp những gì để tạo nên 1 video mượt mà, chất lượng, cinematic, chuyên nghiệp... hình ảnh visual, hiệu ứng cho đến content mọi khía cạnh; ứng dụng best practices của các kênh viral; flow làm việc cũng tối ưu cả chất lượng lẫn token."
- The render architecture is deliberately deterministic — the LLM only writes scripts, everything else is ffmpeg + Remotion math. Must not break that.
- Voicebox (local TTS) was OFFLINE this session (http 000), so longform voiceover changes can't be live-rendered — only validated by deterministic timing tests + the shorts path that bakes voiceover already.
- The entire engine locks visuals to voice: every spoken number must be on screen within 0.6s. Any change that desyncs audio from the cut endangers that gate.
- Free-tier quotas (Gemini 250 vision calls/day) and a solo operator — no paid subscriptions beyond the Envato Elements the user already owns.
- Two SKILL.md files described pipelines that no longer exist (Canva, ElevenLabs) — forcing a re-discovery tax every session.
- Rewrite the renderer for a "cinematic" look — highest ceiling, but throws away a working, token-cheap engine and can't be verified in one session. Cost: enormous risk, low confidence.
- Chase visible effects first (transitions, LUTs) — feels productive, but the real amateur tell was audio, not visuals. Cost: polishing the wrong layer.
- Fix the measured ceilings in dependency order — reliability → audio → voice → visual, each verified before the next. Cost: slower, unglamorous phase 0, but everything after stands on solid ground. ✅
How the upgraded engine fits together
Two render paths share one asset + audio spine. The short-form path renders through Remotion; the long-form path composites through ffmpeg. This session added a single source of truth for timing, a shared audio manifest, and role-driven voiceover — so both paths speak the same language.
| Technical termThuật ngữ kỹ thuật | Plain nameTên dễ hiểu | RoleVai trò |
|---|---|---|
timing.py | One clock everyone reads | The cut-offset / shot-start math used to live copied across four files; one wrong copy made the sync gate lie. Now assemble, sync-qa, quality-gate and verify all import the same functions — proven numerically identical to the old code. |
sfx-manifest.json + music_pool.py | Real sound, rotated | Logical SFX names (whoosh/pop/riser…) resolve to real Envato samples with variant rotation so back-to-back cuts don't repeat; music picks by mood (tension/optimism/data/warm) least-recently-used, so the channel never repeats a track. |
narrate.py roles | Voice that breathes | Each voiceover paragraph gets a role (hook/explain/stat/punch/cta) that sets its own pace and the silence around it — the read speeds through a hook and takes a real 0.7s breath before a number lands. |
Captions.tsx | Karaoke captions | Words light up one at a time across the line's cue-accurate window — the 2026 standard for the 85% who watch muted. |
yt-retention.js | Start collecting the truth | Pulls YouTube retention curves per video into content/retention/ from today, so the future analytics loop (Phase 7) has history to learn from. |
Read it in 30 seconds: the top row is where a video is authored; the middle row is the three shared spines this session hardened (timing, audio, data); the bottom row is the two render paths that now both draw from those spines. Nothing in the deterministic core was replaced — the upgrades slot into the existing math.
What shipped, in dependency order
Layered decision cardsCác quyết định theo tầng
Artifact mapBản đồ tệp tạo ra
| PathĐường dẫn | WhatLà gì | Who reads itAi dùng |
|---|---|---|
scripts/twv-automation/longform/timing.py | NEW — single source of truth for xfade-chain timing math | Imported by assemble.py, sync-qa.py, quality-gate.py, verify.py |
scripts/twv-automation/longform/assemble.py | Staleness fix, transition lint, 3 grade presets, alternating Ken-Burns, manifest-driven SFX | Long-form ffmpeg compositor |
scripts/twv-automation/longform/narrate.py | Per-beat role pacing, breaths before stats, emphasis passthrough | Long-form voiceover (Voicebox) |
scripts/twv-automation/longform/quality-gate.py + run.py | Auto-fix clobber guard (never replace curated/pinned footage) | QA gate + orchestrator |
scripts/twv-automation/music_pool.py | NEW — mood-tagged, least-recently-used music selector | Both render paths |
.../remotion/src/clip/components/Captions.tsx | Word-by-word karaoke reveal | Short-form TwvClip |
.../remotion/src/clip/scenes/SplitCompare|ChecklistBeat|QuoteCard.tsx | NEW — 3 short-form layouts (VN-vs-X, checklist, pull-quote) | Short-form scene dispatch |
scripts/twv-automation/yt-api/yt-retention.js | NEW — pulls YouTube retention curves per video | Future analytics loop (Phase 7) |
~/beup-workspace/assets/sfx/sfx-manifest.json + music-manifest.json | NEW — real Envato audio catalogs with rotation | assemble.py + twv-clip-bridge.mjs |
docs/decisions.md | Two dated entries logging the full roadmap + Phase 2/3 decisions | Project decision log |
- Run `node scripts/twv-automation/yt-api/yt-auth.js` once to re-consent (adds the YouTube Analytics scope) — then `yt-retention.js pull` starts collecting retention history.
- Download 4 Envato items to replace synthesized fallbacks (real riser/impact/ding SFX + a tension music track) per ~/beup-workspace/assets/sfx/ENVATO-DOWNLOAD-CHECKLIST.md, then run `node sync-sfx.mjs`.
- Open the Voicebox app and run one longform `run.py` to live-validate the new role-based voiceover pacing (it was offline this session, so voiceover changes were only validated by deterministic timing tests).