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íficamentetranscript/<name>.en.full.srt(la transcripción completa en inglés, fusionada a partir de fragmentos de whisperX) ytranscript/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 CLIcook, con whisperX/yt-dlp/torch para el pipeline de subtítulos. - Entorno de IndexTTS2 (
~/Git/index-tts/.venv) — un venv separado que contieneindextts,torch,demucsywhisperx(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.pydel 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:
- 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.
- 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.
- Legibilidad TTS — ¿IndexTTS2 pronunciará esto de forma natural? Sin secuencias de caracteres incómodas, sin puntuación huérfana...
... [truncado para la traducción]