Automatisierte Demo-Videos aufnehmen (Playwright)
Modus (Zuerst entscheiden)
Argumente empfangen: $ARGUMENTS
| Argumente | Modus | Vorlage |
|---|---|---|
enthalten audio (oder die Anfrage verlangt Sprachausgabe / gesprochenen Kommentar) | Mit Sprachausgabe — TTS-Stimme + Untertitel | record-demo-narrated-template.mjs + narrate.py |
| leer / alles andere | Stumm — nur Untertitel (Standard) | record-demo-template.mjs |
Modus mit Sprachausgabe: Sprache en (Standard) oder pt (Brasilianisches Portugiesisch); Engine auto (Standard: Englisch → kokoro offline; Portugiesisch → edge, fallback auf kokoro offline) oder erzwinge kokoro / edge. Beispiele: audio, audio pt, audio pt kokoro, audio en edge. Beachte auch den Abschnitt Modus mit Sprachausgabe unten.
Schreibe Untertitel und Kommentar in der gewählten Sprache (en außer wenn pt angegeben wurde oder der Benutzer auf Portugiesisch schreibt).
Überblick
Ein Demo-Video ist Code, keine manuelle Aufnahme: Ein Playwright-Skript steuert den Browser mit nativer Aufnahme, eingeblendeten Untertiteln und visuellen Highlights. Es ist bei jeder Änderung des Produkts wiederholbar (npm run demo:record), mit kontrolliertem Tempo und einer klaren Geschichte.
Grundprinzip: Das Video ist ein Pitch, kein E2E-Test. Jede Szene dient dazu, ein Argument zu belegen – und die Arbeit ist erst erledigt, nachdem das generierte Video Frame für Frame ÜBERPRÜFT wurde.
Wann verwenden / nicht verwenden
- ✅ Webprodukt-POC/-Demo für Investoren, Kunden, Stakeholder, Onboarding
- ✅ Dokumentation eines sich ständig weiterentwickelnden End-to-End-Flows (wiederholbar)
- ✅ Mit Sprachausgabe: Modus mit Sprachausgabe (lokal/offline TTS) – siehe oben
- ❌ Automatisierte Tests (Playwright Test verwenden) · ❌ Nicht-Web-Apps · ❌ Aufnahme des eigenen Tons der App (Playwright kann Browser-Audio nicht aufnehmen)
Ablauf
- Deterministischer Zustand zuerst. Das Video führt echte Aktionen aus (Logins, Klicks, DB-Schreibvorgänge). Stelle vor der Aufnahme idempotente Seed-/Demo-Daten und dedizierte Konten bereit. Starte den Server im Produktionsmodus (
build && start) oder wärme in der Entwicklung Routen vor – Lazy Compilation zeigt sich als weißer Bildschirm im Video. - Schreibe das Skript BEVOR du programmierst. Ein Pitch-Ablauf in 4 Akten: die Vision (Eröffnung) → wie es funktioniert (das Unterscheidungsmerkmal, im Detail) → das Benutzererlebnis → der Beweis/das Ergebnis (Abschluss mit der Vision). Jede Szene = 1 Argument + 1 Untertitel (starke Überschrift + erklärender Untertext). Ziel: 60–120s insgesamt, 3–5s pro wichtigem Bildschirm.
- Passe die Vorlage an – fertige Hilfsmittel für Untertitel, Highlights, Login, Tempo und Konvertierung. Verwende die Markenfarben des Projekts für den Untertitel.
- Nimm auf und behandle Selektorfailures als defektes Skript (repariere es, umgehe es nicht).
- ÜBERPRÜFE das Video – obligatorisch. Dauer und Szenen:
Erst nach der Inspektion der Frames ausliefern. Wenn eine Szene fehlgeschlagen ist, neu aufnehmen.ffprobe -v error -show_entries format=duration -of csv=p=0 demo.mp4 # nimm 1 Frame pro Szene und SCHAU es dir an (richtiger Untertitel? richtiger Bildschirm? nichts kaputt?) ffmpeg -y -ss <t> -i demo.mp4 -frames:v 1 -vf scale=800:-1 frame_<t>.png - Liefere
.mp4aus (funktioniert in PowerPoint/Slack/WhatsApp) zusätzlich zum.webm. Hinterlasse ein npm-Skript (demo:record) und ignoriere die Ausgabe via gitignore (sie ist regenerierbar).
Modus mit Sprachausgabe (audio)
Gleicher Ablauf wie oben, mit diesen Unterschieden:
- Voraussetzungen:
uvundffmpegim PATH (uv --version,ffmpeg -version). Kopierenarrate.pyin den GLEICHEN Ordner wie das Aufnahmeskript. Der erste Lauf lädt das Kokoro-Modell herunter (~120 MB, zwischengespeichert in~/.cache/recording-demo-videos). - Das Tempo kommt vom Audio. Jede Szene in
SCRIPThatcaption(Bildschirm) +say(Sprache, 1–2 Sätze, Ton des Pitches).await narrate(page, "id")zeigt den Untertitel, platziert die Sprache und wartet auf deren Ende – verwende kein festespause()für gesprochene Szenen. Sprechen während einer Interaktion:narrate(page, id, { wait: false }); das nächstenarrate()wartet auf die vorherige Sprache (keine Überlappung). - Sprache/Stimme per Umgebungsvariable:
DEMO_LANG=en|pt,DEMO_TTS=auto|kokoro|edge,DEMO_VOICE=<voice>(Kokoro:af_heart,am_michael,bf_emma,pf_dora,pm_alex; edge:en-US-AvaMultilingualNeural,pt-BR-FranciscaNeural…).kokoro= offline, Apache-2.0, Standard für Englisch; sein Portugiesisch ist deutlich weniger natürlich.edge= Standard für Portugiesisch, beste Stimmen, aber online über einen inoffiziellen Microsoft-Endpunkt – für interne Demos in Ordnung; für kommerzielle Nutzung bevorzugekokorooder einen offiziellen TTS-Dienst. - Synchronisation ist automatisch: Die Vorlage öffnet mit einem magentafarbenen Blitz (
syncMarker), misst die reale Verzögerung des Videostarts und schneidet den Blitz ab. LassesyncMarkerals erste Aktion; wenn das Log "Sync marker not found" sagt, wird die Stimme nicht synchron sein – überprüfe, dasssyncMarkerdie erste Aktion ist und nichts den Bildschirm in der ersten Sekunde verdeckt, dann nimm neu auf. - Ausgabe:
demo.mp4(mit Sprachausgabe – das auslieferbare Format),demo.srt,timeline.json,demo.raw.webm(roh, stumm),audio/(Zwischenspeicher: erneute Aufnahme synthetisiert nur geänderte Zeilen neu). - Zusätzliche Überprüfung (obligatorisch): Über die Frames hinaus bestätige, dass die Stimme an der richtigen Stelle landet:
ffprobe -v error -show_entries stream=codec_type,duration -of csv=p=0 demo.mp4 # muss VIDEO und AUDIO haben ffmpeg -i demo.mp4 -af silencedetect=n=-45dB:d=0.6 -f null - 2>&1 | grep silence_end # jedes silence_end ≈ eine Szene bei atVideo in timeline.json (±0.2s); nimm den Frame zu diesem Zeitpunkt und überprüfe den Untertitel
Fallstricke (auf die harte Tour gelernt)
| Fallstrick | Behebung |
|---|---|
| Video bekommt einen zufälligen Dateinamen | page.video() VOR dem Schließen abrufen; await video.path() NACH context.close(); umbenennen |
| Untertitel verschwindet bei Navigation | Untertitel nach jedem goto neu einfügen (caption()-Helfer), oder addInitScript + sessionStorage |
| Video für Menschen zu schnell | slowMo: 100–150 + explizite 3–5s Pausen pro Szene + pressSequentially({ delay: 40–60 }) für sichtbares Tippen |
| mp4 lässt sich in PowerPoint/Playern nicht öffnen | ffmpeg -c:v libx264 -pix_fmt yuv420p |
| Video kleiner/beschnitten | recordVideo.size MUSS gleich viewport sein |
| Weißer/Bereit-Bildschirm im Video | waitUntil: "networkidle" + waitForURL nach Aktionen; Routen in der Entwicklung vorwärmen |
| Betrachter weiß nicht, wohin er schauen soll | Bernsteingelbes Highlight (box-shadow) auf dem Element vor dem Klick, oder ein eingefügter Fake-Cursor |
| Erneute Aufnahme erzeugt Duplikate | Idempotente Aktionen oder ein Seed, der Wiederholungen toleriert (Slugs mit Suffix, Upserts) |
| Login-Daten zwischen Personen durchsickern | context.clearCookies() zwischen Benutzerwechseln |
| (mit Sprachausgabe) Stimme nicht synchron mit dem Bildschirm | Playwrights Video startet 0.2–1.5s nach der Seite (variiert je nach Lauf/Version) – verwende das syncMarker der Vorlage, niemals einen festen Offset |
| (mit Sprachausgabe) Stimme abgeschnitten oder Szenen überhastet | Festes pause() in einer gesprochenen Szene – ersetze durch narrate(), das auf die Sprache wartet |
| (mit Sprachausgabe) Akronym/Marke falsch ausgesprochen | Buchstabiere es phonetisch in say (z. B. "S Q L" oder "Sequel"); der Untertitel bleibt korrekt |
Häufige Fehler
- Überspringen der Frame-Überprüfung ("es ist ohne Fehler gelaufen" ≠ "das Video ist gut") – immer nachschauen.
- Untertitel beschreiben die UI ("Anmeldebildschirm") statt das Argument zu erzählen ("Das Team meldet sich an – feingranulare Rollensteuerung").
- Aufnahme ohne schriftliches Skript: du bekommst einen langen Screencast ohne Geschichte.
- Echte Zugangsdaten fest codiert – verwende Demo-/Seed-Konten, injiziere sie bei sensiblen Daten über Umgebungsvariablen.