Enregistrement de vidéos de démonstration automatisées (Playwright)
Mode (à décider EN PREMIER)
Arguments reçus : $ARGUMENTS
| Arguments | Mode | Template |
|---|---|---|
contiennent audio (ou la demande demande une voix / narration parlée) | Narré — voix TTS + sous-titres | record-demo-narrated-template.mjs + narrate.py |
| vides / autre chose | Silencieux — sous-titres uniquement (par défaut) | record-demo-template.mjs |
Mode narré : langue en (par défaut) ou pt (portugais brésilien) ; moteur auto (par défaut : anglais → kokoro hors ligne ; portugais → edge, avec repli sur kokoro hors ligne), ou forcer kokoro / edge. Exemples : audio, audio pt, audio pt kokoro, audio en edge. Suivez également le Mode narré ci-dessous.
Rédigez les sous-titres et la narration dans la langue choisie (en sauf si pt a été donné ou si l’utilisateur écrit en portugais).
Aperçu
Une vidéo de démonstration est du code, pas une capture manuelle : un script Playwright pilote le navigateur avec un enregistrement natif, des sous-titres superposés et des mises en évidence visuelles. Enregistrable à nouveau à chaque modification du produit (npm run demo:record), avec un rythme et un récit maîtrisés.
Principe fondamental : la vidéo est un pitch, pas un test E2E. Chaque scène existe pour prouver un argument — et le travail n’est terminé qu’après avoir VÉRIFIÉ la vidéo générée image par image.
Quand l’utiliser / ne pas l’utiliser
- ✅ Démo/POC web pour investisseurs, clients, parties prenantes, onboarding
- ✅ Documenter un flux de bout en bout qui évolue (ré-enregistrable)
- ✅ Avec une voix : mode narré (TTS local/hors ligne) — voir ci-dessus
- ❌ Tests automatisés (utilisez Playwright Test) · ❌ applications non web · ❌ capturer le son propre de l’application (Playwright n’enregistre pas l’audio du navigateur)
Processus
- État déterministe d’abord. La vidéo effectue des actions réelles (connexions, clics, écritures en base). Assurez-vous d’avoir des données seed/démo idempotentes et des comptes dédiés avant l’enregistrement. Lancez le serveur en mode production (
build && start) ou préchauffez les routes en développement — la compilation à la demande apparaît comme un écran blanc dans la vidéo. - Écrivez le script AVANT de coder. Un arc de pitch en 4 actes : la vision (ouverture) → comment ça marche (le différenciateur, à l’intérieur) → l’expérience utilisateur final → la preuve/résultat (conclure sur la vision). Chaque scène = 1 argument + 1 sous-titre (titre fort + sous-texte explicatif). Objectif : 60–120s au total, 3–5s sur chaque écran clé.
- Adaptez le template — des aides prêtes pour les sous-titres, les mises en évidence, la connexion, le rythme et la conversion. Utilisez les couleurs de la marque du projet pour le sous-titre.
- Enregistrez, et traitez les échecs de sélecteur comme un script cassé (corrigez-le, ne contournez pas).
- VÉRIFIEZ la vidéo — obligatoire. Durée et scènes :
Ne livrez qu’après inspection des images. Si une scène a échoué, réenregistrez.ffprobe -v error -show_entries format=duration -of csv=p=0 demo.mp4 # capturez 1 image par scène et REGARDEZ chacune (bon sous-titre ? bon écran ? rien de cassé ?) ffmpeg -y -ss <t> -i demo.mp4 -frames:v 1 -vf scale=800:-1 frame_<t>.png - Livrez le
.mp4(fonctionne dans PowerPoint/Slack/WhatsApp) en plus du.webm. Laissez un script npm (demo:record) et ignorez la sortie dans git (elle est régénérable).
Mode narré (audio)
Même processus que ci-dessus, avec ces différences :
- Prérequis :
uvetffmpegdans le PATH (uv --version,ffmpeg -version). Copieznarrate.pydans le MÊME dossier que le script d’enregistrement. La première exécution télécharge le modèle Kokoro (~120 Mo, mis en cache dans~/.cache/recording-demo-videos). - Le rythme vient de l’audio. Chaque scène dans
SCRIPTacaption(écran) +say(parole, 1–2 phrases, ton du pitch).await narrate(page, "id")affiche le sous-titre, place la parole et attend qu’elle se termine — n’utilisez pas depause()fixe sur les scènes parlées. Parler pendant une interaction :narrate(page, id, { wait: false }); le prochainnarrate()attend la parole précédente (pas de chevauchement). - Langue/voix via des variables d’environnement :
DEMO_LANG=en|pt,DEMO_TTS=auto|kokoro|edge,DEMO_VOICE=<voix>(Kokoro :af_heart,am_michael,bf_emma,pf_dora,pm_alex; edge :en-US-AvaMultilingualNeural,pt-BR-FranciscaNeural…).kokoro= hors ligne, Apache-2.0, par défaut pour l’anglais ; son portugais est sensiblement moins naturel.edge= par défaut pour le portugais, meilleures voix, mais en ligne via un point de terminaison Microsoft non officiel — acceptable pour des démos internes ; pour un usage commercial, préférezkokoroou un TTS officiel. - La synchronisation est automatique : le template s’ouvre avec un flash magenta (
syncMarker), mesure le vrai délai de démarrage de la vidéo et coupe le flash. GardezsyncMarkercomme première action ; si le log indique « Sync marker not found », la voix sera désynchronisée — vérifiez quesyncMarkerest la première action et que rien ne couvre l’écran dans la première seconde, puis réenregistrez. - Sortie :
demo.mp4(narré — le livrable),demo.srt,timeline.json,demo.raw.webm(brut, silencieux),audio/(cache : le réenregistrement ne resynthétise que les lignes modifiées). - Vérification supplémentaire (obligatoire) : en plus des images, confirmez que la voix tombe au bon endroit :
ffprobe -v error -show_entries stream=codec_type,duration -of csv=p=0 demo.mp4 # doit avoir vidéo ET audio ffmpeg -i demo.mp4 -af silencedetect=n=-45dB:d=0.6 -f null - 2>&1 | grep silence_end # chaque silence_end ≈ l’heure d’une scène dans timeline.json (±0.2s) ; capturez l’image à cet instant et vérifiez le sous-titre
Pièges (appris à la dure)
| Piège | Correction |
|---|---|
| La vidéo a un nom de fichier aléatoire | Récupérez page.video() AVANT de fermer ; await video.path() APRÈS context.close() ; renommez |
| Le sous-titre disparaît lors de la navigation | Réinjectez le sous-titre après chaque goto (helper caption()), ou utilisez addInitScript + sessionStorage |
| Vidéo trop rapide pour les humains | slowMo: 100–150 + pauses explicites de 3–5s par scène + pressSequentially({ delay: 40–60 }) pour la frappe visible |
| mp4 ne s’ouvre pas dans PowerPoint/lecteurs | ffmpeg -c:v libx264 -pix_fmt yuv420p |
| Vidéo plus petite/recadrée | recordVideo.size DOIT être égal à viewport |
| Écran blanc/en attente dans la vidéo | waitUntil: "networkidle" + waitForURL après les actions ; préchauffez les routes en développement |
| Le spectateur ne sait pas où regarder | Surbrillance ambre (box-shadow) sur l’élément avant de cliquer, ou un curseur factice injecté |
| Le réenregistrement duplique les données | Actions idempotentes ou seed qui tolère les réexécutions (slugs suffixés, upserts) |
| La connexion fuit entre les personnages | context.clearCookies() entre les changements d’utilisateur |
| (narré) Voix désynchronisée de l’écran | La vidéo de Playwright commence 0,2–1,5s après la page (varie selon l’exécution/version) — utilisez le syncMarker du template, jamais un décalage fixe |
| (narré) Voix coupée ou scènes bâclées | pause() fixe sur une scène parlée — remplacez par narrate(), qui attend la parole |
| (narré) Acronyme/marque mal prononcé | Épelez-le phonétiquement dans say (par exemple « S Q L » ou « sequel ») ; le sous-titre reste correct |
Erreurs courantes
- Sauter la vérification des images (« ça s’est exécuté sans erreur » ≠ « la vidéo est bonne ») — regardez toujours.
- Les sous-titres décrivent l’interface (« écran de connexion ») au lieu de raconter l’argument (« l’équipe se connecte — contrôle fin des rôles »).
- Enregistrer sans scénario écrit : vous obtenez un long screencast sans histoire.
- Informations d’identification réelles en dur — utilisez des comptes démo/seed, injectez via des variables d’environnement si sensibles.