Faire une vidéo avec voix off à partir de code
Aperçu
Une vidéo = script finalisé → TTS phrase par phrase → vérification ASR phrase par phrase → timeline → animation HTML → captures d'écran image par image → montage ffmpeg.
Principe fondamental : la timeline provient de l'audio. D'abord, enregistrer et vérifier chaque phrase, mesurer les temps de début et de fin réels de chaque segment de sous-titres, puis accrocher toutes les animations visuelles à ces horodatages. Les durées ne sont pas codées en dur dans les images ; si le doublage change, on relance la timeline. Le rendu consiste à prendre des captures d'écran de la page à chaque __seek(t), le résultat est déterministe et reproductible.
Ce qui convient : vidéos explicatives/tutoriels (2–5 minutes), vidéos promotionnelles de produits (20–40 secondes), vidéos éducatives avec sous-titres et chapitres. Ce qui ne convient pas : montage avec présence humaine, vidéos nécessitant des prises de vue réelles, clips musicaux purs.
Processus
Chaque étape a une porte de contrôle ; ne pas passer à l'étape suivante tant que la précédente n'est pas validée.
| # | Étape | Résultat | Porte de contrôle | Référence |
|---|---|---|---|---|
| 0 | Initialisation | Répertoire de travail | bash scripts/init.sh <dir> réussi | ci-dessous |
| 1 | Choix du sujet, vérification, script | script.md, audio/make_script.py | Faits vérifiés (tester si expérimental) ; accord explicite de l'utilisateur sur le script avant de commencer le doublage | references/script.md |
| 2 | Doublage | audio/raw/NN.wav → audio/clips/NN.wav (×1.3) | Chaque phrase vérifiée par ASR ; les phrases problématiques sont réenregistrées | references/voice.md |
| 3 | Timeline | build/narration.wav, build/timeline.json | La segmentation des sous-titres correspond à la voix | references/timeline.md |
| 4 | Contenu | Captures d'écran d'interfaces réelles, sorties terminal réelles | Cohérent avec la narration ; utiliser example.com pour les exemples de domaines | references/capture.md |
| 5 | Composition | comp/index.html | Vérifier chaque image composite image par image | references/composition.md |
| 6 | Rendu | dist/video.mp4 | Durée = timeline, niveau sonore -14 LUFS | references/render.md |
| 7 | Vignettes, texte | dist/cover-bili.png, dist/cover-douyin.png, dist/copy.txt | Vignettes dans la zone de recadrage sécurisée de chaque plateforme | references/publish.md |
| 8 | Livraison, nettoyage | Envoyer à l'utilisateur ; archiver les fichiers sources | Ne nettoyer qu'après confirmation de sauvegarde par l'utilisateur | ci-dessous |
Initialisation
bash scripts/init.sh ~/work/<projet>/video-<numéro> copie template/ dans le répertoire de travail, copie scripts/ dans tools/, installe playwright-core. Le template est une démo de 30 secondes qui fonctionne directement ; remplacer ces contenus d'exemple avant de commencer :
script.md,audio/make_script.py: script du numéro en cours ; les chapitres sont définis une seule fois dansCHAPTERSdemake_script.py; les pauses entre chapitres, les barres de chapitre et les horodatages de chapitre en sont déduits.audio/direction.txt: description de la scène pour la direction vocale TTS.asr-prompt.txt: glossaire du numéro (écriture standard, prononciation si nécessaire).comp/index.html: scènes et animations ;comp/cover-*.html: contenu des vignettes.
Commandes courantes
Exécuter dans le répertoire de travail :
| Commande | Rôle |
|---|---|
python3 audio/make_script.py | Génère audio/script.json et audio/lines.json |
python3 tools/tts.py audio/lines.json audio/raw | Doublage phrase par phrase ; réutilise les fichiers audio existants selon le texte, relancer directement après modification du script |
bash tools/speed.sh audio/raw audio/clips 1.3 | Accélération sans changement de ton |
python3 tools/asr.py audio/clips --en | Reconnaissance phrase par phrase : mode simple / avec prompt terminologique / anglais |
python3 tools/takes.py 3,7 3 | Enregistre 3 prises pour les phrases 3 et 7, et les note |
python3 tools/takes.py audio/var.json 2 | 2 prises pour chaque variante de la même phrase |
python3 tools/tts.py audio/lines.json audio/raw --adopt 3 audio/takes/03-0-1.raw.wav | Adopte une prise spécifique |
python3 tools/build_audio.py audio/clips build audio/script.json | Assemble le doublage et génère la timeline |
python3 tools/align_chunks.py audio/clips build/timeline.json audio/script.json | Aligne les segments de sous-titres sur la prononciation réelle |
node tools/render.mjs --timeline=build/timeline.json --stills=2,8.5,20 | Capture d'images clés dans build/stills (vide d'abord) |
bash tools/contact.sh build/stills build/sheets | Compositing 2×2, visualiser avec Read |
bash tools/render-par.sh build/timeline.json build/narration.wav dist/video.mp4 8 | Rendu parallèle complet avec 8 processus et mixage audio |
node tools/render.mjs --html=comp/cover-4x3.html --width=1440 --height=1080 --png=dist/cover-bili.png | Génère une vignette |
python3 tools/bili_kw.py "configuration ssl nginx" "https baota" | Recherche de mots-clés pour le choix du sujet (top 6 vues sur B站) |
Dépendances : ffmpeg, Node 18+, Google Chrome local, Python 3 + requests + openai-whisper (modèle small). TTS via MiMo, clé lue depuis la variable d'environnement MIMO_API_KEY ou le fichier pointé par MIMO_ENV_FILE, sinon demander à l'utilisateur ; autre service TTS acceptable tant que chaque phrase produit un fichier WAV.
Points d'erreur les plus fréquents
- Doubler sans approbation. Envoyer d'abord le script à l'utilisateur ; "continuer" / "commencer" / "OK" vaut accord. Si la formulation a été modifiée pour la prononciation, lister les changements à la livraison.
- Sous-titres et doublage écrits de la même manière. Les sous-titres sont pour les humains (
Nginx,443,1.3), le doublage pour la TTS (Engine X,quatre quatre trois,un virgule trois). Dansmake_script.py, utiliserP(sous_titre, prononciation)pour séparer ; la concaténation des prononciations doit être égale au texte TTS de la phrase entière. - Écouter uniquement le mix final. La reconnaissance du mix complet mélange les termes anglais ; faire une reconnaissance phrase par phrase. Pour choisir une prise, ne regarder que le score plain et le résultat
--en; le mode avancé avec promptprime"corrige" même les erreurs, les règles de décision sont dans voice.md. - Graver des durées en dur dans les animations. Après la première phrase, toutes les animations utilisent
T(phrase, segment); la durée change si l'on accélère ou réenregistre. - Page vide au début. La page reste vide 2–3 secondes après l'apparition de la carte, donnant l'impression de blocage ; le contenu doit apparaître dès le premier segment de sous-titres du chapitre.
- Plusieurs animations
fill: bothsur le même élément, id en double. WAAPI ne signale pas d'erreur ; l'élément peut disparaître ou rester dans un état erroné, voir composition.md. - Ouvrir la page avec
file://pour le rendu. Les CSS mask notamment échouent ;render.mjsintègre un serveur HTTP local. - Vignette rognée par la plateforme. B站 recadre une même image en 4:3 et 16:9, Douyin recadre 9:16 en 3:4 ; ne mettre le contenu que dans la zone de sécurité.
- Redirection interdite. Ne pas inclure dans l'image, la vignette ou le texte de publication d'URL réelles, de QR codes, de coordonnées ; ne pas écrire "vous pouvez voir / chercher". Les domaines réservés comme
example.compeuvent apparaître dans les démos et terminaux. Les vidéos publiées sur plusieurs plateformes ne doivent pas utiliser une plateforme de contenu comme exemple. Les vidéos promotionnelles ne mentionnent pas de prix. - Annoncer le succès avant de voir le résultat. Envoyer les messages de progression après la fin de la commande et après avoir vu la sortie.
Livraison et nettoyage
- Livraison : vidéo finale, vignette B站 4:3, vignette Douyin 9:16,
dist/copy.txt(titre, tags, description, horodatages des chapitres). Par défaut, la vidéo B站 est aussi publiée sur Douyin et autres plateformes ; donc la vignette verticale et le texte Douyin sont fournis ensemble. Si l'utilisateur précise une seule plateforme, ne produire que ce nécessaire. Lister tous les changements de formulation pour la prononciation et les horodatages douteux à faire vérifier par l'utilisateur. - Quand le fichier dépasse la limite de l'outil de chat (souvent 30 Mo), envoyer d'abord une version de prévisualisation avec
crf 22et indiquer où trouver la version haute définition. Si l'envoi du gros fichier a expiré et a été renvoyé, prévenir l'utilisateur qu'il a peut-être reçu deux exemplaires. - Après confirmation par l'utilisateur que les fichiers sont sauvegardés : archiver les sources réutilisables (
comp/et contenu utilisé,audio/make_script.py, spec de variantes,script.md,copy.txt,direction.txt,asr-prompt.txt, scripts de capture,lab/) dans le dossier de templates de l'utilisateur (si non défini, demander où, par exemple~/work/<projet>/video-template/<numéro>/), vérifier avecdiff -rque tout est identique, puis supprimer le répertoire de travail, le profil de navigateur temporaire, les conteneurs, le serveur de développement (tuer par PID après vérification) et les dépendances front-end installées uniquement pour les captures. Conserver le cache du modèle whisper et l'image Docker.