What does video-narrado do?
Entrada: un .md. Salida, en este orden:
- Video detallado 16:9 (2 a 4 min) que recorre el MD completo.
- Versiones cortas 9:16 (2 o 3, menos de 60 s): cada una toma un hallazgo del video detallado.
- Un
publicacion.mdpor pieza (título, descripción, hashtags y texto alternativo).
Todo con la voz seleccionada, subtítulos y música de fondo propia.
- Motor:
motor/, dentro de esta skill (Playwright 1.63.0 + ffmpeg; Python 3 sin dependencias).MOTOR= ruta absoluta de esa carpeta. - Proyecto: una carpeta por video, p. ej.
videos/AAAA-MM-DD_tema/, con una subcarpeta por pieza:largo/,short-1-…/,short-2-…/. Cada pieza lleva sustoryboard.json. - Instalación (una sola vez): ver
README.md.
Voz seleccionada
Por defecto, la voz clonada «audio_pmm» de HeyGen:
campo de create_speech | valor |
|---|---|
voiceId | b088805628184836963fa5d2881bb23b |
engine | elevenlabs |
settings | {"model_id": "eleven_v4"} |
language | es |
Si quien pide el video quiere otra voz, buscarla con mcp__heygen__list_voices y confirmarla antes de generar.
Flujo
1. Leer el MD y proponer las piezas
Leer el MD completo. Definir el recorrido del video detallado (todos los hallazgos, en orden) y un hallazgo por versión corta (el más fuerte primero). Mostrar la lista de piezas antes de escribir los storyboards.
2. Storyboards → aprobación
Una escena por idea. El video detallado, de 8 a 16 escenas; una versión corta, de 4 a 7 escenas, con un gancho (destacado) en la primera. Ejemplo completo: motor/ejemplos/storyboard-desalacion-16x9.json.
{"id": "03-sesiones", "tipo": "barras", "kicker": "Sesiones por caso", "titulo": "Dónde se habló más",
"items": [{"etiqueta": "Caleta Chañaral", "valor": 6}], "destacar": 0, "nota": "Fuente…",
"subtitulo": "Chañaral de Aceituno fue el caso con más sesiones.",
"voz": "Chañaral de Aceituno fue el caso con más sesiones."}
Encabezado del storyboard: titulo, serie (lo que va junto a «Rizoma ·» arriba), formato ("16:9" para el detallado, "9:16" para las versiones cortas), fuente_md y, si se quiere, musica (ver paso 5).
Tipos de escena (todos aceptan kicker, tono: "oscuro" y lluvia: true):
| tipo | campos | para qué |
|---|---|---|
portada | titulo, bajada | abrir el video detallado |
cifras | titulo, items[{valor, prefijo?, sufijo?, texto, detalle?}] (1 a 3), destacar | números que cuentan hacia arriba |
barras | titulo, items[{etiqueta, valor, rotulo?}] (hasta 8), destacar, nota | comparar cantidades |
hito | fecha, etiqueta, titulo, texto, progreso{i, n} | un hecho de una cronología |
lista | titulo, items[{marca?, texto, alerta?}] (hasta 6) | argumentos o pasos |
cita | fecha, texto, quien, nota | lo que alguien dijo |
destacado | texto, bajada | la frase que hay que recordar; el gancho de una versión corta |
cierre | bajada, url, fuente | marca y fuente |
Reglas editoriales:
- Solo hechos del MD. Lo que el MD marca como dudoso queda fuera o se dice como tal.
vozes lo que se lee: números, años, porcentajes y siglas van en palabras (texto_voz.pyrechaza dígitos).subtituloes lo que se ve, con cifras.textoes el texto en pantalla dehito,citaydestacado.- Frases cortas, español de Chile neutro, ~2,9 palabras por segundo. Una versión corta no pasa de ~140 palabras de voz.
- Lo tomado de una transcripción automática se marca como paráfrasis (
nota). - Lo que se publica no nombra clientes ni plataformas internas. El cierre lleva la fuente.
Mostrar cada storyboard como tabla (escena · tipo · lo que se ve · lo que se narra) y esperar el visto bueno: cada narración gasta cuota de voz.
3. Voz (una llamada por pieza)
python3 $MOTOR/texto_voz.py <pieza>
Pasar el texto que imprime (la voz de cada escena unida con <break time="1.2s"/>) a mcp__heygen__create_speech con la voz seleccionada. Con el audio_url que devuelve:
python3 $MOTOR/bajar_voz.py <pieza> "<audio_url>"
Descarga el audio y lo corta por escena buscando las pausas. Si avisa que faltan pausas, re-correr sobre el audio ya bajado con un umbral menor: python3 $MOTOR/bajar_voz.py <pieza> <pieza>/voz/narracion.wav --pausa 0.7 (en la práctica ElevenLabs acorta un poco alguna pausa). Si aun así falla, la voz no respetó un <break>: preguntar antes de regenerar. Nunca reintentar la generación sola tras un error.
4. Construir y renderizar
bash $MOTOR/producir.sh <pieza> # video completo
bash $MOTOR/producir.sh <pieza> --hasta 15 --salida prueba.mp4 # prueba corta
Cada escena dura lo que dura su voz. El MP4 queda en <pieza>/salida/video.mp4; tarda más o menos lo que dura el video.
5. Música de fondo
Automática: colchón ambiental sintetizado con ffmpeg (sin derechos de terceros), 14 dB bajo la voz, con ducking suave y mezcla final a -16 LUFS. En el storyboard: "musica": {"bajo_voz_db": 18} la baja; {"archivo": "pista.mp3"} usa una pista propia (solo de uso libre, con la licencia anotada); false la apaga.
6. Versiones cortas
Después de aprobar el video detallado, armar cada versión corta como pieza aparte (formato: "9:16"), reutilizando escenas del detallado: gancho → 2 a 4 escenas de dato → cierre. Mismo paso 3 (una llamada de voz por pieza) y paso 4.
7. Revisión y entrega
- Sacar 4 a 8 cuadros con ffmpeg y mirarlos: que el subtítulo no tape un dato, que ningún texto se salga del cuadro y que ninguna escena quede en blanco.
- Escribir
publicacion.mdpor pieza: título (≤ 60 caracteres), descripción con la fuente, 3 a 5 hashtags, texto alternativo y lo que falta verificar antes de publicar. - Copiar los MP4 a
~/Downloadscon nombres claros (Video_<tema>.mp4,Short_<n>_<tema>.mp4). El audio y los MP4 no van a git (**/voz/*.wav,**/salida/).
Desde un HTML animado propio
Si ya existe un HTML con window.seek(t) y escenas con tiempos fijos, se escribe guion.json a mano (html: [a, b] por segmento) y se corre preparar.py + render.mjs. Para versiones cortas hechas a mano: motor/plantillas/motor-vertical.js.
Formato y zonas seguras
- 16:9: 1920×1080, subtítulo de 34 px abajo, frases de hasta 58 caracteres.
- 9:16: 1080×1920, subtítulo de 54 px a 400 px del borde inferior, frases de hasta 34 caracteres; contenido entre y=400 e y=1380 (arriba y abajo van los botones de cada red).
- Ningún subtítulo termina en «que», «las», «por» y similares.
Errores conocidos
- Sin red: las fuentes de Google no cargan y el render cae a Arial.
- ffmpeg sin libass (Homebrew): por eso los subtítulos se dibujan dentro de la página.
- Tipo de escena desconocido: el video sale en blanco; revisar
tipo.