Communitygithub.com

johnwhoyou/screen-video

使用 Playwright 錄製帶字幕、可選語音旁白的螢幕錄影,包括流暢的游標、鍵盤和點擊音效、縮放與平移,並輸出為 2×–4K 高畫質 .mp4。可嵌入 GitHub PR/Issue、Linear 任務、Slack 或客戶教學影片。

screen-video 是什麼?

這是一個 AI 代理技能,用於自動化錄製高品質的螢幕錄影,特別適合展示網頁應用程式的操作流程、UI 變更或重現錯誤。它基於 Playwright 框架,能錄製流暢的游標移動、鍵盤輸入和滑鼠點擊音效,並支援字幕和語音旁白(透過 Google Gemini TTS)。錄製結果是優化的 H.264 .mp4 檔案,支援 2× 到 4K 解析度。技能涵蓋完整的工作流程:詢問需求、編寫流程腳本、錄製、添加旁白、檢查品質,以及直接上傳到 GitHub、Linear、Slack 等平台。它特別強調避免常見陷阱(如瀏覽器全螢幕、資料變更),並提供客戶教學影片的專用功能(如 Gmail 場景模擬)。對於需要快速製作產品演示或開發者文件的團隊來說,這是一個強大的工具。

相容平台~Claude Code✓Codex CLI✓Cursor✓Gemini CLI
npx skills add https://github.com/johnwhoyou/screen-video/tree/HEAD/plugins/screen-video/skills/screen-video

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

預覽

來自技能 README

walkthrough.mp4

說明文件

螢幕錄影

Chrome 的螢幕錄影會將瀏覽器本身錄製下來,並重新編碼為 60 fps 的 H.264 .mp4 檔案。字幕、游標、縮放和音效會在之後添加,因此無論頁面正在執行什麼操作,錄影都能保持流暢。

腳本會從當前儲存庫的 node_modules 中解析 playwright,因此請在儲存庫根目錄執行它們。<skill-dir> 是此技能所在的資料夾。

0. 先詢問

除非請求中已說明,否則請在一次訊息中詢問:

  1. 要說什麼故事? 哪個應用程式和 URL、哪些頁面、觀眾應該看到的 3–6 個重點。誰會觀看(團隊成員、審查者、客戶)?
  2. 要配音嗎? 僅有字幕,還是要旁白(AI 語音旁白)?客戶影片一定要有旁白。
  3. 影片要去哪裡? 僅提供 .mp4 檔案(預設)、GitHub PR 或 Issue、Linear 任務、Slack 或其他地方。請詢問 PR/Issue/頻道;切勿代他人建立票證。
  4. 要使用什麼資料? 要針對哪個本地資料庫或帳號進行錄製。絕對不能使用生產環境或真實客戶資料。

1. 錄製

  1. 先在本地執行應用程式,並將每個頁面各打開一次:否則開發伺服器的首次編譯過程會出現在影片中。

  2. 基於 scripts/record-lib.mjs 編寫一個流程腳本(可複製 examples/playwright-site/flow.mjs,這是一個針對公開網站的可執行錄影範例)。 使用 step(id, caption, action)(或僅用 caption())來為每個步驟在螢幕上顯示一行字幕:位於底部中央,稍微高於播放器的控制列,半透明(使用 captionStyle: 'top' 可將其設為橫幅)。腳本長度最好控制在大約 1 分鐘。 使用流程的 click(locator) / hover(locator) 來點擊,而不是 locator.click():前者會先將繪製的游標滑動到目標位置。單純使用 locator.click() 仍然有效,但游標會直接跳過去。 zoom(locator, { scale }) 會平滑地放大到目標,unzoom() 則會縮小回來,而 click(locator, { zoom: true }) 會在點擊前先放大。縮放狀態會保持直到你移動它;在縮放的視圖之外點擊會先平移到該處。

    縮放預算:應該感覺像一個人湊近看,而不是一個反射動作。

    • 只有在「小字體本身就是重點」時才縮放:例如 diff、數值變化、對話框訊息。絕不要為了導覽、開啟選單或任何人都能看到的按鈕按下動作而縮放。
    • 大約每 15–20 秒縮放一次。如果需要連續縮放兩次:在兩者之間平移(對下一個目標呼叫 zoom),而不是先縮小再放大。
    • 在相同區域進行後續操作時保持縮放狀態;當故事轉移到其他地方時再呼叫 unzoom()。
    • 縮放比例維持在 1.3–1.5。只有對於非常微小的東西(例如單一圖示)才使用 2。每次縮放至少保持 2 秒。
    • 與其縮放,不如考慮加入旁白停頓:如果字幕在原始大小下就能清楚閱讀,就不要縮放。

    使用 type(locator, text, { clear }) 來輸入文字:它會以人類打字的速度進行。click 和 type 會將每次點擊和按鍵記錄到 <video>.sfx.json,而 narrate.mjs 會將錄製好的音效(來自 sounds/ 資料夾的滑鼠點擊和機械鍵盤樣本)疊加到語音下方。SFX_VOLUME 用來調整音效音量(預設為 1),SFX=0 則表示無音效。使用 page.keyboard.type 和單純的 locator.click() 不會產生音效。

  3. 執行 node flow.mjs 會輸出 .mp4 檔案的路徑。這需要 ffmpeg(來自 $FFMPEG 環境變數或 PATH 路徑;如果沒有 ffmpeg?執行 npm i --prefix <scratch> ffmpeg-static)。

畫質

record(flow, { scale }) 會以 size(預設為 1280×800 CSS 像素,因此 UI 看起來正常)佈局頁面,並以 scale 倍的像素密度渲染:2(預設)是 2560×1600,3 是 3840×2400。文字會更清晰,而不會變小。在 2 倍縮放下,預計每分鐘約 15–40 MB,3 倍則為其 2–3 倍。對於有大小限制的目標平台(GitHub 免費方案 10 MB,Slack 預覽),請使用 scale: 1 或 CRF=24。

旁白(當要求語音時)

  1. 編寫 script.json:[{ "id": "intro", "say": "…" }, …],每個 step 的 id 對應一句口白。 撰寫時要考慮聽覺體驗:使用短句,數字用文字表示(例如 "fifteen hundred")。
  2. 執行 GOOGLE_API_KEY=… node <skill-dir>/scripts/tts.mjs script.json <audio-dir> 會為每句口白生成一個 WAV 檔案和 durations.json。Gemini 3.8 Flash TTS(可透過 TTS_MODEL、TTS_VOICE 環境變數覆蓋)聽起來比 OpenAI 的 gpt-4o-mini-tts 更自然。請在 aistudio.google.com 獲取 API 金鑰。
  3. 使用 { narration: '<audio-dir>/durations.json' } 選項進行錄製。每個 step 會等到對應的口白播放完畢後才繼續,開始時間會記錄在 <video>.mp4.marks.json 中。
  4. 執行 node <skill-dir>/scripts/narrate.mjs <video.mp4> <audio-dir> <out.mp4> 來混合影片剪輯和音效(使用 CRF 參數重新編碼,預設為 18)。最終交付 <out.mp4>。

TTS 注意事項:

  • 僅發送口白內容。諸如 "Say warmly: …" 的前綴會被朗讀出來,而 systemInstruction 會返回 400 錯誤。
  • Gemini 返回的是包含一個完整 WAV 區塊和一個來源證明區塊的資料;將第二個區塊包裝成第二個檔案頭會導致每句口白的頭尾出現靜電噪音。tts.mjs 因此會提取資料區塊;請保留此邏輯。
  • 要檢查同步,可以對 mp4 音訊進行帶時間戳的轉錄(使用 OpenAI whisper-1,搭配 response_format=verbose_json),然後與標記進行比對。

可能導致需要重拍的陷阱

  • 在已預先填入內容的輸入欄位中,使用 .fill('') 清空後再使用 pressSequentially,因為它只會附加文字。
  • 每次錄製都會變更資料(發佈、接受、儲存)。請在每次錄製之間重設資料。
  • 在進行下一步之前,請等待自動儲存和背景 AI 填充完成,否則 UI 可能會跳動。
  • 滾輪滾動可能會折疊可摺疊的標題,並隱藏下一個按鈕。建議透過 locator.evaluate 使用 el.scrollIntoView()。
  • 絕不要讓頁面進入瀏覽器的全螢幕模式:畫面尺寸會改變,影片會被裁剪到左上角。在全螢幕按鈕之前,執行 document.documentElement.requestFullscreen = undefined。

演示資料

  • 將故事所需的確切狀態植入本地資料庫,使用可信的人物資訊(example.com 電子郵件、555 開頭的電話號碼)。絕對不要使用真實客戶、預覽或生產環境資料庫。
  • 歷史記錄、稽核和事件表是僅追加的:每次重拍都會增加資料列,讓下一次錄影看起來像事先安排好的。每次錄影都使用全新的記錄,或者將故事限定在不留下痕跡的操作上。
  • 先進行乾執行:在流程中使用 DRY=1 分支,對每個破壞性確認對話框點擊取消。在幾個步驟標記處提取畫面,修正定位器與構圖,然後進行唯一的一次正式錄製。
  • 錄製完成後,檢查資料庫的最終狀態是否與字幕或旁白所述一致。
  • 變更資料意味著需要為描述變更的台詞重新執行 tts.mjs。
  • zsh 不會對 $VAR 進行單詞分割。請直接傳遞畫面時間給 frames.mjs,或在 bash 下使用迴圈。

客戶教學影片(入門、操作指南)

  • 一定要有旁白。字幕保持簡短;細節由語音傳達。
  • 電子郵件步驟使用 Gmail:從 scripts/gmail-scene.mjs 匯入 mountGmail(page, { emailHtml, subject, sender, me })。傳入應用程式真正渲染的電子郵件。它會將 target="_blank" 改寫為 _top,因為彈出視窗會讓錄影停在錯誤的頁面上等待。回覆功能已內建:#reply-btn、#reply-text、#send-btn。傳入 mailbox: 'sam' 可處理多個收件匣(透過 /sam 和 /sam/message 路徑提供服務)。回覆永遠不會到達應用程式,因此請將其效果直接寫入本地資料庫。
  • 等待元素,而不是 waitForURL:身份驗證重定向鏈會中止導航(ERR_ABORTED),且某些頁面永遠不會觸發 load 事件。為導覽用的點擊動作加上 { noWaitAfter: true } 選項。
  • 在(重新)啟動開發伺服器後,對錄影會觸及的每個頁面和 API 路由進行預熱(先訪問一次)。
  • 每次錄製前都要重新播種資料。魔法連結 Token 是一次性的,因此失敗的嘗試會消耗它們。

2. 檢查

執行 node <skill-dir>/scripts/frames.mjs <video.mp4> 10 30 50 會在影片旁生成 frame-<t>.jpg 檔案。在交付前先查看它們。對於有旁白的影片,請要求使用者聽一次:僅靠畫面無法發現音訊缺陷。檢查是否有真實客戶資料(姓名、電子郵件、電話):上傳等同於將這些資料發佈給所有能看到目標位置的人。

3. 交付

僅提供檔案(預設)

輸出最終 .mp4 檔案的絕對路徑。完成。

GitHub PR、Issue 或評論(可內嵌播放)

GitHub 僅支援來自 https://github.com/user-attachments/assets/<uuid> 格式的 URL 且位於獨立一行的影片內嵌播放,而該 URL 只能透過 GitHub 的網頁上傳獲得:gh CLI、REST API 和已提交的檔案都只會提供下載連結。上傳的內容對儲存庫成員是私有的,且永遠不會被提交。

node <skill-dir>/scripts/attach-video.mjs <GitHub PR or issue URL> <video.mp4>
  • 會開啟一個可見的 Chromium 視窗。如果已儲存的登入狀態已過期,它會顯示 GitHub 的登入頁面:請使用者自行登入。絕對不要詢問密碼,也絕對不要讀取瀏覽器的 Cookie。
  • 透過頁面的評論框上傳;評論絕不會被發佈。會輸出 ATTACHMENT_URL https://github.com/user-attachments/assets/<uuid>。
  • 會將登入狀態保存在 ~/.cache/screen-video/gh-profile。設定 FORGET_SESSION=1 可在上傳後刪除它。如果同時有另一個上傳任務在進行,它會等待這個設定檔(訊息:WAITING_FOR_PROFILE)。

將該 URL 單獨放在一行(gh pr edit <n> --body-file <file>、gh issue comment <n> --body-file <file>)。若要確認,請檢查 PR 的 body_html 中是否有 <video src=…private-user-images…> 標籤;對資產 URL 執行 curl -sI 可能返回 404,即使它實際上可以播放。檔案大小限制:免費方案為 10 MB,付費方案為 100 MB。

Linear 任務

Linear MCP 可以直接上傳,無需瀏覽器。

  1. 使用 issue、filename、contentType(設為 video/mp4)和 size(精確位元組數:macOS 使用 stat -f%z <file>,Linux 使用 stat -c%s)來呼叫 prepare_attachment_upload。
  2. 在 60 秒內,使用 curl -X PUT --data-binary @<file> "<uploadRequest.url>" 上傳,並原封不動地包含 uploadRequest.headers 中的每一項。改變任何標頭的大小寫或遺漏任一項都會導致 403 錯誤。一次只上傳一個檔案。
  3. 使用 issue 和 assetUrl 呼叫 create_attachment_from_upload。
  4. 為了讓它在討論串中播放,使用 save_comment 加上 ![walkthrough.mp4](<assetUrl>) 以及一行說明文字。打開該任務一次以確認它顯示為播放器。
  5. 若要更換影片:上傳新的,編輯同一則評論(使用 id 參數呼叫 save_comment),然後使用 delete_attachment 刪除舊的。

Slack

使用 Slack MCP,在使用者確認頻道後(分享無法撤銷):

  1. 使用 filename 和 content_length(精確位元組數)呼叫 slack_get_file_upload_url。
  2. 執行 URL='<upload_url>' && curl -X POST "$URL" -H 'Content-Type: video/mp4' --data-binary @<file>。請確保存放 URL 的變數內容與原始 URL 完全一致;如果收到 401 錯誤,表示 URL 可能已被篡改或過期,請重新獲取一個。
  3. 使用 file_id、channel_id(一個 C…/D… 格式的 ID,不是頻道名稱;請使用 slack_search_channels 查詢)、可選的 thread_ts 和 initial_comment 來呼叫 slack_complete_file_upload。

如果沒有 MCP,請將檔案路徑提供給使用者,讓他們手動拖曳上傳。

其他平台

提供檔案路徑和大小;如果目標平台有大小限制,請提及可以使用 scale: 1 或 CRF=24 來降低檔案大小。

相關技能