Communitygithub.com

Vidéo A/B narrée

Réalise une vidéo de démonstration A/B d'une PR ou d'une fonctionnalité : enregistre chaque côté avec Playwright, ajoute des cartes titres/evidence/verdict et des bannières A/B, voice chaque segment avec `say` (macOS) ou une voix IA, accélère les attentes de modèle, gèle la dernière image pour synchroniser la narration, et concatène le tout en un seul fichier MP4.

Qu'est-ce que B narrée ?

Cette compétence d'agent IA (compatible Claude Code, Cursor, Codex, etc.) permet de produire automatiquement une vidéo de démonstration A/B narrée à partir d'une Pull Request ou d'une fonctionnalité. Elle utilise Playwright pour enregistrer les deux versions (A et B) dans un navigateur, génère des cartes visuelles (titre, evidence, verdict) avec des bannières A/B, et ajoute une voix off segment par segment via la commande `say` de macOS ou un modèle vocal IA comme ElevenLabs. La vidéo finale est assemblée avec `ffmpeg` et optimisée pour les démos d'équipe : les temps d'attente du modèle sont accélérés (par défaut 10x), la dernière image est figée jusqu'à la fin de la narration, et tout le processus est scripté en Bash/Bun/Python. La compétence gère plusieurs cas complexes : saisie dynamique (codes OTP, IDs), sélecteurs role/CSS, dépassement de la narration sur le clip, récupération de clips perdus, et vérification préalable des résultats pour éviter de filmer des échecs. Elle nécessite macOS Apple Silicon, Bun, ffmpeg, Python 3 et Playwright ; des alternatives existent pour d'autres systèmes.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/sblattj/agent-chandlery/tree/main/skills/narrated-ab-video

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait B narrée ?

Déclencheurs supplémentaires

présentation vidéo de la PR, faire un enregistrement de démo, démo avec voix off, enregistrer les deux piles, vidéo explicative, filtre drawtext manquant, Playwright recordVideo, shell headless manquant

Un MP4 par jeu d'affirmations : carte titre, clip A, clip B, carte evidence, carte verdict, chaque segment est voice.

Conçu pour macOS sur Apple Silicon (say, le cache Playwright dans ~/Library/Caches/ms-playwright, chrome-mac-arm64). Ailleurs, définissez CHROMIUM_EXE et utilisez --eleven pour la voix. Nécessite Bun, ffmpeg et Python 3.

Procédure

S=${SKILL_DIR:-~/.claude/skills/narrated-ab-video}
$S/preflight.sh /tmp/vid                                  # chromium, playwright ffmpeg, ffmpeg, say voice
bun $S/record.ts a.json raw/a-run                         # un scénario par côté (copier examples/scenario.json)
bun $S/record.ts b.json raw/b-run
bun $S/cards.ts cards.json cards                          # PNGs titre/evidence/verdict + bannière-A/B.png
python3 $S/build.py plan.json                             # [--voice Samantha] [--rate 182]
open -a "QuickTime Player" demo.mp4                       # ouvre en pause ; ne le lisez jamais vous-même

Exemples dans examples/: scenario.json (étapes : wait, select, click, clickIfPresent, type, send, waitStable, expand, fetchText, waitText, probeEnabled, reload ; voir l'en-tête de record.ts), cards.json, plan.json. Les chemins du plan sont relatifs au fichier plan. Validé de bout en bout sur une construction de 4 segments, 40s à partir de deux vrais clips.

Pourquoi c'est construit ainsi (frictions identifiées)

  • Le shell headless épinglé de Playwright n'est souvent pas téléchargé. record.ts et cards.ts lancent la version complète chromium-* la plus récente dans le cache (ou $CHROMIUM_EXE) au lieu d'échouer.
  • recordVideo nécessite sa propre version de ffmpeg. Son absence signifie pas de webm. preflight lit la révision depuis browsers.json et exécute bunx playwright install ffmpeg.
  • ffmpeg de Homebrew peut manquer drawtext. Les bannières sont des PNG transparents générés par cards.ts, superposés dans build.py. Pas de recherche de polices ou de filtres.
  • Les réponses du modèle prennent 10 à 60 secondes de silence. send marque "envoyé" et waitStable marque "répondu". build.py joue cet intervalle à speed (défaut 10x) et le reste à 1x.
  • La narration est généralement plus longue que le clip. build.py gèle la dernière image (tpad stop_mode=clone) jusqu'à la fin de la voix, de sorte qu'une réponse reste à l'écran pendant qu'elle est expliquée.
  • Entrée dynamique (codes OTP, IDs d'un tour précédent) : mettez {{fn:NAME}} dans une étape type et exportez NAME(page, state) depuis hooks.ts à côté du scénario. state.lastJson contient le dernier JSON POST correspondant à captureJsonFrom.
  • Sélecteurs : les boutons sont appariés par rôle et nom regex ("Outils utilisés", "^Envoyer$"). Le CSS est uniquement pour les inputs et les selects. Trouvez les noms avec une capture d'écran rapide avant de scripter.
  • La concaténation nécessite des encodages identiques. Chaque segment est ré-encodé avec le même codec, débit et configuration audio, puis assemblé avec -c copy.
  • Meilleure voix : ELEVENLABS_API_KEY=... build.py plan.json --eleven <voice_id> (modèle eleven_multilingual_v2 ; les clips sont mis en cache par hash voix+texte, donc les réexécutions ne consomment pas de caractères). Gardez la clé dans un fichier 0600, jamais dans le plan, et supprimez-la après.
  • Démo simple face (sans A/B) : enregistrez un scénario, donnez une seule bannière à cards.json, et découpez le même clip en plusieurs segments du plan, une affirmation chacun, avec des spans accélérant les attentes (ex. 5x). Même construction, pas de côté A.
  • L'utilisateur peut coller juste <voice_id> <key>. Cela signifie reconstruire avec --eleven <voice_id>. Une voix plus lente étire la vidéo (72s avec say devient 92s), car build.py gèle la dernière image jusqu'à la fin de la voix.
  • Re-mettre en place une démo démontée avant le tournage : l'authentification à courte durée de vie (JWT locaux, URL de tunnel, ressources cloud temporaires) est généralement morte le lendemain. Re-vérifiez avec un appel API avant d'enregistrer ; un "401 JWT token expiré" aurait filmé un tour dont les résultats n'arrivent jamais.
  • Clip brut perdu : découpez les images de ce côté depuis un MP4 de sortie précédent (trouvez son span en échantillonnant la couleur du coin de la bannière), sauvegardez-le dans <dir>/a.webm avec -f mp4 et un marks.json vide, et utilisez un span explicite. Supprimez la banner car elle est intégrée.
  • Audio : say -o écrit uniquement des fichiers. Ne lisez jamais la vidéo ou l'audio sans l'accord explicite de l'utilisateur. Ouvrez-la en pause et remettez-la.
  • Prouvez avant de filmer. Exécutez l'A/B et vérifiez le résultat (API ou log) d'abord. La vidéo montre un résultat prouvé ; ce n'est pas le test.
  • Les cartes doivent correspondre à la taille du clip. build.py ne redimensionne pas. Un enregistrement avec "viewport": [1440, 900] nécessite "size": [1440, 900] dans cards.json, sinon la concaténation aura des tailles d'image mélangées.
  • Les UIs asynchrones ont besoin de marques, pas d'attentes fixes. waitText marque quand une regex apparaît (ou timeout:<mark>), probeEnabled avec waitMs interroge un bouton et enregistre probes[] dans marks.json, tryMs sur type/send/click marque skipped:<step> au lieu d'annuler, de sorte qu'un côté "reste désactivé" termine quand même le clip.
  • Sondez le même élément que celui sur lequel vous cliquez. Une sonde CSS (button[aria-label=...]) a touché un bouton correspondant différent de celui du rôle-nom send et a lu 'désactivé' alors que l'envoi passait ; utilisez "role" dans probeEnabled.
  • Les superpositions en mode développement (query devtools, toasts d'erreur) apparaissent dans le clip. Ajoutez {"clickIfPresent": "^Fermer .* devtools$"} après la première attente.
  • Attendre une réponse qui ne se "stabilise" jamais : un badge de temps écoulé en direct modifie le texte du corps toutes les secondes. Vérifiez marks.json pour une marque replied avant de construire.
  • Vérifiez que la preuve est dans un texte visible. Les cartes d'outils réduites peuvent ne pas se développer (expand clique silencieusement sur rien). Lisez marks.json body pour la chaîne de l'affirmation. Si elle manque, changez le prompt ou prouvez-la sur une carte à la place.
  • Prouvez la parité avant de filmer une affirmation "la base échoue". Une fois, une base locale a échoué pour une raison (un flag activé localement, désactivé à distance) que l'environnement réel ne rencontre jamais, et la narration l'a affirmé. Faites correspondre d'abord les flags distants, puis filmez.
  • Carte titre : commits testés et flags de chaque côté. Une affirmation par segment de clip. Terminez par une carte verdict.

Liens connexes

  • narrated-screenshot : un seul visuel annoté au lieu d'une vidéo.
  • pr-ab-evidence : la méthode rouge/vert A/B dont cette vidéo est le résultat filmé.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Skills associés