Skip to main content

launch-video

Produces sideshow release/feature videos by driving the real viewer in a recording Chromium against a live server — 1920x1080 stage with window chrome, captions, and title cards, encoded with ffmpeg. Use for release roundups, single-feature demos, PR walkthroughs, and social announcements.

Quellinformationen

Repository
modem-dev/sideshow
Letzte Quellaktivität
14. August 2026 um 00:57
Erkannte Sprache von SKILL.md
Englisch
Sterne
619
Forks
24

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
launch-video
description
Produces sideshow release/feature videos by driving the real viewer in a recording Chromium against a live server — 1920x1080 stage with window chrome, captions, and title cards, encoded with ffmpeg. Use for release roundups, single-feature demos, PR walkthroughs, and social announcements.
# Sideshow video pipeline Maintainer-only: requires a sideshow source checkout (the pipeline lives in `scripts/launch-video/`, which never ships to npm). Generates product videos where every app frame is the real sideshow viewer talking to a real server — no screen recording, no mockups. Unlike hunk's keyframe-compositing pipeline (`skills/launch-video` in the hunk repo), sideshow's product IS live motion — cards streaming in over SSE, sandboxed iframes resizing, the comment loop — so this pipeline records **one real-time pass** instead of compositing stills: ```text stage.html a 1920x1080 HTML stage: window chrome + caption lower-third + full-screen title cards, with the live viewer in an iframe record.mjs boots a fresh server, seeds demo content, drives the storyboard with Playwright while recording video, prints the raw webm path ffmpeg re-encodes the raw webm to mp4 (h264) and webm (vp9) ``` Two halves live in `record.mjs`: reusable machinery (boot, seed, CSP strip, stage resolution, browser/recording setup) and the **storyboard** — the scene sequence, captions, and title cards. The storyboard is editorial content for one video; rewrite it per video. As of this writing the checked-in reference storyboard is the 0.13.0 release video. ## Creating a video Expect a run to take roughly the video's length plus ~20s boot; encoding adds a couple of minutes. Total output should stay near 40–60s. ```sh # 0. one-time work-dir setup (fonts are optional but much nicer than DejaVu) mkdir -p .video-work && cd .video-work printf '{"name":"sideshow-video-work","private":true}\n' > package.json npm i @fontsource-variable/inter @fontsource/jetbrains-mono cd .. # 1. record (server boot + seed + storyboard, prints the raw webm path) node scripts/launch-video/record.mjs # 2. encode both deliverables cd .video-work RAW=$(ls page@*.webm) ffmpeg -y -i "$RAW" -vf "fps=30,format=yuv420p" \ -c:v libx264 -preset slow -crf 18 -movflags +faststart sideshow-X.Y.Z.mp4 ffmpeg -y -i "$RAW" -vf "fps=30,format=yuv420p" \ -c:v libvpx-vp9 -b:v 0 -crf 32 -row-mt 1 sideshow-X.Y.Z.webm ``` ## Choosing a recipe - **Full release:** distill 3–5 user-visible headlines from the release's `CHANGELOG.md` section (per-PR entries are too granular to shoot; confirm a non-obvious shortlist with the user). Lead with what sideshow _is_ (the publish → live render → comment → revise loop) before the release-specific scenes — a social audience hasn't seen it before. End on an install card. - **Single feature / PR:** intro card → one or two scenes demonstrating the change → outro card. Name the output after the feature (`sideshow-0.13-sidebar-rail.mp4`), keep it 15–30s, and keep the canonical release storyboard in `record.mjs` intact — do the trim as a local edit and revert, or copy `record.mjs` to a scratch sibling (imports keep working) and delete it after. - **Custom:** any scene set works — tutorials, comparisons, announcements. Whatever runs in the viewer can be driven: publish over the API mid-recording and the cards stream in on camera; that's the money shot, use it. ## Storyboard model (record.mjs + stage.html) - The driver calls `window.stage` on the stage page between Playwright actions: `stage("caption", html)` swaps the lower-third (slide out/in), `stage("card", html)` shows a full-screen title card, `stage("hideCard")` fades it out. Pacing is plain `sleep(ms)` between beats. - Caption vocabulary: `<span class="badge">NEW</span>` amber pill, `<span class="hl">` amber highlight, `<span class="dim">` muted. A NEW badge is a claim about _this_ release — drop or move badges as features age. - Card vocabulary: `badge`, `h1` (+ `span.ver` for the amber version), `sub`, `cmds`/`cmd` (+ `span.p` for the prompt `$`), `foot`. - Interact with the viewer through `page.frameLocator("#app")`. Useful selectors: `aside .sess-title` (session rows), `.sidebar-toggle` (collapse/expand rail), `.card:not(#whatsNew)` (post cards — the update-notes card is also a `.card`), `button.act.comment` (the icon-only trigger that unfolds the composer; it has no text, so `getByText` won't find it), then `.composer input`. - Publish content via the HTTP API while recording: `POST /api/sessions`, `POST /api/posts` (`{session, title, surfaces: [{kind, …}]}`), `PUT /api/posts/:id` to revise (bumps the version pill live), and `POST /api/comments` (`{surface: postId, author, text}`) for agent replies. Seed background sessions _before_ opening the page; save the live publishes for on-camera. - Target pacing: money shots hold 2.5–4s after content settles, transitions ~1s, typing at `pressSequentially(..., { delay: 34 })`. Intro card ~3s, outro ~4s. ## Environment gotchas (each cost real debugging time) - **The viewer refuses to be framed.** Since 0.12.0 the viewer document sends `Content-Security-Policy: frame-ancestors 'self'`, so the file:// stage's iframe would be blocked. `record.mjs` strips that header with a Playwright route on **exactly the viewer document URL**. Never widen the route pattern: intercepting `/api/events` buffers the SSE stream forever and live updates stop. - **The title-card overlay must keep `pointer-events: none`.** Playwright's hit-target check runs in the top frame; an overlay that intercepts clicks aimed into the app iframe times every action out — even actions "behind" an intro card. - **Wait for iframes after a session switch.** Opening a session re-renders each sandboxed surface from `/s/:id`; cut away too early and the last shot shows empty card shells. `waitFor` a `.card iframe` and hold ~2–3s before the outro (this is also why "publish on camera" shots need their settle time). - **Park the mouse after sidebar clicks** (`page.mouse.move(...)` toward the content) or the hovered session row shows its delete "×" in every following frame. - **Sandbox Chromium/driver mismatch.** In the Anthropic sandbox the pinned `@playwright/test` may expect a newer browser build than `/opt/pw-browsers` provides; `record.mjs` falls back to `/opt/pw-browsers/chromium` (override with `CHROMIUM_PATH`). Driving a slightly older Chromium works. - **mp4 needs a real ffmpeg with libx264** (`apt-get install ffmpeg`; `brew install ffmpeg` on macOS) — Playwright's bundled ffmpeg records the raw webm (VP8) but cannot produce h264. Verify with `ffmpeg -encoders | grep -E 'libx264|libvpx-vp9'`. - **Give `.video-work/` its own `package.json` before `npm i`** or the fonts land in the repo's `package.json` (revert with `git checkout package.json package-lock.json` if that happens). - The recorder spawns `server/index.ts` with `PORT=0` and `SIDESHOW_DB` in the work dir — every run is a fresh workspace, nothing touches `~/.sideshow`. The version being recorded matches `latest` on npm, so the `#whatsNew` update card stays away on its own; scope card selectors with `:not(#whatsNew)` anyway. - A failed run can orphan the spawned server; kill it before rerunning (`pkill -f "[s]erver/index.ts"`). ## Content accuracy - **Verify install commands against reality**: `npm view sideshow dist-tags`. The outro card's commands are `npm i -g sideshow` + `sideshow serve --open` (or `npx sideshow serve --open`) — check they still match the README. - Perf/number claims must come from the changelog entry, phrased no stronger ("up to 95% lighter" for the 0.13.0 hydrate change, not "95% faster"). - The window chrome's URL pill shows the real server host:port — decorative but it must not lie; `record.mjs` fills it from the actual base URL. - Demo content is `bin/demoData.js` (the `sideshow demo` sessions) — label anything invented beyond it honestly, and keep agent names real (`claude-code`, `pi`). - The video is silent — never imply audio in the video or announcement copy. ## Verification and delivery - After encoding, extract spot frames at each scene boundary and mid-scene: `ffmpeg -y -ss <t> -i sideshow-X.Y.Z.mp4 -frames:v 1 check.png` (Read renders PNGs). Look specifically for: blank surface iframes (cut too early), hover artifacts in the sidebar, captions overlapping scene changes, and the intro/outro cards fully faded. Check duration with `ffprobe -show_entries format=duration`. - Outputs land in `.video-work/` (gitignored — never commit videos or the raw recording). Send both files to the user (mp4: social/Slack; webm: web embeds), report duration and sizes, and flag if the mp4 exceeds ~10 MB (Slack) or ~15 MB (X). Copy elsewhere only if the user names a destination.
Auf GitHub ansehen