B 影片 是做什麼的?
更多觸發詞
PR 的影片導覽、製作示範錄影、旁白示範、同時錄製兩個版本、解說影片、沒有 drawtext 濾鏡、Playwright recordVideo、缺少 headless shell
每個斷言集合一個 mp4:標題卡片、A 片段、B 片段、證據卡片、判決卡片,每個都有語音。
專為搭載 Apple Silicon 的 macOS 打造(say、位於 ~/Library/Caches/ms-playwright 的 Playwright 快取、chrome-mac-arm64)。在其他平台上,請設定 CHROMIUM_EXE 並使用 --eleven 參數來啟用語音。需要 Bun、ffmpeg 和 Python 3。
執行方式
S=${SKILL_DIR:-~/.claude/skills/narrated-ab-video}
$S/preflight.sh /tmp/vid # 準備 chromium、playwright ffmpeg、ffmpeg、say 語音
bun $S/record.ts a.json raw/a-run # 每側一個場景(複製 examples/scenario.json)
bun $S/record.ts b.json raw/b-run
bun $S/cards.ts cards.json cards # 標題/證據/判決 PNG + banner-A/B.png
python3 $S/build.py plan.json # [--voice Samantha] [--rate 182]
open -a "QuickTime Player" demo.mp4 # 以暫停狀態開啟;請勿自行播放
範例在 examples/ 目錄中:scenario.json(步驟:wait、select、click、clickIfPresent、type、send、waitStable、expand、fetchText、waitText、probeEnabled、reload;詳見 record.ts 檔頭說明)、cards.json、plan.json。計畫路徑相對於計畫檔案。已在一個 4 片段、40 秒的建置中端到端驗證,使用兩個真實剪輯。
為何如此設計(已知摩擦點)
- Playwright 固定的 headless shell 經常未下載。
record.ts和cards.ts會啟動最新快取的完整chromium-*建置(或$CHROMIUM_EXE)而非直接失敗。 - recordVideo 需要 Playwright 自己的 ffmpeg 建置。 缺少它會導致無法產生 webm。preflight 會從
browsers.json讀取修訂版本並執行bunx playwright install ffmpeg。 - Homebrew 的 ffmpeg 可能缺少
drawtext。 橫幅是由cards.ts產生的透明 PNG,在 build.py 中疊加。無需尋找字型或濾鏡。 - 模型回覆需要 10 到 60 秒的空白時間。
send會標記「sent」,而waitStable會標記「replied」。build.py 會以speed(預設 10 倍速)播放該時間範圍,其餘部分則以 1 倍速播放。 - 旁白通常比影片片段長。 build.py 會將最後一個影格凍結(
tpad stop_mode=clone)直到語音結束,這樣在解釋回覆時,回覆內容仍會停留在畫面上。 - 動態輸入(OTP 碼、來自先前回合的 ID): 在
type步驟中放入{{fn:NAME}},並在場景旁的hooks.ts中匯出NAME(page, state)。state.lastJson會保留最後一個符合captureJsonFrom的 POST JSON。 - 選擇器: 按鈕是透過角色名稱和名稱正規表達式(
"Tools used"、"^Send$")來匹配。CSS 僅用於輸入框和下拉選單。在編寫腳本前,可先快速截圖來找出名稱。 - 合併需要完全相同的編碼。 每個片段都使用相同的編解碼器、幀率和音訊配置重新編碼,然後使用
-c copy進行合併。 - 更好的語音:
ELEVENLABS_API_KEY=... build.py plan.json --eleven <voice_id>(模型 eleven_multilingual_v2;片段會根據語音+文字雜湊值快取,因此重新執行不會消耗字數)。請將金鑰保存在權限 0600 的檔案中,切勿放在計畫檔案裡,並在使用後刪除。 - 單側示範(無 A/B): 錄製一個場景,為
cards.json提供一個橫幅,並將同一個剪輯切割成幾個計畫片段,每個片段一個斷言,使用spans加速等待時間(例如 5 倍)。建置方式相同,沒有 A 側。 - 使用者可能只貼上
<voice_id> <key>。 這意味著使用--eleven <voice_id>重新建置。較慢的語音會拉長影片(使用say的 72 秒變成了 92 秒),因為 build.py 會將最後一個影格保持到語音結束。 - 拍攝前重新建立已拆除的示範環境: 短期有效的驗證資訊(本機 JWT、隧道 URL、暫時的雲端資源)通常到隔天就會失效。在錄製前先用一次 API 呼叫重新驗證;如果那邊出現「JWT token expired」的 401 錯誤,你拍攝的回合將永遠等不到結果。
- 遺失原始片段: 從先前的輸出 mp4 中剪下該側的 footage(透過取樣橫幅角落顏色來找出其時間範圍),將其儲存為
<dir>/a.webm並使用-f mp4和空的 marks.json,然後使用一個明確的 span。因為橫幅已經嵌入其中,所以省略banner。 - 音訊:
say -o只會寫入檔案。在未經使用者明確同意之前,切勿播放影片或任何音訊。以暫停狀態開啟並交給使用者。 - 先證明再拍攝。 先執行 A/B 測試並斷言結果(API 或日誌)。影片展示的是已證明的結果,它本身不是測試。
- 卡片必須與片段尺寸相符。 build.py 不會縮放。如果使用
"viewport": [1440, 900]錄製,則cards.json中也需要"size": [1440, 900],否則合併時會出現混合的影格尺寸。 - 非同步 UI 需要標記,而非固定等待。
waitText會在正規表達式出現時標記(或timeout:<mark>),probeEnabled搭配waitMs會輪詢按鈕並將結果記錄在 marks.json 的probes[]中,type/send/click上的tryMs會標記skipped:<step>而非中止,這樣「維持禁用」的一側仍能完成片段。 - 探測你要點擊的同一個元素。 CSS 探測(
button[aria-label=...])可能匹配到不同的按鈕,而非角色名稱為send的那個,並在點擊成功時回報禁用狀態;請在probeEnabled中使用"role"。 - 開發模式疊加層(查詢 devtools、錯誤提示)會出現在片段中。 在第一次 wait 之後加上
{"clickIfPresent": "^Close .* devtools$"}。 - 等待一個永遠不會「穩定」的回覆: 一個即時經過時間的徽章每秒都在變更主體文字。在建置前請先檢查
marks.json中是否有replied標記。 - 確認證明存在於可見文字中。 折疊的工具卡片可能不會展開(
expand靜默地什麼都不點)。請閱讀marks.json的body來尋找斷言字串。如果找不到,請修改提示詞,或改為在卡片上證明。 - 在拍攝「基礎版本失敗」的斷言前,先證明兩者相等。 一旦本機基礎版本因為某個原因(某個在本機開啟、在遠端關閉的旗標)而失敗,而這個原因是真實環境中永遠不會發生的,並且旁白卻宣稱了這個失敗。請先匹配遠端的旗標,然後再拍攝。
- 標題卡片:每個側邊測試的提交與旗標狀態。每個片段一個斷言。以判決卡片結束。
相關技能
narrated-screenshot:單張帶註解的靜態圖片,而非影片。pr-ab-evidence:此影片所拍攝的紅/綠 A/B 方法。