Vertical Reels Video Studio (pro)
De extremo a extremo: (primera ejecución: pedir al usuario sus handles/marca de agua/CTA/voz) → tema → investigación → borrador → aprobación → locución → temporización → escenas en Remotion → renderizado → exportación para Reels → QA → portadas + subtítulos → entrega.
Esta es la versión más completa de vertical-reels-video-studio (básica). La configuración de marca vive en brand.config.json (leído por src/brand.ts y pasado a las composiciones como la propiedad brand).
0a. Primera ejecución: preguntar al usuario SUS configuraciones (nunca usar valores predeterminados)
Antes del primer video, si brand.config.json falta, tiene "configured": false o aún contiene @yourhandle,
pregunta al usuario (una pregunta a la vez) y guarda las respuestas en brand.config.json:
- Handles sociales: Instagram, Threads, TikTok, X (los que use).
- Texto de la marca de agua (exactamente como debe aparecer, ej.
@yourhandleo@ig / @x) y posición (bottom-center,bottom-left,top-left,top-right). - CTA texto y el handle que menciona.
- Voz: su ID de voz de ElevenLabs + modelo + idioma, o "Grabaré mi propia voz". La clave de API se solicita solo como el secreto
ELEVENLABS_API_KEY. - Idioma y tono; cómo la voz debe decir su nombre/handle.
Luego establece "configured": true. Nunca inventes un handle, nunca reutilices el de otra persona (incluido el autor de este kit) y nunca renderices una marca de agua que aún diga @yourhandle.
0b. Configuración de marca (brand.config.json, completada con las respuestas del usuario)
| Clave | Marcador de posición | Notas |
|---|---|---|
| Handles | {{IG_HANDLE}}, {{THREADS_HANDLE}}, {{TIKTOK_HANDLE}}, {{X_HANDLE}} | handles.*, los propios del usuario |
| Marca de agua | {{WATERMARK}} + posición | watermark.text / watermark.position, el texto exacto que dio el usuario; nunca lo hardcodees en las escenas |
| Idioma / tono | {{LANGUAGE}}, {{TONE}} | ej. indonesio informal gua/lu, emoji escasos |
| Nombre hablado | {{SPOKEN_NAME}} | Cómo la voz dice tu handle (ej. "Jane Doe" para @janedoe_). Los subtítulos aún muestran el handle real |
| Aviso legal | {{DISCLAIMER}} | Para temas financieros, ej. "Educación, no asesoría financiera" |
| Voz | {{VOICE_ID}}, {{TTS_MODEL}}, {{LANGUAGE_CODE}} | voice.*, la elección del usuario (o su propia grabación) |
| Clave de API | env ELEVENLABS_API_KEY | Nunca la imprimas, registres ni confirmes |
| CTA | {{CTA}}, {{CTA_HANDLE}} | cta.text / cta.handle, preguntados al usuario (ej. "Sigue a {{CTA_HANDLE}} para más", "Comenta TUTORIAL") |
| Reglas estrictas | {{HARD_RULES}} | Marcas que nunca burlarse, nombres a difuminar, palabras que nunca decir |
| Dispositivo de renderizado | {{RENDER_DEVICE}} | Máquina GPU o CPU. Siempre reporta cuál se usó realmente |
1. Investigación y borrador (sin audio, sin renderizado)
- Verifica cada afirmación con fuentes primarias: documentos oficiales/PRs, la propia publicación del autor, registros, capturas de pantalla fechadas. Las publicaciones de ejecutivos cuentan como declaraciones oficiales. Etiqueta estimaciones (
ESTIMATE · not official) y acusaciones (ALLEGED). Descarta lo que no puedas verificar en lugar de suavizarlo. - Una historia por video, palabras cotidianas, ritmo lento. Los nombres de productos difíciles van en pantalla, no en la locución.
- Ofrece 2–4 opciones de gancho (H1, H2, …): una pregunta de por qué/cómo o un contraste audaz. Deja que el usuario elija.
- Tabla temporizada:
time | on-screen | VO. Apunta a 45–65 s, máximo estricto 90 s (apunta ≤ 88 s para que una revisión aún quepa). - Escribe
factcheck.md(afirmación → fuente → estado) ysources.md. - Envía el borrador y espera la aprobación. "No renderices" del usuario es definitivo hasta que diga continuar.
2. Locución (ElevenLabs)
- Dos guiones:
script-display.txt(lo que muestran los subtítulos) yscript-tts.txt(deletreado para la voz). Mantén un mapa display↔TTS. - Pronunciación (ver
docs/VOICEOVER.md):A.I.con puntos para que suene en inglés; tickers letra por letra en VO no inglés (ETH →E, T, H); números como palabras; términos en inglés con ortografía inglesa simple (sin rescribir con guiones); una coma después de una /t/ final antes de una vocal; nombres de marcas rescribir solo cuando las pruebas demuestren que ayuda. - Postprocesa
atempo1.05 (1.1 para contenido rápido). Sonoridad −14 LUFS integrado, ≤ −1 dBTP. - Sin líneas duplicadas. Si una línea repite el gancho o un ritmo anterior, córtala o reescríbela.
- Primero verifica la cuota de caracteres. Repite la voz solo de las líneas que fallan (pasa
previous_textpara continuidad).
3. Temporización
- Marcas de tiempo de palabras desde la alineación de TTS (dividir por el factor de tempo) o faster-whisper →
timing.json{duration, phrases:[{s,e,words:[{w,s,e}]}], scenes:[{id,start}]}(scripts/build-timing.py). - Fusiona los tokens rescribir de vuelta a palabras de visualización (
A.I.→AI,E, T, H→ETH) o los subtítulos empiezan tarde. - Las escenas se basan en palabras habladas:
wt(scene, word)→ fotograma donde comienza esa palabra.
4. Estilo de movimiento (Remotion, 1080x1920, 30 fps)
Aspecto: fondo #0A1016, crema #EFE6D2, ámbar #FFB000, rojo #E2553F, verde #7BD88F. Fuentes vía @remotion/google-fonts: Big Shoulders 800 (titulares), Plus Jakarta Sans (cuerpo/subtítulos), JetBrains Mono (chips/código).
Diseño: contenido importante y 200–1500. Grupo inferior: subtítulos karaoke ~y 1310–1353, marca de agua (si el usuario eligió abajo) ~y 1427–1446, progreso ~y 1474, todo a la izquierda de x 960 (Reels dibuja sus botones a la derecha).
Sin fotogramas vacíos: el gancho es legible en el fotograma 0; la apertura y el final están tan completos como el medio (una imagen principal, el presentador, un chip), nunca una línea solitaria sobre un fondo en blanco.
Ayudantes de easing (src/motion/ease.ts):
const clamp01=(x:number)=>Math.max(0,Math.min(1,x));
const P=(t:number,a:number,b:number)=>clamp01((t-a)/(b-a));
const eOut=(x:number)=>1-Math.pow(1-x,3);
const eIn=(x:number)=>x*x*x;
const backOut=(x:number,s=1.70158)=>1+(s+1)*Math.pow(x-1,3)+s*Math.pow(x-1,2);
const sec=(f:number,at:number)=>(f-at)/30;
const ap=(f:number,at:number,d=0.3)=>eOut(P(sec(f,at),0,d));
const pop=(f:number,at:number)=>Math.max(0,backOut(P(sec(f,at),0,0.35),2.2));
const punch=(f:number,at:number)=>{const t=sec(f,at);if(t<0)return 1;return 1+0.12*eOut(P(t,0,0.2))-0.06*eOut(P(t,0.2,2.5));};
const shake=(f:number,at:number)=>{const t=sec(f,at);return t<0||t>0.4?0:14*Math.exp(-t*10)*Math.sin(t*70);};
Movimientos: tipo cinético (la palabra aterriza en su tiempo de VO, scale(0.6+0.4*pop), ±4° asentamiento durante 0.25 s, palabras acentuadas más grandes + ámbar/rojo), zoom punch en imágenes clave, revelación con máscara circular, transiciones de zoom (escala 1→4.2 en ~9 fotogramas, siguiente escena 1.3→1), sellos rotados y chips mono, cajas de resaltado rojas en capturas de pantalla, contador de pasos, subtítulos karaoke (palabra actual ámbar + 1.08×), CTA pop con shake, SFX de whoosh/pop bajo la VO. Aproximadamente un ritmo cada 1.5–3 s.
Clips de video: envuelve cada <OffthreadVideo> en un <Sequence from={at}> y establece startFrom relativo a él; de lo contrario, el clip se fija en su último fotograma y parece congelado. Repite clips cortos para ritmos largos. Los clips comienzan en el primer fotograma utilizable.
Transiciones de audio: cuando una apertura usa audio en vivo (una canción, un clip) antes de la narración, desvanece hasta silencio y deja ~0.3–0.5 s (una transición de flash/whoosh) antes de la primera palabra de VO para que nunca quede enmascarada.
Presentador opcional (mascota) con sincronía de labios
- Una identidad de personaje consistente en cada video. Capas separadas: cuerpo (boca cerrada pintada), brazo en un pivote de hombro bajo el borde del abrigo, una tapa de hombro estática, sprites de boca
closed/half/open/wide/round. - Conduce la boca desde el WAV de VO, no un bucle:
scripts/build-mouth-track.py→ estados por fotograma (cerrado en silencio y al final, mantenimiento mínimo de 2 fotogramas). Limita el brazo al rango del rig; un balanceo de toda la imagen no es suficiente. - Monta al presentador solo mientras está en pantalla; las portadas usan el cuerpo estático de boca cerrada.
5. Renderizado
- Prefiere una máquina GPU si tienes una (Remotion
--hardware-acceleration, una configuración sinencodingBufferSize/encodingMaxRate, que rompen el NVENC requerido); recurre a CPU solo si está permitido. Siempre di qué dispositivo renderizó. - Renderizado sin procesar:
yuv420p+bt709enremotion.config.ts, luego el pase de Reels.
6. Exportación segura para Reels (scripts/reels-export.sh, detalles en docs/REELS_SAFE_EXPORT.md)
1080x1920 SAR 1, H.264 High level 4.1, yuv420p rango TV, etiquetas BT.709, CFR 30, GOP 60 cerrado, 10 Mbps (mín 8, máx 12), dos pasadas loudnorm −14 LUFS / −1.5 dBTP, AAC 192k 48 kHz estéreo, +faststart, < 90 s. También una vista previa pequeña < 40 MB (scripts/make-preview.sh).
7. QA (obligatorio, antes de la entrega)
- Susurra el MP4 final (
scripts/whisper-check.py): pronunciaciones incorrectas, tartamudeos, sonidos extraños, líneas duplicadas, primera palabra de cada escena audible. - Hoja de contacto en cada límite de escena (
scripts/contact-sheet.sh): fallos, superposiciones, texto cortado, zona segura, clips congelados, apertura/cierre vacíos, marca de agua presente, que coincida conbrand.config.jsony deletreado exactamente. scripts/qa-check.sh: códec/perfil/nivel, pix_fmt, rango, etiquetas de color, CFR, AAC 48k, moov antes de mdat, duración < 90 s, −14 LUFS.- Corrige y vuelve a renderizar. Mantén la versión anterior cuando revises.
8. Siempre entrega con el video
- Subtítulos para IG (gancho, cuerpo, CTA, hashtags ligeros), Threads (más cortos, sin hashtags), TikTok, más un archivo
.srt. - Cuatro portadas A–D: 1080x1920 + cuadrícula 1080x1350, texto clave dentro del centro 1080x1080; un gancho, una foto principal, máximo 2–3 tarjetas numéricas; hoja de contacto + deja que el usuario elija.
- Créditos de fuentes y el archivo de verificación de datos.
- Una línea en
videos-log.md: título, archivos, duración, sonoridad, dispositivo, fuentes, estado "no publicado".
Reglas
- Nunca publiques, envíes mensajes directos, subas o compres. Entrega archivos.
- Sin hechos inventados, estrellas, precios o resultados. Da crédito a los autores de código abierto en pantalla.
- Solo fotos, logotipos y clips reales; sin caras generadas por IA de personas reales. Marca las maquetas como
illustration. - Usa medios a los que tengas derechos; informa al usuario cuando la música o los clips tengan derechos de autor (riesgo de silenciamiento/reclamación).
- Sin secretos en archivos. Haz cualquier pregunta al final, en su propio mensaje.