Communitygithub.com

Localización de video: doblaje al chino con voz clonada

Sustituye la voz en inglés de un video por un doblaje al chino clonado del hablante original, sobre una línea de tiempo idéntica: misma imagen, misma duración, música de fondo conservada y subtítulos bilingües incrustados, con controles de duración, revisión en frío y escucha.

¿Qué es Localización de video: doblaje al chino con voz clonada?

Receta Localización de video, paso 3 (el doblaje), de ChHsiching/video-dubbing-skill. Lee el video original y una pasada de subtítulos bilingües y genera una segunda versión sin tocar la primera. El agente traduce para doblaje — frases completas ajustadas a un presupuesto de caracteres por cue, no fragmentos de subtítulo — y aplica los controles de calidad: duración, revisión en frío, escucha y revisión tras incrustar. La CLI `cook dub` hace el trabajo determinista: Demucs conserva la música, IndexTTS2 clona la voz del hablante, cada línea se ajusta a su ventana original con atempo por cue y ffmpeg incrusta subtítulos ZH+EN. Resultado: un MP4 doblado con la duración exacta del original y SRT zh/en para subir.

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ChHsiching/video-dubbing-skill/tree/HEAD/skills/video-dubbing

Preguntar en tu IA favorita

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

Documentación

Reemplaza las voces originales en inglés de un video con una locución en chino en una línea de tiempo de identidad: el video nunca se reajusta temporalmente, cada fotograma conserva su marca de tiempo original y la versión doblada tiene exactamente la duración del video original. El audio sintetizado de cada señal se ajusta a su propia ventana original (inicio de la señal → inicio de la siguiente señal) con un atempo por señal; tanto la síntesis más rápida como la más lenta encajan en la ventana. El resultado es una segunda versión: la misma imagen a la misma velocidad, audio en chino y subtítulos bilingües (ZH+EN) incrustados.

Esta habilidad realiza las dos partes creativas que la CLI no puede hacer: traducir para doblaje (oraciones completas escritas dentro de un presupuesto de caracteres, no la fragmentación de subtítulos) y ejecutar los controles de calidad (control de longitud, revisión en frío, control auditivo, revisión posterior a la incrustación). La ejecución determinista (Demucs, IndexTTS2, ensamblaje, reajuste temporal, ffmpeg) es manejada por el subcomando cook dub de la CLI cook. Todos los scripts de etapa viven dentro del paquete cook (stages/dub/); cada invocación a continuación es un comando cook. pip install -U video-cook actualiza todo una vez.

Cuándo recurrir a esta habilidad

Tienes un video que ya tiene:

  • Un archivo de video en bruto (<output-root>/raw/<name>.raw.mp4) — el original, con voces en inglés.
  • Una ejecución de subtítulos bilingües de video-subtitle — específicamente transcript/<name>.en.full.srt (la transcripción completa en inglés, fusionada a partir de fragmentos de whisperX) y transcript/translations.txt.

Deseas una versión doblada al chino. Si aún no tienes estos, ejecuta primero video-download y luego video-subtitle — esta habilidad lee sus salidas.

Lo que produces

Se entregan tres productos: cooked/<name>.dubbed.mp4 (video original sin cambios, doblaje en chino, subtítulos bilingües incrustados — duración idéntica al original), cloud-srt/zh.dub.srt + en.dub.srt (subtítulos para subir). Todo lo demás son archivos de trabajo bajo dubbed/ — el árbol anotado a continuación mapea cada uno de ellos.

La ejecución no termina hasta que pasan los controles del Paso 6.

Estructura de directorios

Esta habilidad añade dubbed/ (directorio de trabajo) y escribe los productos finales en cooked/ y cloud-srt/:

<output-root>/
├── raw/                            ← de video-download (esta habilidad lo lee)
│   └── <name>.raw.mp4
├── transcript/                     ← de video-subtitle (esta habilidad lo lee y añade)
│   ├── <name>.en.full.srt          ← transcripción completa en inglés (la fuente del guion de doblaje)
│   ├── translations_dub.txt        ← esta habilidad escribe: una línea en chino por señal
│   └── <name>.zh.dub.srt           ← esta habilidad escribe
├── cooked/                         ← los videos finales viven aquí
│   ├── <name>.cooked.bar.mp4       ← de video-subtitle (sin cambios)
│   └── <name>.dubbed.mp4           ← producto de esta habilidad
├── cloud-srt/                      ← los subtítulos para subir viven aquí
│   ├── zh.srt / en.srt             ← de video-subtitle (sin cambios)
│   └── zh.dub.srt / en.dub.srt     ← subtítulos para subir de esta habilidad
└── dubbed/                         ← directorio de trabajo de esta habilidad
    ├── _reference/
    │   └── ref.wav
    └── _full/
        ├── _segments/              ← caché de IndexTTS2 por señal
        ├── dub.wav                 ← el doblaje en el reloj original
        ├── dubbing.srt             ← archivo de trabajo (ZH, antes de acortar)
        ├── dubbing.short.srt       ← archivo de trabajo (ZH, salida acortada)
        ├── dubbing.merged.srt      ← archivo de trabajo (ZH, después de acortar; copiado a cloud-srt)
        ├── dubbing.en.srt          ← archivo de trabajo (EN de oración completa)
        ├── dubbing.en.short.srt / dubbing.en.merged.srt  ← sus salidas de acortar y fusionar
        ├── dubbing.bilingual.srt   ← archivo de trabajo (unión bilingüe; lo que se incrusta)
        ├── dubbing.cooked.ass     ← archivo de trabajo (salida ass, copiado a burn.ass)
        └── burn.ass                ← el ASS realmente incrustado

Regla: dubbed/ es el directorio de trabajo; cooked/<name>.dubbed.mp4 y cloud-srt/{zh,en}.dub.srt son los productos. Nunca toques raw/, transcript/<name>.zh.srt, cooked/<name>.cooked.mp4 o cloud-srt/{zh,en}.srt — esos pertenecen a video-subtitle. Si esta habilidad falla a mitad de camino, el envío del video cocinado bilingüe aún está completo.

El pipeline

Dos etapas después del trabajo creativo: síntesis luego ensamblaje, invocadas a través de cook:

cook dub synth    <root> <name> --python <indextts-venv>/Scripts/python.exe
cook dub assemble <root> <name> --python <indextts-venv>/Scripts/python.exe [--keep-subs]
# o ambas:
cook dub full <root> <name> --python <indextts-venv>/Scripts/python.exe

Los pasos a continuación describen lo que cada etapa hace internamente (para que puedas verificar las salidas y diagnosticar fallos); los comandos cook dub <stage> son cómo las ejecutas.

Paso 0 — Resolver los entornos

Hay dos entornos de Python involucrados, y confundirlos es el modo de fallo que este paso pretende evitar:

  • Entorno de cook (Python del sistema o ~/.venvs/video-tools/) — donde vive la CLI cook, con whisperX/yt-dlp/torch para el pipeline de subtítulos.
  • Entorno de IndexTTS2 (~/Git/index-tts/.venv) — un venv separado que contiene indextts, torch, demucs y whisperx (el pipeline de doblaje añade los dos últimos sobre el archivo de bloqueo ascendente). Estas dependencias son pesadas y están aisladas intencionalmente; no intentes instalarlas en el entorno de cook.

cook ejecuta cada etapa de doblaje como un subproceso bajo el venv de IndexTTS2 mediante --python, por lo que from indextts import ... se resuelve allí. Cada comando cook dub en esta habilidad toma --python <indextts-venv>/Scripts/python.exe. Resuelve la ruta del venv una vez (por defecto ~/Git/index-tts/.venv) y reutilízala para toda la ejecución.

0a. Verificar que ambos entornos son accesibles:

<cook-venv>/Scripts/cook doctor                                    # whisperX/yt-dlp/ffmpeg
<indextts-venv>/Scripts/python -c "from indextts.infer_v2_5 import IndexTTS2; import demucs, whisperx; print('ok')"   # indextts (v2.5) + demucs + whisperx

0b. Restricción de un solo hilo. IndexTTS2 debe ejecutarse en un solo hilo (OMP_NUM_THREADS=1), o produce audio basura. cook establece esto internamente antes de importar torch, por lo que no necesitas exportarlo tú mismo.

Listo cuando el doctor de cook informa que whisperX/yt-dlp/ffmpeg están instalados, el venv de IndexTTS2 importa indextts (v2.5) + demucs + whisperx, y conoces la ruta absoluta a <indextts-venv>/Scripts/python.exe para pasarla como --python.

Paso 1 — Separar las voces del video original

El audio original es una pista mixta (voces + BGM + SFX). Demucs lo divide para que podamos extraer una referencia limpia y verificar la presencia de BGM más adelante.

cook dub separate <output-root> <name> [--model htdemucs] --python <indextts-venv>/Scripts/python.exe

Demucs vive en el venv de IndexTTS2, por lo que --python apunta allí. Usa htdemucs (modelo único, ~3GB de RAM), no htdemucs_ft (conjunto de 4 modelos, ~20GB de RAM — se queda sin memoria en máquinas de 32GB). La calidad es ligeramente inferior pero adecuada para la extracción de referencia.

En modo de primer plano (predeterminado), cook mueve las pistas separadas a dubbed/vocals.wav + dubbed/no_vocals.wav automáticamente; en modo --detach las mueves tú mismo después de que aparezca el marcador de finalización.

Listo cuando dubbed/vocals.wav Y dubbed/no_vocals.wav existen y su duración coincide con el original ±0.5s.

Paso 2 — Extraer el clip de referencia

IndexTTS2 necesita un clip limpio de 14-30 segundos del hablante original — lee como máximo los primeros 15s, por lo que una ventana de capacidad completa es mejor que una corta. IndexTTS2 clona la prosodia, no solo el timbre: el ritmo de entrega de la ventana se convierte en el ritmo de entrega de todo el doblaje. El selector predeterminado toma el discurso continuo más largo, que tiende a seleccionar el pasaje más lento y deliberado — a menudo incorrecto. Prefiere el escáner:

cook dub scan-reference <output-root> <name> \
    [--full-band] [--python <indextts-venv>/Scripts/python.exe]

Clasifica ventanas fijas de 15s por tasa de sílabas (filtradas por densidad y pausas) y corta los 3 candidatos principales (más copias de banda completa de 48k con --full-band). La densidad es solo la mitad de la historia — IndexTTS2 también clona la prosodia de la ventana, por lo que también clasifica a los candidatos por vivacidad: desviación estándar de F0 de pyin y rango de semitonos por ventana (una ventana plana dobla de manera plana; usa librosa.pyin, NO detect_pitch_frequency, que informa la sonorización en silencio). Autentica cualquier archivo de referencia heredado antes de reutilizarlo: un derivado reprocesado conserva su incrustación de hablante y estadísticas de tono, pero pierde la correlación de forma de onda, por lo que una correlación cercana a cero NO significa "hablante diferente" — calcúlala contra un clip de anclaje con cook dub scan-reference --score <file> --anchor <anchor.wav> (el audio real del mismo hablante se sitúa cerca de 0.94; el sintetizado toma 0.66-0.76 es la banda normal) y compara las estadísticas de pyin. De cualquier manera: el oído del usuario elige la referencia, siempre — sintetiza las frases piloto una vez por candidato (Paso 3a), preselecciona cada toma con un recorrido de ida y vuelta de ASR de modelo pequeño (las palabras omitidas indican confusión; la salida en chino tradicional indica un acento que la ventana filtra en la toma), califica con cook dub scan-reference --score, luego entrega los wavs con igualación de volumen al usuario.

Listo cuando dubbed/_reference/ref.wav existe, tiene 14-30s y no tiene un intervalo de silencio > 0.3s (ffmpeg -af silencedetect).

Paso 3 — Traducir para doblaje (el agente hace esto)

Aquí es donde el doblaje diverge de los subtítulos. No uses translations.txt (la traducción de subtítulos) — sigue los cortes de fragmentos de whisperX, que dividen oraciones. El doblaje necesita oraciones completas para que el chino fluya naturalmente cuando se habla.

Lee <output-root>/transcript/<name>.en.full.srt (la transcripción completa en inglés). Traduce cada señal tú mismo, escribiendo a transcript/translations_dub.txt — una línea en chino por señal en inglés, línea N = señal N.

La regla del presupuesto de caracteres — el núcleo de la traducción de doblaje. El presupuesto de cada señal es segundos de ventana × tasa de habla normal, fijado antes de que escribas: la ventana es el inicio de la señal hasta el inicio de la siguiente señal (ventana + pausa siguiente), la tasa es medida, nunca asumida — cook dub measure-rate en las tomas piloto que el usuario eligió (procedimiento: Paso 3a). Una tasa asumida pasa todas las compuertas y dobla en segundos de silencio muerto: una conferencia de 45 minutos presupuestada a una tasa asumida de 5.0 chars/s contra una real de 6.93 dejó 83 señales con >2.5s de silencio y costó una resíntesis de 82 señales. Escribe según el presupuesto: ligeramente por encima está bien, notablemente por debajo no — una toma corta ahora mantiene su ritmo natural y deja una pausa de respiración, por lo que el texto por debajo del presupuesto significa silencio muerto y el texto por encima del presupuesto significa compresión audible. Recalibra ante cualquier cambio de referencia o factor de duración: un presupuesto calibrado para una referencia retirada pasa todas las compuertas y aún así dobla en silencio muerto. Sintetiza una sonda estratificada (5-10 señales típicas por hablante en bandas de longitud) y vuelve a verificar el guion contra las ventanas a las tasas medidas — procedimiento en REFERENCE.md → "Recalibración de tasa". Cómo llenar un presupuesto legítimamente (y qué "llenar" NO está permitido inventar): REFERENCE.md → "Llenar el presupuesto de caracteres".

Principios de traducción (más allá del presupuesto):

  • Traduce pensamientos completos, no fragmentos. El inglés ya son oraciones completas; tu señal en chino es un pensamiento completo.
  • Mantén los términos técnicos en inglés donde los desarrolladores chinos lo hacen — spec, plan, prototype, agent, token, skill, session, branch, route, etc. Ver REFERENCE.md → "Lista de retención de términos".
  • Mantén el inglés para cualquier cosa que se muestre en pantalla — etiquetas de UI, código, URLs, nombres de archivo. Ejemplos en REFERENCE.md → "Lista de retención de términos".
  • Traduce conceptos que tienen nombres chinos estándar cuando no se muestran en pantalla — los ejemplos trabajados viven en la lista de retención de términos.
  • El número de líneas debe ser igual al número de señales. La forma autorizada de cambiar los recuentos es el build_merge.py del Paso 3a, que reescribe ambos archivos juntos.

Luego genera el SRT previo al ensamblaje (las marcas de tiempo se heredan de en.full.srt):

cook dub make-srt <output-root> <name>

Autorevisión — dos pasadas, antes de la compuerta mecánica: lee cada línea en voz alta en tu cabeza como una oración hablada (¿suena como algo que una persona diría?); luego escanea la lista de términos a continuación contra cada línea (cualquier cosa en pantalla permanece en inglés, los conceptos estándar van en chino). Estas son tú verificando tu propio trabajo; la revisión del subagente a continuación es la lectura en frío.

Compuerta de longitud — mecánica (aritmética pura, toma un segundo):

cook dub gate <output-root> <name> --rate <chars/s medidos>

Verifica cada línea contra su ventana de señal de tres maneras: líneas cortas (≤8 sílabas — la trampa de ritmo de narración de IndexTTS2, reescribe más completo o deja que Paso 3a fusione), fallos de presupuesto (habla estimada muy por encima de su ventana — el texto debe reducirse — o muy por debajo — la ventana obtiene silencio muerto, llénala), y el total de cobertura (Σ habla estimada / Σ ventanas — una traducción uniformemente delgada pasa todas las verificaciones de línea y aún así dobla en silencios largos; saludable 90-105%). --rate es obligatorio (el valor medido; --self-measure lo deriva de las tomas existentes). La salida 1 enumera las líneas y el veredicto de cobertura; reescribe y vuelve a ejecutar hasta que pase. Una línea por encima del presupuesto se corrige reescribiendo más corta o fusionándola con su vecina con cook dub merge — nunca dejándola para que se comprima con atempo.

Compuerta de calidad — revisión de subagente en abanico (obligatoria, antes de la síntesis). La compuerta auditiva y esta revisión son las únicas compuertas antes de horas de síntesis. Despliega un subagente con acceso de lectura tanto a <name>.en.full.srt como a translations_dub.txt, y pídele que verifique, para cada señal:

  1. Precisión de traducción — ¿el chino transmite fielmente el inglés? Sin cláusulas omitidas, sin contenido añadido (nada que el inglés no diga), sin traducciones incorrectas.
  2. Ortografía de nombres propios — nombres (personas, productos, empresas) exactamente como los usa la fuente. "Claude" no "克劳德", "IndexTTS" no "索引TTS", a menos que exista un nombre chino estándar genuino.
  3. Legibilidad TTS — ¿IndexTTS2 pronunciará esto de forma natural? Sin secuencias de caracteres incómodas, sin puntuación huérfana...

... [truncado para la traducción]

Skills relacionados