Communitygithub.com

rocketmandrey/conversational-video-subtitles

Quema subtítulos para videos cortos de diálogo/conversación (Reels, Shorts, TikTok, 1:1, 16:9) a partir de tiempos de palabras: cada palabra hablada, una línea base fija centrada (sin elevaciones ni desplazamientos laterales; las superposiciones con rostros/manos/fuego se registran como aceptadas, una elevación opcional por colisión se registra con motivo y px), los hablantes se distinguen por un color de acento o micromarcador, sin rebote tipo karaoke, contraste WCAG verificado por fotograma. Genera un manifiesto de subtítulos canónico (JSON), una vista previa horneada, SRT plano, ASS y WebVTT con estilo, además de un validador que mide los píxeles renderizados contra el perfil. Úsalo cuando te pidan "añadir subtítulos a un video de diálogo", "субтитры для диалога / скетча / рилса", "haz subs como en Shorts", "quemar subtítulos sin cubrir rostros", "subtítulos karaoke con palabra activa", "проверь субтитры по safe zone", o para exportar SRT/ASS/VTT a partir de marcas de tiempo de palabras (ElevenLabs Scribe, Groq Whisper).

¿Qué es conversational-video-subtitles?

Este skill proporciona un pipeline completo para añadir subtítulos de alta calidad a videos cortos de diálogo o conversación (Reels, Shorts, TikTok, 1:1, 16:9), diseñado para agentes de IA y desarrolladores. Su algoritmo de segmentación inteligente divide el habla en unidades visuales estables basándose en pausas, puntuación, longitud máxima y duración, asegurando que ninguna palabra se corte o se elimine. La colocación por defecto es una línea base fija centrada que nunca se mueve vertical ni lateralmente, evitando el molesto efecto de rebote; las superposiciones con elementos importantes (rostros, manos) se registran como aceptadas en lugar de forzar cambios bruscos, y opcionalmente se puede activar una elevación por colisión si se requiere evitar cubrir rostros a toda costa. El sistema verifica automáticamente el contraste WCAG 4.5:1 por fotograma, soporta cinco perfiles visuales (karaoke, revelación, tarjetas, cinético, híbrido) y exporta a múltiples formatos: manifiesto JSON canónico, SRT plano, ASS con estilo completo (con fuentes, karaoke y posicionamiento), WebVTT con CSS y video horneado como verdad visual. Está orientado a plataformas como Claude Code, Cursor y otros agentes de línea de comandos, y se integra con servicios STT como ElevenLabs Scribe o Groq Whisper para obtener marcas de tiempo de palabras a partir de audio.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add rocketmandrey/conversational-video-subtitles-skill

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Subtítulos de video conversacional

Resumen

Los subtítulos son una capa independiente: llevan cada palabra hablada y nunca dependen de lo que se muestra en el encuadre. Las etiquetas de objetos en el encuadre (nombres de partes, pros/contra, flechas) son una capa diferente con un lenguaje visual distinto (llamadas junto al objeto); nunca cortes una palabra de los subtítulos porque "ya está en pantalla".

Pipeline: words.json + forbid.json (cajas que los subtítulos no deben cubrir) + config.json + perfil → plan → manifest.json (datos canónicos) → burn (vista previa horneada = verdad visual) / export srt|ass|vtt → validate.

Roles de los formatos (no los mezcles):

formatocontieneNO contiene
manifest.jsonpalabras, tiempos, diseño, placement, superposiciones aceptadas, elevaciones con motivos, verificaciones de renderizado: fuente de verdad para los datospíxeles
.srttexto + tiempopeso, color, contorno, palabra activa, posición. Nunca prometas estilo en SRT
.assfamilia + Negrita/\b700, tamaño convertido a métricas libass, contorno, caja de fondo, palabra activa por segmento de tiempo, posiciónfundidos; necesita la fuente: subtitles=x.ass:fontsdir=fonts
.vtt::cue CSS (familia, peso, contorno mediante text-shadow, fondo) + clases .active_<spk> / .em_<spk> / .dim / .u por segmento de tiempoposición exacta; la página debe cargar la fuente con @font-face
preview.mp4 horneadotodo: la única fuente de verdad visual—

Cuándo usarlo

  • Sketchs de diálogo, entrevistas, explicadores con narrador: vertical o cuadrado, máximo una o dos líneas.
  • Tienes tiempos de palabras (o audio: STT en la nube opcional mediante claves de entorno) y sabes dónde están los rostros / manos / objetos clave (cajas; para escenas 3D renderiza una máscara de color plano y delimítala con cajas).
  • No para videos de letras (rebote, palabras con escala no se admiten deliberadamente).

Algoritmo

Segmentación (chunk en el perfil): corte forzado en pausa > pause_s (0,35 s), final de oración, cambio de hablante; corte suave en coma una vez que un fragmento tiene 2+ palabras; limitado por max_words, ancho en píxeles (max_width_frac de la zona segura) y max_dur_s (~1,2 s de habla se lee bien). Un fragmento nunca termina en una palabra funcional (не, в, а, and, the…): pasa al siguiente fragmento. Una palabra solitaria se une a su vecina de la misma oración si cabe. Las duraciones de las palabras se recortan a 2,5× la mediana para que una palabra no se prolongue en una pausa. Tiempo: una unidad aparece ≤ 50 ms antes de su primera palabra, nunca corta la palabra anterior, permanece hold_s después de su última palabra, al menos min_show_s.

Colocación — placement: {mode: fixed_bottom_center, collision_lift: false} (por defecto para diálogo).

  1. Una línea base para todo el video: baseline = safe.bottom - baseline_margin_px (o establécela explícitamente). Cada unidad se alinea por abajo en ella (y + h = baseline) y se centra en el encuadre (x + w/2 = W/2). Nada se mueve: sin elevación, sin desplazamiento lateral, sin x por hablante. El ancho de línea está limitado para que la línea centrada se mantenga dentro de la zona segura.
  2. Una línea que cruza una caja prohibida en la línea base se mantiene: la unidad recibe overlap_accepted: [classes] y la hoja de QA dibuja un marco amarillo "superposición aceptada (línea base fija)". Es información, no un fallo. Cruzar la interfaz de plataforma sigue siendo un error (reduce la fuente o divide la frase).
  3. Opción collision_lift: true (modo baseline_lift): x bajo el hablante (align: speaker) o centrado con alternativas izquierda/derecha; si cada x colisiona, eleva en pasos completos de lift_step_px hasta que quede libre. La unidad recibe lift = {px, reason: [clases impactadas en la línea base], t0, t1}, el manifiesto lista todas las elevaciones en collision_lifts. Sin motivo → sin elevación. Úsalo solo cuando el pedido indique mantener los rostros despejados a toda costa.
  4. Nunca elimines una palabra.

Aspecto. Contorno (outline_px) + sombra suave + un fondo claro cuyo alfa se incrementa por unidad solo lo necesario para mantener el color iluminado más oscuro ≥ 4,5:1 (WCAG) contra el percentil 90 de los píxeles detrás. Los hablantes se diferencian por el color de la palabra activa y, en híbrido/tarjetas, una barra de 4 px en el color del hablante; sin burbujas de diálogo, sin placas de nombre pesadas. Movimiento calmado: solo fundidos alfa, sin saltos de escala, sin deslizamientos verticales.

QA (scripts/validate.py, también se ejecuta con demo):

  • esquema: el manifiesto contra templates/manifest.schema.json;
  • líneas ≤ 2;
  • colocación: en fixed_bottom_center la dispersión de y-línea base y de x-centro sobre todas las unidades es 0 (tolerancia 0,5 px) y no hay elevaciones; en baseline_lift cada elevación está en la cuadrícula, tiene un motivo y la unidad realmente colisiona en la línea base;
  • cero superposiciones con la interfaz de plataforma en cada fotograma; cajas prohibidas: 0 en baseline_lift, reportadas como overlap_accepted (información) en fixed_bottom_center;
  • cada palabra exactamente una vez, en orden, visible mientras se habla;
  • contraste ≥ 4,5 y sin rebote, ambos medidos por fotograma mediante burn;
  • fuentes resueltas: archivo presente, familia y estilo Pillow, peso OS/2, fc-scan, y la selección de fuente libass elige el archivo y peso declarados;
  • píxeles: en el fotograma horneado Y en el renderizado libass del ASS, el color activo está presente, la parte inferior del glifo está dentro de 3 px del diseño, el ancho del trazo está dentro de 1 px de un ráster de referencia del peso declarado, y el contorno está dentro de 1 px de outline_px.

Perfiles (profiles/*.json)

perfilquémejor para
karaoke2–4 palabras a la vez, palabra hablada en color del hablante, resto atenuadomás estable; carriles fijos, explicadores
revealpalabras aparecen al hablar; diseño fijo de antemano; ≤ 2 líneasnarración calmada
cardsun pensamiento por tarjeta (≤ 8 palabras, 2 líneas), panel denso, palabra clave coloreadalíneas lentas y citables; se lee antes del habla
kineticun token a la vez, tipo más grandesolo remates; poco contexto
hybrid (por defecto)≤ 3 palabras, palabras aparecen, subrayado bajo la palabra hablada, color de énfasis, barra de hablantediálogo

Opciones de los sistemas de I+D como parámetros: tempo = chunk.max_dur_s: 1.2 + timing.min_show_s: 0.83 (activado en híbrido); stress = emphasis: "energy" (puntuación z de sonoridad de palabra por hablante desde --audio), o "keyword" con una lista keywords.

Comandos

python -m subkit demo examples/fixture                     # 5 perfiles + hybrid_lift -> vistas previas, srt/ass/vtt, validation_report.{md,json}
python -m subkit plan --words W --forbid F --config C [--profile hybrid] [--audio A] -o manifest.json
python -m subkit burn manifest.json --forbid F --video in.mp4 [--audio A] -o preview.mp4 --qa qa.jpg
python -m subkit export manifest.json --format ass -o captions.ass     # srt | ass | vtt
python scripts/check_clean.py                                          # antes de un commit: sin media/keys/rutas absolutas
ELEVENLABS_API_KEY=... python -m subkit stt audio.wav -o words.json     # opcional; o --provider groq con GROQ_API_KEY

Fallos comunes

  • libass ≠ tamaño de fuente Pillow. libass dimensiona por OS/2 winAscent+winDescent y coloca la línea base winAscent debajo de la parte superior de la línea; export convierte ambas, o el texto ASS sale ~35% más pequeño, más delgado y más bajo.
  • Las fuentes variables en ASS caen en la instancia predeterminada (generalmente Regular). Entrega una instancia estática del peso que uses (aquí Onest-Bold.ttf, 700) y pasa fontsdir.
  • ffmpeg -ss antes de -i reinicia las marcas de tiempo: el filtro de subtítulos renderiza entonces t=0. Busca después de -i al quemar ASS.
  • El karaoke \k/\kf está en centisegundos y colorea cada palabra hablada permanentemente; para "solo la palabra actual" emite segmentos de tiempo (como hace export).
  • SRT no tiene estilo: si un entregable necesita peso o resaltado, entrega ASS/VTT o el video horneado.
  • Una línea flotante (x por hablante, histéresis, elevaciones "que se ven mejor") se percibe como vibración en diálogo. Mantén el modo fijo predeterminado; en baseline_lift el validador vuelve a verificar que cada elevación realmente colisiona.
  • El contraste se evalúa sobre el color iluminado contra el 10% más brillante de los píxeles detrás, no el promedio: el fuego, la nieve y los reflejos fallan los promedios.
  • No elimines palabras ya dibujadas en pantalla. Mueve la etiqueta dibujada a una capa de anotaciones en su lugar.
  • Las zonas seguras en templates/safe_zones.json son aproximadas; las interfaces de plataforma cambian, así que verifícalas de nuevo antes de publicar.

Lista de verificación

  • words.json cubre todo el discurso (STT en la nube o un alineador forzado); hablantes etiquetados
  • forbid.json: rostros, manos, fuego/objetos por toma (+ cajas speakers para align: speaker)
  • config: tamaño, fps, safe_zone, colores de hablante (diferentes matices, ambos ≥ 4,5:1 sobre el fondo)
  • plan → manifiesto; lee overlap_accepted (fijo) o collision_lifts (modo elevación, cada uno con un motivo real)
  • burn → mira la hoja de QA (cian = línea base, amarillo = superposición aceptada, naranja = elevación con motivo) y los fotogramas reales
  • validate todo OK; entrega el video horneado (+ ASS/VTT si un reproductor los renderiza; SRT solo como texto plano)

Skills relacionados