🔧 Compuertas, scripts y determinismo
Ahora verás los comandos reales detrás de cada fase. Verás los scripts del motor — islands.py, cut.py, verify-cut.py, lint-timeline.py, captions.py, make-sfx.sh e mix-sfx.py — y la regla de determinismo que hace que todo esto sea confiable. Cada bloque se puede pegar e incluye cómo verificarlo.
🎚️ silencedetect, no timestamps de Whisper
El corte no confía en los tiempos que devuelve Whisper — tienen un jitter de ±0,2–0,3s, lo que deja restos de silencio y palabras a medias. En su lugar, el motor mide el silencio real del audio con el silencedetect de ffmpeg. El script islands.py usa esa energía para encontrar los fragmentos de voz y proponer cuáles conservar, quedándote con la última toma de cada frase.
Objetivo
Detectar las islas de voz del material original mediante la energía del audio y generar un islands.json con la propuesta de KEEP/DROP — la base del corte, sin depender de los timestamps imprecisos de la transcripción.
python3 islands.py \
--media \
--transcript word.json \
--noise -30dB --d 0.35 \
--out islands.json
Cómo verificar
El script imprime una tabla legible: cada islote con su tiempo, el texto y la propuesta KEEP/DROP con el motivo. Lee la tabla; si se escapó de la heurística alguna toma repetida o tangencial, corrige el campo "keep" en el islands.json antes de pasar al cut.py.
Conceptos clave: silencedetect, jitter, isla de voz, KEEP/DROP.
✂️ islands.py → cut.py → verify-cut.py
El corte es una cadena de tres scripts con una comporta dura al final. islands.py propone qué conservar; cut.py une las islitas con keep=true en un solo archivo; y verify-cut.py comprueba que el resultado esté limpio — sin silencios largos en el cuerpo ni repeticiones. Sale con exit 0 cuando pasa y exit 1 cuando falla. No animas nada antes de ese PASS.
verify-cut.py si no devuelve exit 0, el corte no avanza a las animaciones.# 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
Cómo verificar
Mira el código de salida (echo "exit: $?"). 0 significa corte limpio — puedes animar. 1 significa que todavía hay un silencio largo en el cuerpo o una repetición audible: el script imprime exactamente lo que falló. Corrige los keep y ejecuta la cadena de nuevo hasta llegar a 0.
Conceptos clave: cadena de scripts, compuerta estricta, exit 0/1.
📏 lint-timeline.py: gap > 4s = error
La regla de ritmo «nunca más de 4s sin impacto visual» deja de ser un consejo y se convierte en verificación automática. O lint-timeline.py lee el motion/index.html de Hyperframes, mide la distancia entre los beats visuales y señala un error cuando encuentra un hueco mayor que el --max-gap (4s por defecto). Es una red de seguridad para el ritmo: no reemplaza revisar los fotogramas, pero detecta el fallo obvio antes de renderizar.
Objetivo
Ejecutar un lint estático en la línea de tiempo y fallar (exit ≠ 0) si hay algún tramo de más de 4s sin un beat visual, bloqueando el render de un reel con un bache 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
Cómo verificar
Si pasa, exit 0 y silencio. Si falla, el lint señala la línea y el intervalo del hueco ("gap de X s a partir de Ys"). Agrega un beat (corte, zoom, chip, B-roll o reveal) en ese punto del motion/index.html y vuelve a ejecutarlo hasta eliminar los errores.
Conceptos clave: lint estático, beat visual, --max-gap, compuerta de ritmo.
💬 captions.py: subtítulo a la altura del micrófono
O captions.py genera «beats» de subtítulos de 2–3 palabras, sincronizados con la voz, con estilo de retención. El truco anticliché está en el resaltado: las palabras que pasas en --keywords (números, nombres de marca, conceptos clave) se pintan en la tu color de acento. Como las keywords cambian con cada video, tus subtítulos nunca salen iguales a los de nadie.
Objetivo
Producir uno captions.json con beats cortos y una palabra clave destacada por beat, para subtítulos que apoyen lo que dices (a la altura del pecho/micrófono) en vez de repetirlo en la parte inferior de la pantalla.
python3 captions.py \
--transcript corte-final.json \
--max-words 3 \
--keywords "" \
--out captions.json
Cómo verificar
Abre el captions.json: cada beat tiene start, end y una lista words con hi:true en la palabra destacada. Comprueba que los tiempos coincidan con la voz y que la palabra destacada sea la correcta (si no hay coincidencia en las keywords, el script destaca la palabra más larga del beat).
Conceptos clave: beat de subtítulos, --keywords, color de acento, área segura.
🔊 make-sfx.sh + mix-sfx.py: SFX bajo la voz
El sonido se crea en dos pasos. make-sfx.sh sintetiza la paleta de efectos (whoosh, pop, type, buzz, boom, ding, riser) con ffmpeg — sin descargar nada, libre de derechos y siempre igual. Después, mix-sfx.py superpone cada efecto en el momento justo por debajo de la voz, con un limitador para que no sature. Pasas los eventos como una lista de [nome, segundo].
Objetivo
Generar la paleta de SFX localmente y mezclarla en el render en los tiempos de los cortes, entregando un final.mp4 con efectos que «producen» las transiciones sin competir con lo que dices.
# 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]]'
Cómo verificar
Después del make-sfx.sh, comprueba que la carpeta sfx/ tiene los .wav (whoosh, boom, ding…). Después del mix-sfx.py, escucha el final.mp4: los efectos deben coincidir exactamente con los cortes y quedar por debajo de la voz. Si vuelves a recortar el video, los tiempos de los eventos cambian — retemporiza la lista --events.
Conceptos clave: paleta de SFX, eventos [nombre, t], ducking, limitador.
🎲 Determinismo en Hyperframes
Nada de esto funciona si cada render sale diferente. Por eso, las animaciones de Hyperframes son determinísticas: sin Math.random() y sin Date.now(), nada de repeat:-1 (repeticiones infinitas), y las líneas de tiempo quedan en estado paused, registradas para que el motor controle cada frame. Mismo proyecto → mismo frame, cada vez — condición para que el lint y el QC sean confiables.
✗ Rompe el determinismo
- ✗
Math.random()para posiciones/tiempos - ✗
Date.now()/ reloj del sistema - ✗
repeat:-1(bucle infinito)
✓ Mantiene el determinismo
- ✓Valores fijos o derivados de la duración
- ✓Repeticiones finitas calculadas
- ✓
gsap.timeline({paused:true})registrada
Objetivo
Confirmar que el motor de motion (Hyperframes) esté instalado y disponible: el requisito previo para renderizar las timelines deterministas.
# 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"
Cómo verificar
O --version debe imprimir un número (motor instalado). El grep debe imprimir OK: determinístico: si enumera líneas, hay una fuente de aleatoriedad que debe eliminarse antes de renderizar. Ejecuta el lint-timeline.py justo después: los dos juntos garantizan un render reproducible.
Conceptos clave: determinismo, repetición finita, pausado, reproducibilidad.
✅ Resumen del módulo
Siguiente ruta:
Ruta 3 — Cómo usarla: instalar el stack, generar tu skill en la entrevista y crear el primer reel.