Communitygithub.com

hongliangzheng11/lyric-video-skill

Genera videos de letras bilingües en formato vertical 1080×1920 con HyperFrames: fondo oscuro y onírico, un único vidrio geométrico que rebota y se deforma, una banda de animales en la parte inferior y subtítulos con la letra original y su traducción al chino, sincronizados con marcas de tiempo reales obtenidas mediante Whisper.

¿Qué es lyric-video-skill?

Este skill automatiza la creación de videos musicales con letras animadas para canciones, combinando la potencia de HyperFrames y Whisper. El usuario solo debe proporcionar el archivo de audio (MP3/WAV) y el nombre del artista. El skill se encarga de todo: desde la configuración del proyecto, la transcripción automática con Whisper (con soporte para canciones en múltiples idiomas), la sincronización precisa de las letras, hasta la renderización final del video. Soporta dos flujos de trabajo principales: 3A (cuando el usuario proporciona las letras oficiales, que se priorizan y se usan como guía para la transcripción) y 3B (cuando no hay letras oficiales, y se reconstruyen completamente desde el audio). Está diseñado para ser usado por desarrolladores o agentes de IA en plataformas como Claude Code, Cursor o Codex, y su funcionamiento se basa en un pipeline de scripts (Python y Node.js) que garantizan un estilo visual consistente: fondo oscuro con degradados, un objeto de vidrio que rebota y cambia de forma, una banda de animales animada y subtítulos centrados con la letra original arriba y la traducción al chino abajo. La calidad final se verifica mediante múltiples pasos de validación y capturas de pantalla, asegurando que no haya errores de sincronización ni visuales.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add hongliangzheng11/lyric-video-skill

Preguntar en tu IA favorita

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

Documentación

Canalización estándar para videos de letras

Un conjunto de estilos fijos ya validados. El usuario solo necesita proporcionar el archivo de audio y el nombre del artista; todo lo demás se ejecuta según este estándar, sin rediseñar la apariencia. Las letras oficiales son opcionales: si se proporcionan, se sigue la ruta rápida 3A (sin conjeturas de texto); si no, se sigue la ruta de reconstrucción 3B. En ambas rutas, la línea de tiempo debe basarse en mediciones reales.

Entrada / Salida

EntradaDescripción
AudioObligatorio. Cualquier archivo MP3/WAV/FLAC, de duración ilimitada
Nombre del artistaObligatorio. Se utiliza para la introducción, la marca de agua constante y los créditos finales
Letras oficiales (opcional)Si el usuario las tiene, se priorizan: sin conjeturas de texto, pero la línea de tiempo se obtiene completamente mediante reanálisis con indicaciones (ver 3A). Si no se proporcionan, se reconstruyen con Whisper (3B)
Título de la canción (opcional)Por defecto se toma del nombre del archivo; Artista - Título.mp3 elimina automáticamente el prefijo del artista
Frase temática (opcional)Una línea en chino debajo del título de la introducción; por defecto no se muestra

Salida: <proyecto>/renders/video.mp4 + renders/contact-sheet.jpg + transcript.json + NOTES.md.

Especificaciones fijas (no modificables)

  • Pantalla vertical 1080 × 1920, 30 fps, duración total igual a la duración real del audio (sin recortar, estirar ni cambiar la velocidad).
  • El audio se usa en su archivo original; el marco de trabajo lo normaliza durante el renderizado (data-volume="0.9").
  • Fondo: de negro puro a azul violáceo muy oscuro, con dos grupos de motas de luz desfasadas que se desvanecen lentamente; no usar filtros CSS de pantalla completa (dejan uniones visibles).
  • Elemento principal: un solo vidrio transparente, con 8 contornos geométricos redondeados (esfera/elipse horizontal/elipse vertical/rectángulo redondeado/rombo/triángulo/pentágono/hexágono). Al chocar con los bordes de la pantalla, se comprime un 11% a lo largo de la normal → se transforma en otro contorno en 0.94 s → rebota y continúa desplazándose. Línea de tiempo única para toda la canción, no se reinicia al cambiar de letra.
  • Parte inferior: banda de animales (gato/oso/pingüino/zorro) en bucle continuo de 4.5417 s, pegada al fondo, 1080×526, capa por encima de las motas de luz y por debajo del vidrio, mix-blend-mode: screen.
  • Subtítulos: todo el bloque centrado verticalmente en el centro óptico de la imagen (y≈960); inglés/texto original arriba (Inter, tamaño de fuente autoajustable según el ancho de la línea), chino abajo (SimSun, color marrón cálido #DDA97D), con fundido de entrada/salida en grupo.
  • Los créditos utilizan el nombre del artista, no aparecen etiquetas de párrafo como VERSE / CHORUS / BRIDGE en la imagen.

Los parámetros de detalle, colores, tamaños de fuente y aspectos técnicos se encuentran en references/style-spec.md.

Flujo de trabajo

Supongamos que HF = directorio raíz de instalación de HyperFrames (por defecto G:\hyperframes), PY = Python con faster-whisper (por defecto C:\Users\Admin\.workbuddy-ai\binaries\python\envs\hf\Scripts\python.exe), SK = directorio de este skill.

1. Crear proyecto

"$PY" "$SK/scripts/new_project.py" "<ruta_del_audio>" "<nombre_del_artista>" [--title "título"] [--tagline "una línea en chino"] [--root G:/hyperframes/videos]

Genera la ruta absoluta del proyecto; anótela obligatoriamente. Crea un directorio único → copia el audio → lo convierte a WAV mono de 16 kHz → ejecuta el script oficial analyze-beatgrid.py generando audiomap.json → copia el sprite de la banda de animales y GSAP → escribe project.json.

Nunca sobrescribir un directorio de proyecto existente; para modificar un trabajo ya entregado, debe crearse un nuevo proyecto.

2. Transcripción

"$PY" "$SK/scripts/transcribe.py" "<proyecto>" --model medium.en --lang en

Para una canción de 3–4 minutos en CPU int8 se tarda unos 3–6 minutos. Para canciones que no sean en inglés, cambie --lang al idioma correspondiente y quite el .en de --model. Genera whisper_raw.json (con marcas de tiempo a nivel de segmento y palabra).

Canciones multilingües (inglés+japonés, inglés+coreano, etc.) no usar transcribe.py: --lang fija un solo idioma y el otro será transcrito forzosamente como el primero — el resultado sonará coherente pero estará completamente equivocado, peor que no tener solución. En su lugar, use la detección automática por ventanas:

"$PY" "$SK/scripts/transcribe_mixed.py" "<proyecto>" --model "C:/Users/Admin/.cache/whisper-manual/large-v3-turbo"

Decodifica en ventanas de 14 s / paso de 10 s, cada ventana determina su propio idioma, conserva solo las palabras centrales de la ventana (las zonas superpuestas se descartan, sin duplicados), imprime un mapa de idiomas y escribe whisper_mixed.json. Revise primero el mapa de idiomas: los puntos donde cambia el idioma suelen ser los límites de las estrofas. Tenga en cuenta que los modelos .en (medium.en / small.en) son exclusivamente para inglés y físicamente no pueden generar japonés; las canciones multilingües deben usar modelos multilingües.

Para la ruta 3A (con letras oficiales), este paso es opcional: la línea de tiempo se basa en el reanálisis con indicaciones, pero para canciones multilingües sigue siendo recomendable ejecutar primero transcribe_mixed.py para ver el mapa de idiomas y determinar si cada ventana debe usar --lang en o --lang ja.

3. Escribir el archivo fuente de letras (este paso debe hacerse manualmente)

Primero, decida qué ruta seguir y luego escriba <proyecto>/lyrics_source.json.

3A El usuario ha proporcionado letras oficiales (ruta preferida)

  • El texto se toma tal cual de las letras oficiales, sin cambios: el campo en es el texto original oficial (las frases en inglés se ponen en inglés, las frases en japonés se ponen en japonés, los kanji/kana se escriben exactamente), zh es la traducción frase por frase.
  • La línea de tiempo no se obtiene de una transcripción completa de la canción: se usan las letras oficiales como initial_prompt para reanálisis por zonas y obtener marcas de tiempo a nivel de palabra (ver 3.5), y cada línea tiene start/end explícitos. align_lyrics.py usará el canal manual para anclar cada línea.
  • Si las letras oficiales no coinciden con lo que se escucha en el reanálisis → se adopta lo oficial en todos los casos, y las diferencias se listan en NOTES.md para que el usuario las confirme. Tres casos comunes: forma estándar vs error tipográfico (不甲斐ない / 不甲埋ない), palabras con pronunciación similar (束の間の / 明日の間の), confusión de consonantes sordas (smiling / falling).
  • Si en las letras oficiales hay dos líneas separadas que se cantan de una sola vez y cada una dura menos de 1 segundo → los subtítulos se fusionan en una sola línea (el texto no cambia, solo se muestran juntas), y la tabla de fusiones se escribe en NOTES.md.
  • Las onomatopeyas o improvisaciones no presentes en las letras oficiales ("Yeah,", tarareos, silbidos) → no se subtitulan, no se inventan.
  • La estructura se escribe así:
{
  "audio": "Artista - Título.mp3",
  "models": ["large-v3-turbo — reanálisis por zonas con indicación, letras oficiales como initial_prompt"],
  "lines": [
    {"en": "Oh my god, they're coming again", "zh": "Oh Dios mío, están llegando otra vez", "start": 6.36, "end": 9.10, "lang": "en", "conf": "high"}
  ]
}

3B Sin letras oficiales (ruta de reconstrucción)

Lea whisper_raw.json (o whisper_mixed.json para canciones multilingües) y escriba el archivo fuente:

{
  "audio": "Artista - Título.mp3",
  "models": ["faster-whisper medium.en — pista completa, marcas de tiempo por palabra"],
  "lines": [
    {"en": "Sometimes I lay under the moon", "zh": "A veces me acuesto bajo la luna"},
    {"en": "And thank God I'm breathing", "zh": "Y agradezco a Dios por seguir respirando"}
  ]
}

Requisitos comunes para ambas rutas:

  • Cada línea = una frase real cantada, con la puntuación de las letras oficiales; si Whisper fusiona frases demasiado largas, deben dividirse.
  • La ortografía en inglés debe corregirse según las letras oficiales (conservar apóstrofos como 'Cause / I've), no copie los errores de Whisper.
  • La traducción al chino debe ser frase por frase, con el tono adecuado a la canción, sin omitir frases, sin fusionar, sin texto de relleno.
  • Los estribillos repetidos deben escribirse cada vez (no usar "ídem").
  • Si alguna frase está cubierta por el acompañamiento y la alineación automática es claramente incorrecta, asígnele un tiempo explícito (escuche el audio primero antes de rellenar): {"en": "...", "zh": "...", "start": 13.22, "end": 16.80}.
  • Canciones en chino / música instrumental: en las canciones en chino, ponga el texto original en en y deje zh como cadena vacía; en la pantalla solo se muestra una línea. La música instrumental no es aplicable a este skill.
  • Canciones multilingües: el campo en debe contener el texto original en el idioma real de la frase (inglés en inglés, japonés en japonés, kanji/kana tal cual), zh debe contener la traducción al chino. La pantalla está diseñada para mostrar "texto original + chino" en dos líneas, por lo que la combinación no rompe el diseño; la línea en inglés de la plantilla ya incluye Yu Gothic UI / Yu Gothic / Meiryo como fuentes de respaldo, por lo que los kana y kanji se mostrarán correctamente.

3.5 Reanálisis con indicaciones: método clave para la precisión de la línea de tiempo

La transcripción completa de la canción suele ser imprecisa para canciones rápidas, coros simultáneos o frases habladas rápidamente. Usar las letras conocidas como initial_prompt para reanálisis por zonas es el método de mayor precisión — la calidad de la indicación determina directamente la precisión del reanálisis: letras oficiales > letras adivinadas > sin indicación. Se puede escribir un script rescan_all.sh para procesar toda la canción en lote:

"$PY" "$SK/scripts/rescan.py" "<proyecto>" --from 6.0 --to 20.0 \
    --model "C:/Users/Admin/.cache/whisper-manual/large-v3-turbo" \
    --lang en --prompt "<texto original oficial de esa zona>"

Puntos clave:

  • Dividir toda la canción en zonas de 20–26 s, con un solapamiento de aproximadamente 0.5 s entre zonas, y reanalizar cada zona por separado; large-v3-turbo en CPU int8 tarda unos 5–10 s por zona.
  • Para canciones multilingües, cada zona se reanaliza dos veces (una con --lang en y otra con --lang ja): solo se adoptan las marcas de tiempo a nivel de palabra que coincidan en ambas pasadas; las palabras que aparecen solo en una pasada se verifican con la otra.
  • Para frases con arranque dudoso o poco claro, añada una zona estrecha de 2 s para fijar el inicio (--from 37.2 --to 39.2 solo para fijar el inicio de una frase); el inicio de las zonas divididas suele verse afectado por el final de la frase anterior.
  • Manejo de alucinaciones: el reanálisis puede, de forma esporádica, repetir la misma frase completa 15+ veces → esa zona se descarta, se cambia al canal del otro idioma o se reduce la zona y se reanaliza, y solo se toman las marcas de tiempo limpias y únicas; no escriba las marcas de tiempo de las alucinaciones en el archivo fuente de letras.
  • Cada reanálisis guarda rescan_<from>-<to>.json e imprime el inicio y fin de cada palabra dentro de la zona. Esto es una segunda decodificación, no una estimación; los números se escriben directamente en start/end de lyrics_source.json.
  • Para pasajes cubiertos por el acompañamiento, procesados como ooh, fusionados en un bloque grande, o frases repetidas que se han tragado, use este mismo método primero para escribir valores de cobertura; cambiar el modelo (small.en / medium.en) también es efectivo.

4. Alineación + Generación

"$PY" "$SK/scripts/align_lyrics.py" "<proyecto>"
"$PY" "$SK/scripts/motion.py" "<proyecto>"
"$PY" "$SK/scripts/gen_storyboard.py" "<proyecto>"
"$PY" "$SK/scripts/build_frame.py" "<proyecto>"
"$PY" "$SK/scripts/make_index.py" "<proyecto>"

align_lyrics.py imprimirá una tabla línea por línea con los tiempos y realizará dos comprobaciones estrictas:

  • Si alguna frase al inicio o final no coincide → sale con error e indica qué frases son (no usará tiempos adivinados para encubrirlo).
  • Si alguna frase del medio no coincide, o dos frases tienen el mismo inicio, o alguna frase se comprime por debajo de 0.5 s → imprime REVIEW y sale con código 2.

Si ocurre alguno de estos casos, vuelva al paso 3.5 y añada valores de cobertura hasta que la salida sea OK: every line anchored to a real sung timestamp. No omita este paso para renderizar. En la ruta 3A, todas las líneas tienen start/end explícitos (canal manual), por lo que todas deberían estar ancladas, sin huecos; en la ruta 3B, se basa en la coincidencia de palabras; las líneas que no coincidan se les asignan tiempos según el paso 3.5.

La semilla aleatoria de motion.py está determinada por el "nombre del artista + título de la canción" — para una misma canción, cada reejecución genera una trayectoria idéntica; canciones diferentes generan trayectorias diferentes.

5. Verificación

cd "<proyecto>"
node "$HF/packages/cli/bin/hyperframes.mjs" check .
node "$HF/packages/cli/bin/hyperframes.mjs" snapshot . --at "3,20,60,120" --no-end -o build/shots

Debe cumplirse: lint / runtime / layout / motion 0 errores, y el contraste debe pasar WCAG AA. Luego revise las capturas de pantalla : el vidrio debe estar completo, las letras centradas, la banda de animales en la parte inferior, sin burbujas ni etiquetas de párrafo sobrantes. composition_file_too_large es una advertencia de estilo cuando un archivo de composición supera las 300 líneas; es aceptable (es inevitable en un diseño de línea de tiempo única).

6. Renderizado + Aceptación

node "$HF/packages/cli/bin/hyperframes.mjs" render . --skill=music-to-video -q delivery -o renders/video.mp4 --fps 30
"$PY" "$SK/scripts/verify.py" "<proyecto>"

verify.py verificará la resolución, la tasa de fotogramas y la desviación de duración (si supera un fotograma, se reporta un error) y generará una hoja de contactos. Es obligatorio revisar la hoja de contactos para confirmar que la introducción, las estrofas, los estribillos, las repeticiones y el final tengan imágenes, y que los subtítulos no tengan espacios vacíos o desalineaciones.

Finalmente, escriba NOTES.md (especificaciones, origen de la línea de tiempo, comandos de reproducción, registro de verificación) y entregue el video final y la hoja de contactos al usuario.

Disciplinas estrictas

  • No usar BPM/cuadrícula de compás para deducir los tiempos de las letras; las marcas de tiempo solo pueden provenir de mediciones reales de Whisper (incluido el reanálisis con indicaciones) o de tiempos de cobertura obtenidos al escuchar manualmente.
  • Las letras oficiales proporcionadas por el usuario tienen prioridad sobre cualquier resultado de reconstrucción del modelo; si hay conflicto, se adopta lo oficial y las diferencias se escriben en NOTES.md para que el usuario las confirme.
  • No cambiar la resolución, la tasa de fotogramas, la duración, los créditos o la posición de los subtítulos solo porque "se vea mejor".
  • No afirmar sincronización audiovisual sin haberla verificado; cada canción debe revisarse fotograma a fotograma.
  • Si faltan materiales, herramientas no disponibles o hay conflictos de especificaciones → informar claramente, no usar datos estimados para fingir resultados.
  • Los proyectos ya entregados y sus archivos fuente se consideran de solo lectura; cualquier modificación debe realizarse en un nuevo directorio.

Dependencias del entorno

  • HyperFrames: node <HF>/packages/cli/bin/hyperframes.mjs (incluye skills/music-to-video/scripts/analyze-beatgrid.py).
  • Python: faster-whisper, librosa, soundfile, numpy, scipy, pillow. Si faltan: python -m venv <env> && <env>/Scripts/pip install faster-whisper librosa soundfile numpy scipy pillow.
  • Modelos Whisper: pasando el nombre del modelo se descarga automáticamente; también se puede apuntar a un directorio local (en esta máquina se usa comúnmente C:\Users\Admin\.cache\whisper-manual\medium.en; para reanálisis con indicaciones se prefiere C:\Users\Admin\.cache\whisper-manual\large-v3-turbo; en CPU int8, una canción completa tarda unos 48 s, una zona 5–10 s).
  • ffmpeg / ffprobe deben estar en el PATH.

Problemas conocidos

  • No usar <video> para la capa de la banda: hyperframes render en Windows falla al enlazar el directorio de fotogramas extraídos al directorio de compilación (error ENOENT ... __hyperframes_video_frames/*/frame_00001.jpg). Usar sprite.
  • El filtro CSS de pantalla completa filter: hue-rotate() deja juntas verticales en el renderizado; usar dos capas de motas de luz con fundido cruzado.
  • Las plantillas HTML con data-composition-id no deben colocarse en el directorio raíz del proyecto (activa multiple_root_compositions); colocarlas en build/.
  • Las fuentes chinas deben declararse explícitamente con @font-face { src: local("SimSun"); }, de lo contrario, lint reporta font_family_without_font_face.
  • Alucinaciones en el reanálisis: ciertas zonas pueden repetir la misma frase completa 15+ veces (por ejemplo, 「永遠の行方はどこにある?」×15) → esa zona se descarta, se cambia al canal en inglés intercalado o se reduce la zona y se reanaliza; no escribir las marcas de tiempo de las alucinaciones en el archivo fuente de letras.
  • Inconsistencias entre letras oficiales y reanálisis: adoptar lo oficial y listar la tabla comparativa en NOTES.md para que el usuario confirme; no modificar el texto oficial por cuenta propia.
  • Líneas rápidas fusionadas: en las letras oficiales, si dos líneas separadas se cantan de una sola vez y cada una dura menos de 1 segundo, los subtítulos se fusionan en una sola línea (el texto no cambia, solo se muestran juntas), la tabla de fusiones se escribe en NOTES.md.
  • Error de carga de fuentes en modo headless (Yu Gothic UI muestra FAILED al extraer fotogramas) no es motivo de alarma: las fuentes con src: local() que están instaladas en el sistema suelen ser encontradas por el navegador, la siguiente fuente de respaldo también funcionará; primero revise las capturas de pantalla antes de decidir si intervenir.

Skills relacionados