🔧 Comportas, scripts e determinismo
Agora os comandos reais por trás de cada fase. Você vai ver os scripts do motor — islands.py, cut.py, verify-cut.py, lint-timeline.py, captions.py, make-sfx.sh e mix-sfx.py — e a regra de determinismo que faz tudo isso ser confiável. Cada bloco é colável e vem com como verificar.
🎚️ silencedetect, não timestamps de Whisper
O corte não confia nos tempos que o Whisper devolve — eles têm um jitter de ±0,2–0,3s, o que deixa restos de silêncio e meias-palavras. Em vez disso, o motor mede o silêncio real do áudio com o silencedetect do ffmpeg. O script islands.py usa essa energia para achar as ilhotas de voz e propor quais manter, ficando com a última tomada de cada frase.
Objetivo
Detectar as ilhotas de voz do bruto por energia de áudio e gerar um islands.json com a proposta de KEEP/DROP — a base do corte, sem depender dos timestamps imprecisos da transcrição.
python3 islands.py \
--media \
--transcript word.json \
--noise -30dB --d 0.35 \
--out islands.json
Como verificar
O script imprime uma tabela legível: cada ilhota com seu tempo, o texto, e o KEEP/DROP proposto com o motivo. Leia a tabela; se alguma tomada dobrada ou tangente escapou da heurística, corrija o campo "keep" no islands.json antes de seguir para o cut.py.
Conceitos-chave: silencedetect, jitter, ilhota de voz, KEEP/DROP.
✂️ islands.py → cut.py → verify-cut.py
O corte é uma cadeia de três scripts com uma comporta dura no fim. islands.py propõe o que manter; cut.py cola as ilhotas com keep=true num único arquivo; e verify-cut.py confere que o resultado está limpo — sem silêncios longos no corpo nem repetições. Ele sai com exit 0 quando passa e exit 1 quando falha. Você não anima nada antes desse PASS.
verify-cut.py não der exit 0, o corte não avança para as animações.# 1) monta o corte a partir das ilhotas com keep=true
python3 cut.py --islands islands.json --out edicion/corte-final.mp4
# 2) re-transcreva o CORTE (word-level) e verifique — bloqueante
python3 verify-cut.py \
--media edicion/corte-final.mp4 \
--transcript corte-word.json \
--max-sil 0.6
echo "exit: $?" # 0 = PASSA · 1 = FALHA
Como verificar
Olhe o código de saída (echo "exit: $?"). 0 significa corte limpo — pode animar. 1 significa que ainda há silêncio longo no corpo ou uma repetição audível: o script imprime exatamente o que falhou. Conserte os keep e rode a cadeia de novo até dar 0.
Conceitos-chave: cadeia de scripts, comporta dura, exit 0/1.
📏 lint-timeline.py: gap > 4s = erro
A regra de ritmo "nunca mais de 4s sem golpe visual" deixa de ser um conselho e vira verificação automática. O lint-timeline.py lê o motion/index.html do Hyperframes, mede a distância entre os beats visuais e acusa erro quando encontra um buraco maior que o --max-gap (4s por padrão). É uma rede de segurança do ritmo — não substitui olhar os frames, mas pega o furo óbvio antes de renderizar.
Objetivo
Rodar um lint estático na timeline e falhar (exit ≠ 0) se houver qualquer trecho com mais de 4s sem um beat visual — bloqueando o render de um reel com buraco de ritmo.
# lint do ritmo: falha se houver >4s sem beat visual
python3 lint-timeline.py motion/index.html --max-gap 4.0
echo "exit: $?" # 0 = ritmo ok · ≠0 = buraco de ritmo
Como verificar
Se passar, exit 0 e silêncio. Se falhar, o lint aponta a linha e o intervalo do buraco ("gap de X s a partir de Ys"). Adicione um beat (corte, zoom, chip, B-roll ou reveal) naquele ponto do motion/index.html e rode de novo até zerar os erros.
Conceitos-chave: lint estático, beat visual, --max-gap, comporta de ritmo.
💬 captions.py: legenda na altura do micro
O captions.py gera "beats" de legenda de 2–3 palavras, sincronizados com a voz, no estilo retenção. O truque anti-clichê está no destaque: as palavras que você passa em --keywords (números, nomes de marca, conceitos fortes) são pintadas na sua cor de acento. Como as keywords mudam a cada vídeo, suas legendas nunca saem iguais às de ninguém.
Objetivo
Produzir um captions.json com beats curtos e uma palavra-chave destacada por beat, para legendas que apoiam a fala (na altura do peito/microfone) em vez de repeti-la embaixo da tela.
python3 captions.py \
--transcript corte-final.json \
--max-words 3 \
--keywords "" \
--out captions.json
Como verificar
Abra o captions.json: cada beat tem start, end e uma lista words com hi:true na palavra destacada. Confira que os tempos batem com a voz e que a palavra em destaque é a certa (se não houver match nas keywords, o script destaca a palavra mais longa do beat).
Conceitos-chave: beat de legenda, --keywords, cor de acento, área segura.
🔊 make-sfx.sh + mix-sfx.py: SFX sob a voz
O som é feito em dois passos. make-sfx.sh sintetiza a paleta de efeitos (whoosh, pop, type, buzz, boom, ding, riser) com o ffmpeg — sem baixar nada, livre de direitos e sempre igual. Depois, mix-sfx.py sobrepõe cada efeito no tempo certo por baixo da voz, com um limitador para não saturar. Você passa os eventos como uma lista de [nome, segundo].
Objetivo
Gerar a paleta de SFX localmente e misturá-la no render nos tempos dos cortes, entregando um final.mp4 com efeitos que "produzem" as transições sem competir com a fala.
# 1) sintetiza a paleta de SFX em ./sfx
bash make-sfx.sh sfx
# 2) mistura os efeitos sobre o render, por baixo da voz
python3 mix-sfx.py \
--base render.mp4 --sfx-dir sfx --out final.mp4 \
--events '[["boom",0.0],["whoosh",6.5],["ding",8.6]]'
Como verificar
Depois do make-sfx.sh, confira que a pasta sfx/ tem os .wav (whoosh, boom, ding…). Depois do mix-sfx.py, ouça o final.mp4: os efeitos devem cair exatamente nos cortes e ficar por baixo da voz. Se você re-cortar o vídeo, os tempos dos eventos mudam — re-temporize a lista --events.
Conceitos-chave: paleta de SFX, eventos [nome, t], ducking, limitador.
🎲 Determinismo em Hyperframes
Nada disso funciona se cada render sair diferente. Por isso as animações do Hyperframes são determinísticas: sem Math.random() e sem Date.now(), nada de repeat:-1 (repetições infinitas), e as timelines ficam em estado paused, registradas para o motor controlar frame a frame. Mesmo projeto → mesmo frame, toda vez — condição para o lint e o QC serem confiáveis.
✗ Quebra o determinismo
- ✗
Math.random()para posições/tempos - ✗
Date.now()/ relógio do sistema - ✗
repeat:-1(loop infinito)
✓ Mantém o determinismo
- ✓Valores fixos ou derivados da duração
- ✓Repetições finitas calculadas
- ✓
gsap.timeline({paused:true})registrada
Objetivo
Confirmar que o motor de motion (Hyperframes) está instalado e disponível — o pré-requisito para renderizar as timelines determinísticas.
# confere que o motor de motion está disponível
npx hyperframes --version
# checagem rápida: nenhuma fonte de aleatoriedade na timeline
grep -nE "Math\.random|Date\.now|repeat:\s*-1" motion/index.html || echo "OK: determinístico"
Como verificar
O --version deve imprimir um número (motor instalado). O grep deve imprimir OK: determinístico: se ele listar linhas, há uma fonte de aleatoriedade que precisa sair antes de renderizar. Rode o lint-timeline.py logo depois — os dois juntos são a garantia de um render reprodutível.
Conceitos-chave: determinismo, repeat finito, paused, reprodutibilidade.
✅ Resumo do módulo
Próxima trilha:
Trilha 3 — Como usar: instalar a stack, gerar a sua skill na entrevista e rodar o primeiro reel.