Remplacer les voix originales anglaises d'une vidéo par un doublage chinois sur une chronologie identique : la vidéo n'est jamais ré-étalonnée — chaque image conserve son horodatage d'origine et la version doublée a exactement la durée de la vidéo brute. L'audio synthétisé de chaque réplique est inséré dans sa propre fenêtre d'origine (début de la réplique → début de la réplique suivante) avec un atempo par réplique ; une synthèse plus rapide ou plus lente aboutit toutes deux dans la fenêtre. Le résultat est une deuxième version — même image à la même vitesse, audio chinois, sous-titres bilingues ZH+EN incrustés.
Cette compétence effectue les deux parties créatives que la CLI ne peut pas faire : traduire pour le doublage (phrases complètes écrites dans un budget de caractères, pas la fragmentation des sous-titres) et exécuter les contrôles de qualité (vérification de longueur, relecture à froid, vérification auditive, vérification post-incrustation). L'exécution déterministe (Demucs, IndexTTS2, assemblage, réétalonnage, ffmpeg) est gérée par la sous-commande cook dub de la CLI cook. Tous les scripts d'étape vivent dans le paquet cook (stages/dub/) ; chaque invocation ci-dessous est une commande cook. pip install -U video-cook met tout à jour en une seule fois.
Quand utiliser cette compétence
Vous avez une vidéo qui possède déjà :
- Un fichier vidéo brut (
<output-root>/raw/<name>.raw.mp4) — l'original, avec des voix anglaises. - Une exécution de sous-titres bilingues de
video-subtitle— spécifiquementtranscript/<name>.en.full.srt(la transcription anglaise en phrases complètes, fusionnée à partir des fragments whisperX) ettranscript/translations.txt.
Vous voulez une version doublée en chinois. Si vous ne les avez pas encore, exécutez d'abord video-download puis video-subtitle — cette compétence lit leurs sorties.
Ce que vous produisez
Trois produits sont livrés : cooked/<name>.dubbed.mp4 (vidéo brute inchangée, doublage chinois, sous-titres bilingues incrustés — durée identique à la brute), cloud-srt/zh.dub.srt + en.dub.srt (sous-titres de téléchargement). Tout le reste sont des fichiers de travail sous dubbed/ — l'arborescence annotée ci-dessous les cartographie tous.
L'exécution n'est pas terminée tant que les vérifications de l'étape 6 ne sont pas passées.
Disposition des répertoires
Cette compétence ajoute dubbed/ (répertoire de travail) et écrit les produits finaux dans cooked/ et cloud-srt/ :
<output-root>/
├── raw/ ← de video-download (cette compétence le lit)
│ └── <name>.raw.mp4
├── transcript/ ← de video-subtitle (cette compétence lit + ajoute)
│ ├── <name>.en.full.srt ← anglais en phrases complètes (source du script de doublage)
│ ├── translations_dub.txt ← cette compétence écrit : une ligne chinoise par réplique
│ └── <name>.zh.dub.srt ← cette compétence écrit
├── cooked/ ← les vidéos finales vivent ici
│ ├── <name>.cooked.bar.mp4 ← de video-subtitle (inchangé)
│ └── <name>.dubbed.mp4 ← produit de cette compétence
├── cloud-srt/ ← les sous-titres de téléchargement vivent ici
│ ├── zh.srt / en.srt ← de video-subtitle (inchangé)
│ └── zh.dub.srt / en.dub.srt ← sous-titres de téléchargement de cette compétence
└── dubbed/ ← répertoire de travail de cette compétence
├── _reference/
│ └── ref.wav
└── _full/
├── _segments/ ← cache IndexTTS2 par réplique
├── dub.wav ← le doublage sur l'horloge d'origine
├── dubbing.srt ← fichier de travail (ZH, avant raccourcissement)
├── dubbing.short.srt ← fichier de travail (ZH, sortie raccourcie)
├── dubbing.merged.srt ← fichier de travail (ZH, après raccourcissement ; copié vers cloud-srt)
├── dubbing.en.srt ← fichier de travail (EN en phrases complètes)
├── dubbing.en.short.srt / dubbing.en.merged.srt ← ses sorties de raccourcissement et fusion
├── dubbing.bilingual.srt ← fichier de travail (union bilingue ; ce qui est incrusté)
├── dubbing.cooked.ass ← fichier de travail (sortie ass, copiée vers burn.ass)
└── burn.ass ← le ASS réellement incrusté
Règle : dubbed/ est le répertoire de travail ; cooked/<name>.dubbed.mp4 et cloud-srt/{zh,en}.dub.srt sont les produits. Ne touchez jamais à raw/, transcript/<name>.zh.srt, cooked/<name>.cooked.mp4, ou cloud-srt/{zh,en}.srt — ceux-ci appartiennent à video-subtitle. Si cette compétence échoue à mi-chemin, la livraison bilingue cuite est toujours complète.
Le pipeline
Deux étapes après le travail créatif : synth puis assemble, invoquées via 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]
# ou les deux :
cook dub full <root> <name> --python <indextts-venv>/Scripts/python.exe
Les étapes ci-dessous décrivent ce que chaque étape fait en interne (afin que vous puissiez vérifier les sorties et diagnostiquer les échecs) ; les commandes cook dub <stage> sont la façon de les exécuter.
Étape 0 — Résoudre les environnements
Il y a deux environnements Python en jeu, et les confondre est le mode d'échec que cette étape vise à éviter :
- L'environnement de cook (Python système ou
~/.venvs/video-tools/) — où la CLIcookréside, avec whisperX/yt-dlp/torch pour le pipeline de sous-titres. - L'environnement d'IndexTTS2 (
~/Git/index-tts/.venv) — un venv séparé contenantindextts,torch,demucs, etwhisperx(le pipeline de doublage ajoute les deux derniers par-dessus le fichier de verrouillage en amont). Ces dépendances sont lourdes et isolées intentionnellement ; n'essayez pas de les installer dans l'environnement de cook.
cook exécute chaque étape de doublage comme un sous-processus sous le venv IndexTTS2 via --python, donc from indextts import ... s'y résout. Chaque commande cook dub dans cette compétence prend --python <indextts-venv>/Scripts/python.exe. Résolvez le chemin du venv une fois (par défaut ~/Git/index-tts/.venv) et réutilisez-le pour toute l'exécution.
0a. Vérifiez que les deux environnements sont accessibles :
<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. Contrainte mono-thread. IndexTTS2 doit s'exécuter en mono-thread (OMP_NUM_THREADS=1), sinon il produit un audio de mauvaise qualité. cook définit cela en interne avant d'importer torch, donc vous n'avez pas besoin de l'exporter vous-même.
Terminé lorsque le doctor de cook signale que whisperX/yt-dlp/ffmpeg sont installés, que le venv IndexTTS2 importe indextts (v2.5) + demucs + whisperx, et que vous connaissez le chemin absolu vers <indextts-venv>/Scripts/python.exe à passer comme --python.
Étape 1 — Séparer les voix de la vidéo brute
L'audio original est une piste mixte (voix + BGM + SFX). Demucs le sépare afin que nous puissions extraire une référence propre et vérifier le BGM plus tard.
cook dub separate <output-root> <name> [--model htdemucs] --python <indextts-venv>/Scripts/python.exe
Demucs vit dans le venv IndexTTS2, donc --python pointe là-bas. Utilisez htdemucs (modèle unique, ~3GB de RAM), pas htdemucs_ft (sac de 4 modèles, ~20GB de RAM — OOM sur des machines de 32GB). La qualité est légèrement inférieure mais adéquate pour l'extraction de référence.
En mode avant-plan (par défaut), cook déplace les pistes séparées vers dubbed/vocals.wav + dubbed/no_vocals.wav automatiquement ; en mode --detach, vous les déplacez vous-même après l'apparition du marqueur de fin.
Terminé lorsque dubbed/vocals.wav ET dubbed/no_vocals.wav existent tous deux avec une durée correspondant à la brute ±0,5s.
Étape 2 — Extraire le clip de référence
IndexTTS2 a besoin d'un clip propre de 14 à 30 secondes du locuteur original — il lit au maximum les 15 premières secondes, donc une fenêtre pleine capacité est préférable à une fenêtre courte. IndexTTS2 clone la prosodie, pas seulement le timbre : le rythme de délivrance de la fenêtre devient le rythme de délivrance de tout le doublage. Le sélecteur par défaut prend le discours continu le plus long, qui tend à sélectionner le passage le plus lent et le plus délibéré — souvent faux. Préférez le scanner :
cook dub scan-reference <output-root> <name> \
[--full-band] [--python <indextts-venv>/Scripts/python.exe]
Il classe les fenêtres fixes de 15s par taux de syllabes (filtré par densité et pause) et coupe les 3 premiers candidats (plus les copies pleine bande 48k avec --full-band). La densité n'est que la moitié de l'histoire — IndexTTS2 clone aussi la prosodie de la fenêtre, donc classez les candidats également par vivacité : pyin F0 std et gamme de demi-tons par fenêtre (une fenêtre plate double plat ; utilisez librosa.pyin, PAS detect_pitch_frequency, qui signale la voisation sur le silence). Authentifiez tout fichier de référence hérité avant de le réutiliser : un dérivé retraité conserve son embedding de locuteur et ses statistiques de hauteur mais perd la corrélation de forme d'onde, donc une corrélation proche de zéro ne signifie PAS "locuteur différent" — notez-le par rapport à un clip d'ancrage avec cook dub scan-reference --score <file> --anchor <anchor.wav> (l'audio réel du même locuteur atterrit près de 0,94 ; les synthèses dans la bande normale 0,66-0,76) et comparez les statistiques pyin. Quoi qu'il en soit : l'oreille de l'utilisateur choisit la référence, toujours — synthétisez les phrases pilotes une fois par candidat (Étape 3a), présélectionnez chaque prise avec un aller-retour ASR de petit modèle (les mots manquants signalent un charabia ; la sortie en chinois traditionnel signale un accent que la fenêtre transmet à la prise), notez avec cook dub scan-reference --score, puis remettez les wavs adaptés en volume à l'utilisateur.
Terminé lorsque dubbed/_reference/ref.wav existe, fait 14-30s, et n'a pas de silence > 0,3s (ffmpeg -af silencedetect).
Étape 3 — Traduire pour le doublage (l'agent fait cela)
C'est là que le doublage diverge des sous-titres. N'utilisez pas translations.txt (la traduction des sous-titres) — elle suit les coupes de fragments de whisperX, qui divisent les phrases. Le doublage a besoin de phrases complètes pour que le chinois sonne naturellement lorsqu'il est parlé.
Lisez <output-root>/transcript/<name>.en.full.srt (la transcription anglaise en phrases complètes). Traduisez chaque réplique vous-même, en écrivant dans transcript/translations_dub.txt — une ligne chinoise par réplique anglaise, ligne N = réplique N.
La règle du budget de caractères — le cœur de la traduction pour doublage. Le budget de chaque réplique est secondes de fenêtre × taux de parole normal, fixé avant que vous écriviez : la fenêtre est le début de la réplique jusqu'au début de la réplique suivante (fenêtre + pause suivante), le taux est mesuré, jamais supposé — cook dub measure-rate sur les pilotes que l'utilisateur a choisis (procédure : Étape 3a). Un taux supposé passe toutes les vérifications et double en secondes de silence : une conférence de 45 minutes budgétée à un taux supposé de 5,0 caractères/s contre un réel de 6,93 a laissé 83 répliques avec >2,5s de silence et a coûté une re-synthèse de 82 répliques. Écrivez dans le budget : légèrement au-dessus est acceptable, nettement en dessous ne l'est pas — une prise courte garde son rythme naturel et laisse une pause de respiration, donc un texte sous-budgétisé signifie du silence, et un texte sur-budgétisé signifie une compression audible. Re-calibrez sur tout changement de référence ou de facteur de durée : un budget calibré sur une référence retirée survit à toutes les vérifications et double toujours en silence. Synthétisez une sonde stratifiée (5 à 10 répliques typiques par locuteur dans différentes tranches de longueur) et revérifiez le script par rapport aux fenêtres aux taux mesurés — procédure dans REFERENCE.md → "Re-calibrage du taux". Comment remplir un budget légitimement (et ce que "remplir" n'est pas autorisé à inventer) : REFERENCE.md → "Remplir le budget de caractères".
Principes de traduction (au-delà du budget) :
- Traduisez des pensées complètes, pas des fragments. L'anglais est déjà en phrases complètes ; votre réplique chinoise est une pensée complète.
- Gardez les termes techniques en anglais là où les développeurs chinois le font — spec, plan, prototype, agent, token, skill, session, branch, route, etc. Voir REFERENCE.md → "Liste de rétention des termes".
- Gardez l'anglais pour tout ce qui est affiché à l'écran — étiquettes d'interface, code, URL, noms de fichiers. Exemples dans REFERENCE.md → "Liste de rétention des termes".
- Traduisez les concepts qui ont des noms chinois standards lorsqu'ils ne sont pas affichés à l'écran — les exemples travaillés se trouvent dans la liste de rétention des termes.
- Le nombre de lignes doit être égal au nombre de répliques. La manière autorisée de changer les comptes est via
build_merge.pyde l'Étape 3a, qui réécrit les deux fichiers ensemble.
Ensuite, générez le SRT pré-assemblage (horodatages hérités de en.full.srt) :
cook dub make-srt <output-root> <name>
Auto-révision — deux passages, avant la vérification mécanique : lisez chaque ligne à voix haute dans votre tête comme une phrase parlée (est-ce que ça ressemble à quelque chose qu'une personne dirait ?) ; puis parcourez la liste de termes ci-dessous par rapport à chaque ligne (tout ce qui est à l'écran reste en anglais, les concepts standards vont en chinois). Ce sont vous qui vérifiez votre propre travail ; la révision du sous-agent ci-dessous est la lecture à froid.
Vérification de longueur — mécanique (pure arithmétique, prend une seconde) :
cook dub gate <output-root> <name> --rate <chars/s mesurés>
Elle recoupe chaque ligne par rapport à sa fenêtre de réplique de trois manières : lignes courtes (≤8 syllabes — le piège du rythme de narration d'IndexTTS2, réécrivez plus complètement ou laissez l'Étape 3a fusionner), dépassements de budget (parole estimée bien au-dessus de sa fenêtre — le texte doit être réduit — ou bien en dessous — la fenêtre devient du silence, remplissez-la), et le total de couverture (Σ parole estimée / Σ fenêtres — une traduction uniformément mince passe chaque vérification de ligne et double toujours en longs silences ; sain 90-105%). --rate est obligatoire (la valeur mesurée ; --self-measure la redérive des prises existantes). La sortie 1 liste les lignes et le verdict de couverture ; réécrivez et réexécutez jusqu'à ce qu'il réussisse. Une ligne hors budget est corrigée en réécrivant plus court ou en la fusionnant avec sa voisine via cook dub merge — jamais en la laissant être compressée par atempo.
Vérification de qualité — révision par sous-agent en étoile (obligatoire, avant la synthèse). La vérification auditive et cette révision sont les seules vérifications avant des heures de synthèse. Distribuez à un sous-agent avec accès en lecture à <name>.en.full.srt et translations_dub.txt, et demandez-lui de vérifier, pour chaque réplique :
- Précision de la traduction — le chinois transmet-il fidèlement l'anglais ? Pas de clauses omises, pas de contenu ajouté (rien que l'anglais ne dit pas), pas de mauvaises traductions.
- Orthographe des noms propres — noms (personnes, produits, entreprises) exactement comme la source les utilise. "Claude" pas "克劳德", "IndexTTS" pas "索引TTS", sauf si un nom chinois standard existe vraiment.
- Lisibilité TTS — IndexTTS2 prononcera-t-il cela naturellement ? Pas de séquences de caractères étranges, pas de voyelles orphelines...