vault-sync: 2026-10-16 00:01 (jimmy)

This commit is contained in:
jimmy
2026-10-16 00:01:34 -07:00
parent eb54b93880
commit 5083eca6ae
13 changed files with 1472 additions and 0 deletions
@@ -0,0 +1,20 @@
---
tags: [video-stack, decisions]
updated: 2026-10-15
---
# Decisions
Back to [[Video Stack - Overview]]
| Date | Decision | Reason |
|---|---|---|
| 2026-10-15 | Business voice = **Gemini Kore** (B6), style "friendly, polished product explainer, conversational, upbeat but credible" | User picked B6 from B1-B10 ([[Test Scorecards]]) |
| 2026-10-15 | Political voice = **Gemini Charon** (P1), newscaster style ("measured, authoritative newscaster, confident, serious") | User picked P1; authoritative male fits political ads |
| 2026-10-15 | Local fallbacks: Chatterbox cloning the user's own recording (`C:\tts\voices\user_announcer_ref.wav`) and Qwen3-TTS | Free on the 3060; real human reference beats Kokoro refs |
| 2026-10-15 | **HyperFrames** default renderer, Remotion fallback | Rendered Prop 1 first try in 31 s; one plain HTML file per scene is easier for local models |
| 2026-10-15 | Music ~12 dB under the voice with ducking; final mix -16 LUFS | Voice clarity; consistent loudness |
| 2026-10-15 | User samples and approves voice and music (and script) before any build | Avoid wasted builds; user taste drives choices |
| 2026-10-15 | Political ads: serious civic style, no cheesy stings | User: "This is a political video. The music does not work." |
| 2026-10-15 | Only ONE llama-server on TonyB (Qwen, port 1922) | 12 GB 3060 VRAM; TTS must fit alongside |
| 2026-10-15 | Only tonyjbala Gitea credentials | User rule |
| 2026-10-15 | Each video gets a unique design | Avoid template sameness |
| 2026-10-15 | Political stack kept separate from business stack | Different rules (sourcing, disclaimers, tone) |
@@ -0,0 +1,287 @@
---
tags: [video-stack, spec]
updated: 2026-10-15
source: explainer-video-stack/STACK.md
---
# STACK.md — Everything Hermes needs to produce Grok-Bot-quality explainer videos
Audience: the Hermes **`explainer`** profile on **TonyB** (Windows 11, RTX 3060 12 GB), running a cheap model (e.g. `gpt-6-luna`).
This is the single source of truth. When another doc disagrees with this one, **this one wins**.
Repos: `tonyjbala/explainer-video-stack` (business explainers) and `tonyjbala/political-explainer-stack` (ballot/political ads).
Worked example to copy: `C:\video\examples\prop1ad` (HyperFrames, 45 s, 9:16).
Last updated: 2026-10-15.
---
## 1. Overview and the quality bar
A "Grok-Bot-quality" video is a **30–90 s explainer or ad (default 45 s, 9:16, 1080x1920)** that passes all six tests:
| # | Bar | What it means in practice |
|---|---|---|
| 1 | **Verified facts** | Every number and claim has an opened, primary source (LAO, official voter guide, SOS, agency data, product docs). Opinion is labelled or phrased as opinion. A short source line sits under every stat on screen. |
| 2 | **Natural VO** | Written for the ear (Section 5.1). No misreads (Whisper-checked), no odd pauses, no read-aloud stage directions. Political = authoritative male (Gemini **Charon**); business = firm, friendly female (Gemini **Kore**). |
| 3 | **Music that adds feel** | Timed to the beats: pulse/tension under the problem or cost, a swell under the CTA, resolves on the cut. A generic bed that "doesn't do anything" is a fail (Prop 1 lesson). |
| 4 | **Kinetic but clean design** | One idea per scene, huge numbers, staggered entrances, a palette made for the topic, nothing inside the platform UI safe zones, no dead half-screens. |
| 5 | **Perfect captions** | Burned-in, 3-word groups, active word highlighted, timed from Whisper word timestamps, spelled correctly ("$500M", "Prop 1", not "Prop.", not "$5"). |
| 6 | **Broadcast loudness** | Music ~12 dB under voice with sidechain ducking; final **-16 LUFS integrated, true peak ≤ -1.5 dBTP**, AAC 192k 48 kHz; last word ends ~2 s before the cut. |
Plus: three approval gates (script, voice, music) **before** any build, and nothing is ever posted or sent by you.
---
## 2. Hardware and software inventory (verified on TonyB 2026-10-15 unless marked)
| Item | Version / path | Notes |
|---|---|---|
| GPU | NVIDIA GeForce RTX 3060, **12288 MiB**, driver 610.88 | Qwen alone uses ~6.4 GB baseline (≈11.5 GB with KV in use). TTS models need 3.9–5.0 GB. |
| LLM | `C:\llama\llama-server.exe`, **Qwen3.8-35B-A3B-Q4_K_M on 127.0.0.1:1922** | The only model the video work assumes. Health: `http://127.0.0.1:1922/health`. |
| Node | **v24.18.0** (HyperFrames needs Node ≥ 22) | |
| HyperFrames | `npx --yes hyperframes@0.8.162 <cmd>` (pinned in `hf\ad\package.json`) | Default renderer. Set `$env:HYPERFRAMES_SKIP_SKILLS="1"` so npx doesn't install its bundled agent skills. Render sends anonymous telemetry: run `npx hyperframes telemetry disable` once. Never run `hyperframes publish` (it uploads). |
| Remotion | `C:\video\template` (16:9 `Main` + 9:16 `MainVertical`, 30 fps) | Fallback renderer only. Copy per project with `xcopy /E /I`. |
| ffmpeg | **9.0.1 essentials (gyan.dev)** on PATH | Mixing, loudness, muxing. |
| Chatterbox | venv **`C:\tts\chatterbox`** (uv, Python 3.11, torch 2.6 cu124) → `C:\tts\chatterbox\Scripts\python.exe`; driver `C:\tts\tts.py` | Local fallback voice; also runs Whisper check (`--check-only`). ~1x realtime, 3.9 GB. |
| Qwen3-TTS | venv **`C:\tts\qwen3tts`** (uv, torch 2.6 cu124, `qwen-tts`) → `C:\tts\qwen3tts\Scripts\python.exe`; 1.7B VoiceDesign + 1.7B Base | Second local option; voice-from-description. Slow (~2.5–5 min per 15–30 s), 4.3–5.0 GB, no flash-attn on Windows. Runner: `C:\tts\vt\vt.py` (env `VREF`/`VTAG`; engines `cb|qwen|qclone`). |
| Whisper | **faster-whisper `small.en`, int8, CPU** | Built into `tts.py`; produces `words.json` `{"1": [[word,start,end],...]}`. |
| Kokoro | kokoro-onnx (box/legacy) | Fast fallback voice for drafts only. **Never use a Kokoro clip as a clone reference** (refs in `C:\tts\voices\kokoro_*_ref.wav` are legacy; don't use them). |
| Human voice ref | `C:\tts\voices\user_announcer_ref.wav` (Anthony, recorded with consent; 20.65 s mono 24 kHz) | The only approved clone reference. |
| Gemini TTS | `C:\tts\gemini_tts.py` (stdlib only; repo copy `scripts/tts/gemini_tts.py`) | **Default voice engine.** Section 2.1. |
| Music (code) | `scripts/music/gen.py` (options A–C), `gen2.py` (D–F) in explainer-video-stack; `compose.py` presets in `C:\video\template` | numpy synth, royalty-free. Writes full track + 20 s preview. |
| Music (paid, optional) | `C:\video\template\el_music.py "<prompt>" <seconds> out.mp3` | ElevenLabs Music, needs `ELEVENLABS_API_KEY`; instrumental only. Use if two synth rounds miss. |
### 2.1 Gemini TTS (`gemini_tts.py`)
```
python C:\tts\gemini_tts.py script.json out.wav [--voice Charon] [--style "..."] [--gap 0.2] [--each DIR] [--whole] [--model gemini-3.8-flash-tts] [--fallback-model gemini-3.8-flash-lite-tts]
```
- `script.json` = JSON list of lines (or a .txt, one sentence per line). Default: one call per sentence, joined with 200 ms gaps. `--each DIR` also saves `DIR\s01.wav…`.
- `--whole` = **one API call for the whole script** (use this by default; it saves quota).
- On a 429 quota error it retries automatically on `gemini-3.8-flash-lite-tts` (`--fallback-model ""` disables).
- **Presets (user-approved Oct 2026):**
- Political (sample P1): `--voice Charon` (default style "measured, authoritative newscaster, confident, serious").
- Business (sample B6): `--voice Kore --style "friendly, polished product explainer, conversational, upbeat but credible" --whole`.
- **Quota (free tier):** flash-tts = **10 requests/day**; flash-lite-tts = 3 requests/minute. A per-sentence political ad can eat a whole day. Paid tier costs about $0.01–0.03/min.
- **Key:** `GEMINI_API_KEY` from the environment. Put `GEMINI_API_KEY=...` in `C:\Users\fight\AppData\Local\hermes\profiles\explainer\.env` (or `setx GEMINI_API_KEY "..."`). Never print it, echo it, or commit it.
- Output: 24 kHz mono 16-bit WAV.
---
## 3. The end-to-end pipeline (with gates)
```
Brief → Research/fact-check → Script (2 variants) → [GATE 1 script]
→ Voice sample → [GATE 2 voice] → Music previews x3 → [GATE 3 music]
→ Full VO + Whisper check → Scene design → HyperFrames build + lint + stills
→ Mix → Final QA → Deliver (9:16 mp4, plus 16:9 re-layout if asked) → [GATE 4 final approval]
```
Project folder: `C:\video\<project>\` with `brief.md`, `vo_review.md`, `script.json`, `vo\`, `music\`, `hf\ad\`, `out\`, `snaps\`.
### Step 1 — Brief → `brief.md`
Topic, audience, length (default 45 s), aspect (9:16 first), sponsor / "Paid for by" name, stance (political), CTA, tone, deadline.
Political: use `political-explainer-stack\templates\brief.md`.
**Done when:** every field filled or explicitly "ask user".
### Step 2 — Research and fact-check → `vo_review.md`
- Open every source you cite. Prefer primary: LAO ballot analysis, CA SOS Quick Guide / Official Voter Guide, UC Berkeley IGS, agency data, the product's own docs.
- Table columns: beat | original line | suggested accurate line | status (✅/⚠️) + why | source URL. Then a "Sources" list.
- Watch for the Prop 1 traps: a figure that applies to only part of the measure ($500–600M/yr is for the $10B part only), "inflation-adjusted" qualifiers, endorsements you cannot verify (EDC GOP's own slate was not online; attribute only to a guide you actually have).
- Label opinion lines ("doesn't change the rules that limit supply").
**Done when:** every number has a URL, every ⚠️ has a fix.
### Step 3 — Script → `script.json` (2 variants)
Rules in Section 5.1. Structure (political, 5 beats): **hook (0–7 s) → breakdown → cost/stakes → concerns/proof → vote call**. Business: hook → problem → how it works → proof/benefit → CTA.
~**105 words max for 45 s** (≤150 wpm, ~2 s spare). One JSON string per beat/scene. Numbers spelled as spoken.
**GATE 1:** send both variants (plain text, with word counts and estimated seconds). Stop until the user picks or edits.
### Step 4 — Voice sample → **GATE 2**
Render line 1 (or the first ~15 s) with the preset voice (Section 4.4). If the user wants options, render 2–3 voices of the same line.
**Done when:** the user approves one voice by name.
### Step 5 — Music previews → **GATE 3**
Write a timed music brief first (e.g. "0–6 s sparse tension; 15–30 s driving low pulse under the cost; 38–45 s resolute swell, resolve on the cut"). Make **3 distinct options**, 20 s previews of the most important section, mp3. One-line description each.
**Done when:** the user picks one (or asks for changes; iterate).
### Step 6 — Full VO + Whisper check → `vo\v1..vN.wav`, `vo\words.json`
Render the whole script with the approved voice, one file per line (or `--whole` then Whisper). Check transcript vs script; re-roll any misread or awkward line.
**Done when:** every line matches (digit-vs-word differences are fine), no pause > ~0.6 s inside a sentence, total VO ≤ length − 2 s.
### Step 7 — Scene design
Pick palette, type, motion for **this topic** (Section 5). Plan one scene per VO line, with start = line start − 0.1 s.
Safe zones 9:16: **top 250 px, bottom 430 px, right 90 px** clear. Content box x 80–910, y 380–1260. Captions at y≈1350.
### Step 8 — HyperFrames build
Scene-per-`<div>` pattern from `prop1ad\build.py`: one Python script writes `hf\ad\index.html` from `words.json` + scene HTML. Lint, snapshot stills at key timestamps, look at them, fix, then render silent.
**Done when:** lint clean, stills at hook / each scene / final 2 s look right, `out\silent.mp4` is exactly the target length.
### Step 9 — Mix
Exact ffmpeg in Section 4.9. **Done when:** I = -16 ±1 LUFS, TP ≤ -1.5 dBTP, voice clearly on top, music audible in gaps and swelling at the CTA.
### Step 10 — Final QA → deliver → **GATE 4**
Run the checklist (Section 4.10). Deliver `out\<project>_45s_vertical.mp4` (H.264 + AAC). If a 16:9 version is wanted, **re-lay out** the scenes for 1920x1080 (new positions, side-by-side panels); never just crop or pad the vertical. Send the path + report to the user; never post it.
---
## 4. Exact commands (PowerShell on TonyB)
### 4.0 Setup per project
```powershell
$P = "C:\video\myproj"
New-Item -ItemType Directory -Force $P,"$P\vo","$P\music","$P\out","$P\snaps" | Out-Null
Copy-Item -Recurse C:\video\examples\prop1ad\hf "$P\hf" # HyperFrames project skeleton
Copy-Item C:\video\examples\prop1ad\build.py "$P\build.py" # then edit scenes/palette
$env:HYPERFRAMES_SKIP_SKILLS = "1"
# Preflight: exactly one llama-server, on 1922
Get-CimInstance Win32_Process -Filter "Name='llama-server.exe'" | % { "$($_.ProcessId) $($_.CommandLine)" }
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
```
Done: one llama-server line (port 1922). If you see more, **report it to the user; don't stop anything yourself.**
### 4.1 Long jobs (anything > ~2 min: Qwen3-TTS, Chatterbox full runs, renders)
Remote-shell children die on disconnect, so use a one-time scheduled task:
```powershell
schtasks /create /tn OneTime_VO /tr "cmd /c C:\video\myproj\run_vo.cmd > C:\video\myproj\vo\log.txt 2>&1" /sc once /st 23:59 /it /f
schtasks /run /tn OneTime_VO
# poll: Get-Content C:\video\myproj\vo\log.txt -Tail 5 (until it prints DONE)
schtasks /delete /tn OneTime_VO /f
```
### 4.4 Voice
```powershell
cd $P
# Political sample (Gemini Charon), line 1 only:
python C:\tts\gemini_tts.py sample.json vo\sample.wav --voice Charon --whole
# Business sample (Gemini Kore):
python C:\tts\gemini_tts.py sample.json vo\sample.wav --voice Kore --style "friendly, polished product explainer, conversational, upbeat but credible" --whole
# Full VO, one file per line (political; costs N requests):
python C:\tts\gemini_tts.py script.json vo\voice.wav --voice Charon --each vo
# Local fallback (no key / quota gone): Chatterbox with the consented human ref, one line per call
C:\tts\chatterbox\Scripts\python.exe C:\tts\tts.py script.json vo --ref C:\tts\voices\user_announcer_ref.wav --ex 0.55 --cfg 0.4
# Re-roll one bad line:
C:\tts\chatterbox\Scripts\python.exe C:\tts\tts.py script.json vo --ref C:\tts\voices\user_announcer_ref.wav --ex 0.55 --cfg 0.4 --only 3 --seed 22
```
Whisper check / words.json (works on any WAVs named `v1.wav..vN.wav`; rename Gemini's `s01.wav` → `v1.wav` first):
```powershell
Get-ChildItem vo\s*.wav | % { $n=[int]($_.BaseName.Substring(1)); Copy-Item $_.FullName "vo\v$n.wav" }
C:\tts\chatterbox\Scripts\python.exe C:\tts\tts.py script.json vo --check-only
```
Done: every HEARD line matches WANT (digits vs words OK). Join lines with 200 ms silence for `vo\voice.wav` if you rendered per line (gemini_tts.py does this already).
### 4.5 Music previews
```powershell
# code-synth options (edit the arc/section times to the brief first)
cd C:\video\myproj\music
python <explainer-video-stack>\scripts\music\gen.py # A–C
python <explainer-video-stack>\scripts\music\gen2.py # D–F
# or presets: $env:MUSIC_SECONDS="47"; C:\tts\chatterbox\Scripts\python.exe C:\video\template\compose.py civic_tension cinematic_build
# or ElevenLabs (if key set): C:\tts\chatterbox\Scripts\python.exe C:\video\template\el_music.py "dark low strings, slow heartbeat pulse, builds to resolute swell at 38s" 47 optA.mp3
ffmpeg -y -i optionA_full.wav -ss 25 -t 20 -af "afade=t=in:d=0.5,afade=t=out:st=19:d=1" -b:a 160k optionA_preview.mp3
```
Music length = video length + ~2 s.
### 4.8 Build, lint, stills, render (HyperFrames)
```powershell
cd $P
python build.py # writes hf\ad\index.html
cd hf\ad
npx --yes hyperframes@0.8.162 lint
npx --yes hyperframes@0.8.162 snapshot --at 1,5,12,24,35,43 -o ..\..\snaps
# LOOK at every PNG in snaps\ (Read tool). Fix, rebuild, re-snapshot only what changed.
npx --yes hyperframes@0.8.162 render -o ..\..\out\silent.mp4 -q delivery
ffprobe -v error -show_entries format=duration:stream=width,height -of csv=p=0 ..\..\out\silent.mp4
```
Done: lint 0 errors; stills OK; 1080x1920; duration = target (e.g. 45.0).
### 4.9 Mix (exact command; same as `scripts/mix.sh`)
```powershell
cd $P
ffmpeg -v error -y -i vo\voice.wav -i music\approved.wav -filter_complex "[1:a]volume=-12dB[m];[0:a]asplit=2[v][sc];[m][sc]sidechaincompress=threshold=0.05:ratio=6:attack=20:release=300[md];[v][md]amix=inputs=2:duration=longest:normalize=0,loudnorm=I=-16:TP=-1.5:LRA=11[a]" -map "[a]" -ar 48000 out\mix.wav
ffmpeg -v error -y -i out\silent.mp4 -i out\mix.wav -map 0:v -map 1:a -c:v copy -c:a aac -b:a 192k -ar 48000 -shortest out\myproj_45s_vertical.mp4
ffmpeg -hide_banner -i out\myproj_45s_vertical.mp4 -af ebur128=peak=true -f null - 2>&1 | Select-String "I:|LRA:|Peak:" | Select-Object -Last 3
```
If the VO doesn't start at 0 s, delay it first: `-af "adelay=200|200"` on the voice or build `voice.wav` with the exact line offsets used in `build.py` (`ST` list).
Done: I -16 ±1, Peak ≤ -1.5, listen once end to end.
### 4.10 Final QA checklist
- [ ] Every fact sourced; source line on screen; "Paid for by …" + "Demo, not official" if political/demo.
- [ ] Gates 1–3 approved (script, voice, music).
- [ ] Whisper matches script; listened end to end; no awkward pauses.
- [ ] Captions match audio word for word, 3-word groups, active word highlighted, inside safe zones.
- [ ] Exact duration; last word ends ≥ ~2 s before the cut; music resolves on the cut.
- [ ] -16 LUFS ±1, TP ≤ -1.5 dBTP; music audible but never masking VO.
- [ ] H.264 + AAC, 1080x1920, file name ends in `.mp4`, plays on a phone.
- [ ] Exactly one llama-server (1922) still running; no leftover scheduled tasks.
- [ ] Score the run in `scorecard.md` (copy, voice, music, visuals, speed, cost; 1–5).
---
## 5. Design system
### 5.1 Copywriting (write for the ear)
1. Sentences ≤ 12 words. One idea per line; one line = one breath.
2. Numbers as spoken: "ten billion dollars", "twenty twenty-six", "Prop One". Never two numbers in one sentence if avoidable (Prop 1 line 3 had four → awkward).
3. No comma chains or read-aloud lists; split them.
4. Contractions (it's, don't, you'll). Lead with the stake in the first 2 s.
5. 1 s breath before the CTA. ≤150 wpm; ~105 words per 45 s.
6. Two variants every time. Read aloud (TTS) and time against words.json before designing.
### 5.2 Palette, type, motion — chosen per topic
- Start from the topic's emotion: civic/serious (navy, red, cream), local/warm (greens, sand), tech/fintech (deep ink + one electric accent). One dark base, one light text, **one accent**, one highlight color for the active caption word. Check contrast (white on base ≥ 7:1).
- Type: one heavy sans (`"Liberation Sans", Arial` weight 800–900 for numbers/headlines, 700 for subs). Sizes for 1080x1920: kicker 30 px letter-spaced caps; sub 46 px; hero numbers **128–280 px**; notes/sources 26–28 px (≥26 px).
- Motion (GSAP): each scene has `a1/a2/a3` elements entering at **+0.2 / +0.7 / +1.2 s**, `opacity 0→1, y 40→0, 0.5 s, power2.out`. No bounces or spins for political. Business can add a count-up or slide, still short (≤0.6 s).
- Never reuse Prop 1's look unchanged for another topic.
### 5.3 Captions
Built from Whisper `words.json`: group **3 words**, show from first word start to min(next group start, last word end + 0.4 s); active word tweens to the highlight color (`#FFD34D` in Prop 1) over 0.08 s at its timestamp. 60 px, weight 800, centered, `text-shadow:0 4px 12px #000`, box x 80–910 at y 1350. Clean tokens: "Prop." → "Prop", fix currency splits ("$5"+"00" → "$500"), attach "%"/"." to the previous token.
### 5.4 Kinetic numbers and data callouts
One number per beat, hero size; proportional bars (`$10B` 820 px vs `$1.25B` 103 px); `stat` blocks with a 10 px accent rule; "✕" concern cards (max 3); a source note under each.
### 5.5 End cards
Held ≥ 5 s. Political: big **NO/YES** block in the accent color, "ON PROP N", sponsor, close line, bordered box "Paid for by <exact committee name> · Demo, not official / Source: …". Business: logo/name, one-line value, CTA (URL), no clutter.
### 5.6 Political vs business
| | Political | Business |
|---|---|---|
| Voice | Gemini **Charon**, authoritative male | Gemini **Kore**, firm friendly female |
| Music | dark/urgent or resolute; pulse under cost, swell under vote call | warm/tech pulse, upbeat but not cheesy |
| Look | civic navy/red/cream, flag stripe, no playful motion | topic-fitted brand palette, livelier motion OK |
| Compliance | "Paid for by", demo label, FPPC check before any real use | product claims sourced |
| Repo | political-explainer-stack | explainer-video-stack |
---
## 6. Hard rules
1. **One llama-server only: Qwen on 1922.** Never start Gemma (1923) or any second model alongside. Don't stop or start models without asking the user. (Don't run `C:\llama\restore_llama_servers.bat` — the TonyB copy still starts Gemma too.)
2. **Long runs via one-time `schtasks`**, then delete the task.
3. **Never post, send, publish, upload or share** without explicit approval. No `hyperframes publish`.
4. **Gitea: tonyjbala account only.** Never use Ty / Ty_Tech credentials.
5. **No Kokoro-made clone references.** Clone only from `C:\tts\voices\user_announcer_ref.wav` (consented) or documented public-domain audio. Never clone an identifiable person without written consent.
6. **Keep tokens low:** reuse templates, read files only as needed, stills instead of test renders, regenerate only the broken line.
7. Never print or commit keys (`GEMINI_API_KEY`, `ELEVENLABS_API_KEY`); no media files (mp4/wav/mp3) in git.
---
## 7. Known pitfalls and fixes
| Pitfall | Fix |
|---|---|
| **Blank HyperFrames scenes** — scenes render empty because the GSAP selector doesn't match (e.g. targeting `.a1` without the scene id, or an id that doesn't exist), so elements stay at the CSS start state / are never animated in. | Every scene div needs a unique `id="sN"` + `class="scene clip"` + `data-start`/`data-duration`; animate with `"#sN .a1"`. Register `window.__timelines["main"]=tl` and `tl.set({},{},<duration>)`. Run `lint`, then `snapshot` mid-scene to confirm content is visible. |
| **Gemini reads directions aloud** ("Say in a calm voice: …" gets spoken) | Text = only words to be spoken. Delivery goes in `--style` (sent as `speech_metadata.style`). systemInstruction is rejected. |
| **Gemini quota (429)** — 10/day on flash-tts | Use `--whole`; script auto-falls back to Flash-Lite (3/min). The daily quota resets once a day (observed around 4:50 PM PT). Fallback: Chatterbox with human ref. |
| **Whisper number formatting** ("350,000" vs "three hundred fifty thousand") | Not a misread. Compare normalized; for captions show digits (they read better on screen). |
| **Awkward VO** (odd pauses, flat delivery) | Shorter sentences, fewer numbers per line; one sentence per call; re-roll seed; never a Kokoro reference. |
| **Tight ending** (slower voice runs into the cut) | Budget ~2 s spare; trim words, or speed VO ≤3% (`atempo=1.03`); never cut the last word. |
| **Windows mp4 extension** — Explorer hides extensions, so a file can end up `name.mp4.mp4` or without `.mp4` and won't play on the phone | Always pass an explicit `-o ...\name.mp4`; verify with `Get-ChildItem out` (shows full names) and `ffprobe`. |
| HyperFrames npx installs skills / telemetry | `$env:HYPERFRAMES_SKIP_SKILLS="1"`; `npx hyperframes telemetry disable`. |
| Remote shell jobs die on disconnect | schtasks (Rule 2). |
---
## 8. How a cheap model should work
1. **One small step at a time.** Do one numbered step from Section 3, write its output to a file, then verify it (read the file back, run the check command) before the next.
2. **Use the "Done when" lines and the QA checklist literally.** If a check fails, fix only that thing.
3. **Copy, don't invent.** Start from `prop1ad\build.py`, the templates, and the exact commands above; change values, not structure.
4. **Stop at every GATE** and ask the user in one short message with the files/paths. Don't build ahead of approvals.
5. **Small context.** Don't paste whole files or logs; use `-Tail`, `Select-String`, and stills.
6. **When unsure, ask.** If a source can't be opened, a tool errors twice, or VRAM is short: report it and ask; don't improvise around a rule.
7. **Report at the end:** mp4 path, duration, LUFS/TP, voice + settings, final script, sources, claims softened, scorecard.
---
## Appendix — file map
- `C:\tts\` — `gemini_tts.py`, `tts.py` (Chatterbox + Whisper), `gen3060.py`, `chatterbox\`, `qwen3tts\`, `vt\vt.py`, `voices\user_announcer_ref.wav`
- `C:\video\` — `examples\prop1ad`, `template` (Remotion), `voice_tests` (B1–B10, P1–P4, NOTES.md), `COPYWRITING.md`, `HERMES_PROMPT.md`, `STACK.md` (this file)
- Hermes profile — `C:\Users\fight\AppData\Local\hermes\profiles\explainer\` (`SOUL.md`, `STACK.md`, `.env`, `skills\media\explainer-video\SKILL.md`)
- Obsidian — `C:\Users\fight\Documents\Obsidian Vault\Video Stack\Full Stack Spec.md`
Back to [[Video Stack - Overview]]
@@ -0,0 +1,15 @@
---
tags: [video-stack, lessons]
updated: 2026-10-15
---
# Lessons Learned
Back to [[Video Stack - Overview]]
- A Kokoro-made reference makes Chatterbox clones flat with odd pauses. Use a real human reference (the user's recording worked well; leave more headroom, it peaked at -0.1 dBFS).
- Generate one sentence at a time (best of several seeds, ~200 ms gaps) to avoid strange pauses.
- Copywriting: short spoken sentences, numbers written as spoken words, ~105 words per 45 s, leave ~2 s spare. Number-heavy long lines made Prop 1 sound awkward.
- Gemini reads directions aloud unless they go in the style field.
- Music must add feel: civic_tension and piano B were rejected; option A (dark low strings, slow heartbeat pulse) chosen for Prop 1. If code-composed music misses twice, use a richer generator.
- ElevenLabs free tier was blocked for TTS ("unusual activity"); not retried.
- HyperFrames is easier for local models than Remotion (plain HTML per scene).
- Pick an authoritative male voice for political ads.
@@ -0,0 +1,10 @@
---
tags: [video-stack, project]
updated: 2026-10-15
---
# GeoLocal Explainer
Back to [[Video Stack - Overview]]
Status: final, landscape (16:9) + vertical (9:16).
- Music: option 6 "arc" (ElevenLabs Music), ducked, final -16.1 LUFS.
- Remotion composition, 6 scenes. Example in explainer-video-stack `examples/geolocal`.
@@ -0,0 +1,8 @@
---
tags: [video-stack, project, stub]
updated: 2026-10-15
---
# MiArc
Back to [[Video Stack - Overview]]
Example subject for business explainers. Repo: https://gitea.vps1.afterthedemo.com/Ty_Tech/MiArc. Details TBD.
@@ -0,0 +1,8 @@
---
tags: [video-stack, project, stub]
updated: 2026-10-15
---
# Pocket Change
Back to [[Video Stack - Overview]]
Example subject for business explainers. Details TBD.
@@ -0,0 +1,12 @@
---
tags: [video-stack, project, political]
updated: 2026-10-15
---
# Prop 1 Ad
Back to [[Video Stack - Overview]]
Status: v2, 45 s vertical, HyperFrames. EDC GOP "Vote No on Prop 1".
- Voice: male am_michael-based voice; music A (dark low strings, heartbeat pulse).
- Feedback: visuals good; voice awkward with the script; copy needs refinement.
- Next: re-voice with Gemini Charon and tighter copy (~105 words).
- Example: `C:\video\examples\prop1ad`.
@@ -0,0 +1,11 @@
---
tags: [video-stack, project, political]
updated: 2026-10-15
---
# Prop 38 Ad
Back to [[Video Stack - Overview]]
Status: v2 + vertical, EDC GOP demo (not official).
- Voice: Chatterbox with Kokoro am_michael ref (speed 1.05).
- Music: ElevenLabs Music, 45 s; ~11.6 dB under voice; final -16.1 LUFS, 45.1 s.
- Example: `examples/prop38ad` in both repos.
@@ -0,0 +1,8 @@
---
tags: [video-stack, project, political]
updated: 2026-10-15
---
# Prop 38 Explainer
Back to [[Video Stack - Overview]]
Status: done (earlier Voting Guide Series explainer). Brief kept as an example in the repos. Details TBD.
@@ -0,0 +1,35 @@
---
tags: [video-stack, scorecard]
updated: 2026-10-15
---
# Test Scorecards
Back to [[Video Stack - Overview]] - details in `C:\video\voice_tests` / NOTES.md. All clips ~-16 LUFS, Whisper-checked with no misreads.
## Political (Prop 1 opening) - pick: **P1**
| ID | Engine / voice | Gen time |
|---|---|---|
| **P1** | Gemini flash-tts, Charon, newscaster style | 6.4 s |
| P2 | Qwen3-TTS VoiceDesign, "deep, authoritative American male political ad narrator" | 145 s |
| P3 | Chatterbox, user ref | 86 s |
| P4 | Qwen3-TTS clone, user ref | 161 s |
## Business (GeoLocal paragraph) - pick: **B6**
| ID | Engine / voice | Gen time |
|---|---|---|
| B1 | Chatterbox, user ref | 130 s |
| B2 | Qwen3-TTS VoiceDesign, warm American male narrator | 229 s |
| B3 | Kokoro am_michael | ~7 s (CPU) |
| B4 | Qwen3-TTS clone, user ref | 304 s |
| B5 | Gemini Flash-Lite, Achird (male) | API |
| **B6** | Gemini Flash-Lite, Kore (female, "Firm") | API |
| B7 | Gemini Flash-Lite, Algieba (male) | API |
| B8 | Gemini Flash-Lite, Zephyr (female) | API |
| B9 | Qwen3-TTS VoiceDesign, young female tech narrator | 280 s |
| B10 | Qwen3-TTS VoiceDesign, relaxed male podcast host | 266 s |
## Stack comparison template (score 1-5)
| Date | Video | Producer | Copy | Voice | Music | Visuals | Speed | Cost | Notes |
|---|---|---|---|---|---|---|---|---|---|
| | | Grok Bot | | | | | | | |
| | | Hermes explainer | | | | | | | |
| | | Regis | | | | | | | |
@@ -0,0 +1,29 @@
---
tags: [video-stack, setup]
updated: 2026-10-15
---
# Tools and Setup
Back to [[Video Stack - Overview]]
## Paths on TonyB
- `C:\tts\tts.py` - Chatterbox voice script (`--ref <wav>` for cloning)
- `C:\tts\gemini_tts.py` - Gemini TTS; `--style` for directions, `--whole` (whole script in one request), `--fallback-model` (switch to Lite when the daily limit is hit)
- `C:\tts\qwen3tts` - Qwen3-TTS venv (1.7B VoiceDesign / Base clone)
- `C:\tts\chatterbox` - Chatterbox venv
- `C:\tts\voices\user_announcer_ref.wav` - user's own announcer recording (20.65 s, mono 24 kHz)
- `C:\tts\vt\vt.py` - voice-test runner
- `C:\video\template` - video template
- `C:\video\HERMES_PROMPT.md`, `C:\video\COPYWRITING.md`
- `C:\video\examples\prop1ad` - worked example
- `C:\video\voice_tests` - bake-off samples
- Hermes profile: `C:\Users\fight\AppData\Local\hermes\profiles\explainer` - start with `explainer chat`
## GPU (RTX 3060, 12 GB)
- Qwen llama-server on :1922 uses ~6.4 GB baseline. Never start Gemma (:1923) alongside it.
- Peak VRAM seen: Chatterbox 3.9 GB, Qwen3-TTS VoiceDesign ~4.3-4.5 GB, Qwen3-TTS clone 5.0 GB. Run TTS models one at a time.
- Long runs: one-time scheduled tasks (`schtasks /create ... /sc once`, `/run`), delete afterwards.
## Gemini
- Free tier: 10 requests/day on flash-tts (resets ~4:50 PM PT); flash-lite-tts 3/min. Script auto-falls back to Lite.
- Style must go in `speech_metadata.style` (interactions endpoint), never in the text.
- `GEMINI_API_KEY` goes in the explainer profile `.env` (user step; not confirmed done).
@@ -0,0 +1,28 @@
---
tags: [video-stack, moc]
updated: 2026-10-15
---
# Video Stack - Overview
## Goals
- Polished explainer and ad videos, with 9:16 vertical shorts as the main format (16:9 masters when needed).
- Political **Voting Guide Series** (EDC GOP) kept separate from business explainers.
- Find the best stack per purpose by scored test runs (see [[Test Scorecards]]).
## Split workflow
- **Grok Bot** designs the workflow, templates, and prompts.
- The **Hermes "explainer" profile** on TonyB (`explainer chat`) does the production work on cheap inference (`gpt-6-luna`).
- Regis (Meta Muse agent) can be given the same brief for head-to-head comparisons.
## Stacks
- Business: [explainer-video-stack](https://gitea.vps1.afterthedemo.com/tonyjbala/explainer-video-stack) (GeoLocal, MiArc, Pocket Change type subjects)
- Political: [political-explainer-stack](https://gitea.vps1.afterthedemo.com/tonyjbala/political-explainer-stack) (templated brief/script, sourcing, "Paid for by" card)
- Older: [video-stack](https://gitea.vps1.afterthedemo.com/tonyjbala/video-stack) (first version, holds the original HERMES_PROMPT)
## Notes
- [[Full Stack Spec]] — the complete STACK.md (tools, pipeline, commands, rules)
- [[Decisions]]
- [[Tools and Setup]]
- [[Lessons Learned]]
- [[Test Scorecards]]
- Projects: [[GeoLocal Explainer]], [[Prop 38 Explainer]], [[Prop 38 Ad]], [[Prop 1 Ad]], [[MiArc]], [[Pocket Change]]
File diff suppressed because it is too large Load Diff