project-demo-video
將展示影片以程式碼方式建置,以便在每次變更後數分鐘內就能重新渲染。螢幕上的一切都是真實的:運行的應用程式、真實的指令輸出、真實的原始碼檔案。絕無模擬。
此技能的撰寫方式讓能力較弱的編碼代理也能逐步跟著執行。請依序遵循各階段操作。每個階段都有一個完成條件行:在條件滿足之前不要進入下一階段。從 templates/ 複製檔案,而不是重新撰寫。
安裝:將整個資料夾(SKILL.md 和 templates/)複製到代理的技能資料夾中,例如 ~/.claude/skills/project-demo-video/,或直接將代理指向 SKILL.md。
您將產生的內容
| 輸出檔案 | 說明 |
|---|---|
<name>-voiced.mp4 | 1920x1080, 30 fps, H.264 + AAC,內嵌字幕,含語音 |
<name>-captions-only.mp4 | 相同畫面,無聲音軌(使用者可選擇此版本或自行錄製語音) |
<name>.srt | 字幕檔案,文字與旁白相同 |
thumbnail.png | 1280x720 |
narration.md, shotlist.md | 含真實時間戳記的腳本;每個場景及其來源 |
frames/ | 您實際查看過的 QA 幀 |
repo/ | 變更或新增的專案檔案,以對應的儲存庫路徑呈現 |
COPY_LIST.md, README-snippet.md, REPORT.md | 應複製的內容、README 區段說明、已完成事項與風險 |
0. 您不得違反的規則
- 唯讀原始碼。 絕對不要寫入、提交或推送到使用者的真實儲存庫。在複本中工作(
robocopy/cp,排除node_modules、.git、建置輸出),然後執行npm ci。將值得保留的變更放在輸出資料夾下,對應儲存庫路徑,加上COPY_LIST.md。由其他工具或人員處理 git 操作。 - 螢幕上要誠實。 任何模擬的內容(設備、連線、付款、模型)必須在影片中標示為「模擬」。絕不暗示不存在的硬體、部署或使用者。複製專案本身的「真實 vs 模擬」表格;如果 README 有誤,請在報告中說明,不要重複該不實陳述。
- 螢幕或交付檔案中不得出現機密: 權杖、API 金鑰、
.env內容、私鑰、真實電子郵件。專案文件中的開發權杖(例如Bearer dev-alice)則可使用。展示程式碼時,應選擇排除機密內容的行數範圍。 - 不要在幀、旁白、字幕或交付文件中提及撰寫程式碼的 AI 工具或其供應商 ,除非使用者另有指示。屬於產品設定一部分的執行時模型 ID 可以作為模型名稱出現。完成前請用
grep -ri檢查。 - 遵守競賽規則。 閱讀時間限制(通常為「少於 3:00」;目標設定為 2:50)及必要技術規則。影片必須展示必要技術真正在運作,而不僅是提及。
- 免費方案,不部署、不購買。 不要進行部署、建立雲端資源或花錢。如果某個步驟需要憑證或使用者的錄製內容,請清楚詢問一次,然後繼續處理其他所有事項。
- 僅向使用者要求 憑證、他們自己的錄製內容,或真正屬於他們的選擇(例如哪種語音)。其他事項由您自行決定,並告知您的決定。
1. 先決條件(檢查,不要假設)
node --version # 22+(建議 24;.mjs 檔案必須是純 JavaScript,無 TypeScript 轉型)
- 在專案複本中:必須存在
@playwright/test(已安裝 Chromium)和ffmpeg-static。檢查方式:node -e "import('@playwright/test').then(async m=>{const b=await m.chromium.launch();console.log(b.version());await b.close()})"與node -e "import('ffmpeg-static').then(m=>console.log(m.default))"。如果缺少且專案禁止新增相依套件,請將它們放在單獨的工具資料夾中並指向該資料夾。 - 語音:Windows PowerShell 搭配
System.Speech(內建)。macOS 使用say,Linux 使用espeak-ng;然後您必須調整templates/lib/tts.mjs(僅synthAll和tts.ps1為作業系統相依)。 ffmpeg-static未隨附ffprobe:從 WAV 標頭(templates/lib/tts.mjs中的wavSeconds)和錄製器自身的計時來測量持續時間。- Windows Shell 注意事項:對於包含反斜線、正規表達式或
$的任何檔案,建議使用「寫入」和「編輯」工具。內嵌python -c/heredoc 重寫 JS 會靜默破壞跳脫字元。前景sleep在某些代理中會被封鎖:請使用until grep -q "done" log; do sleep 10; done等待。
2. 階段
階段 A:需求與驗證對應表
閱讀規則頁面、README、專案自身的文件以及任何故事大綱。在您的筆記中建立一個表格:
| 評審必須確信的是 | 影片在哪裡證明它 | 場景 |
|---|---|---|
| 必要技術在程式碼中被呼叫 | 實際呼叫可見:請求日誌、終端機、程式碼摘錄 | ... |
| 體驗能運作 | 應用程式在瀏覽器中運行 | ... |
| 什麼是真實的 vs 模擬的 | 專案自身的表格 | ... |
完成條件: 每一列都有一個對應的場景。
階段 B:工作區
- 將儲存庫複製到一個暫存資料夾,然後安裝:
npm ci。 - 將
templates/複製到該複本中,命名為video/(保留結構:video/lib、video/pages、video/scenes、video/final.mjs、video/narration.mjs……)。在package.json的腳本中新增:"video:final": "node video/final.mjs"。不要新增任何相依套件。 - 在複本的
.gitignore中新增video/out/。
階段 C:試運行真實應用並修復錯誤(在錄製任何內容之前執行此步驟)
以評審會使用的方式啟動應用(npm run demo 或 README 中的指令),使用其文件規定的預設連接埠,確保螢幕上顯示的每個指令都是字面上的。使用一個小型的 Playwright 腳本驅動它,在故事的每個步驟後以 1920x1080 解析度截圖,然後查看它們。檢查:
- 登入、空白面板、紅色錯誤橫幅、主控台錯誤;
- 故事需要的每個狀態都能正確渲染(參考專案的第一次試運行顯示了登入失敗、空白收件匣和缺少關鍵檢視,這些都被舊的草稿影片隱藏了);
- 文字重複或被截斷。
在複本中最小程度地修復真實錯誤,執行專案自身的檢查(npm run verify、測試、型別檢查),並記錄每個修復項目於 COPY_LIST.md。如果應用程式沒有顯示網路流量的方式,請新增一個小型可選鉤子(請求日誌),而不是偽造一個。
完成條件: 整個故事連續兩次順利執行,且專案的檢查通過。
階段 D:故事板與旁白
- 選擇場景。參考故事(約 2:50):
| 場景 | 長度 | 展示內容 |
|---|---|---|
| 鉤子 | 12 秒 | 一個變更波及多個承諾(動畫卡片) |
| 啟動 | 6 秒 | 啟動一切的單一指令,真實輸出 |
| 模擬(應用程式) | 60 秒 | 8-10 個節拍;在 2 個節拍中開啟請求日誌 |
| 驗證終端機 | 17-30 秒 | 對同一個伺服器執行官方客戶端或 CLI;驗證檢查 |
| 程式碼導覽 | 16 秒 | 3-4 個證明必要技術的真實摘錄 |
| 架構、雲端堆疊、真實 vs 模擬 | 30 秒 | 圖表、堆疊原始碼、誠實表格、開源卡片 |
| 結尾 | 12 秒 | 標語、儲存庫連結、建置資訊、「X 為模擬」頁尾 |
- 撰寫
video/narration.mjs:scenes = [{ id, title, segs: [{ id, text }] }]。每個seg是一句口語台詞和一個字幕(text會照原樣顯示)。規則:每秒約 2.4 個單詞;總口語單詞數 330-380;每行一個想法;螢幕上任何卡片不超過約 12 個單詞;句子大小寫;不誇大。 - 在語音引擎的
say表格中加入發音提示(僅限於口語,例如縮寫拼讀、「Alexa+」唸作「Alexa plus」);絕對不要變更字幕文字。 完成條件: 旁白讀起來像一個陌生人能理解的故事,且驗證對應表中的每一行都有被說出或顯示。
階段 E:調整範本
僅編輯專案特定的部分:
| 檔案 | 編輯內容 |
|---|---|
video/narration.mjs | 您的場景和台詞 |
video/scenes/sim.mjs | 您應用程式的步驟:要輸入或點擊的內容、選擇器、鏡頭縮放(lens(...)、zoomWire) |
video/scenes/static.mjs | 終端機指令(CMD)、codeBlocks(檔案 + 第一行的正則表達式 + 行數 + 高亮正則表達式)、鉤子/結束頁面呼叫 |
video/proofs.mjs、video/proof-auth.mjs | 證明您主張的真實指令;它們只輸出狀態碼和錯誤名稱 |
video/pages/*.html | 文字、顏色、圖塊、圖表方塊、表格列、連結 |
video/lib/overlay.mjs | 您應用程式佈局的 SIM_CSS 選擇器(參閱佈局規則)、跑馬燈的資料來源 |
video/final.mjs | LIMIT 常數和場景順序(順序來自 narration.mjs) |
video/thumbnail.mjs、video/docs.mjs | 標題和 SHOT 表格 |
除非有問題,否則不要編輯 lib/recorder.mjs、lib/director.mjs、lib/tts.*。
階段 F:錄製真實的驗證輸出
node video/proofs.mjs 會啟動一個全新的伺服器,執行真實的指令(官方檢測工具或客戶端 CLI、驗證檢查、安裝/啟動輸出),並將它們輸出的內容儲存到 video/out/proofs.json。終端機場景會重播這個擷取的輸出,呈現在一個可讀大小的 HTML 終端機中。規則:顯示真實的指令文字(僅縮短路徑套件);明顯截斷過長的輸出(以暗淡的「... N 行 ...」行表示),並在標記中寫入「記錄運行」;絕不輸出權杖;如果有的話,以美化格式輸出 JSON 並說明。
階段 G:旁白音訊
預設(無需帳號):node video/final.mjs --rate 2 會使用內建的 Windows 語音(Microsoft Zira)為每一行產生一個 WAV 檔案,並測量每個檔案的長度。速率 1 對參考腳本來說約為 150 秒的語音,速率 2 約為 139 秒,速率 3 太快。選擇能讓整部影片保持在限制內的最慢速率。
選用的人聲語音:參閱附錄 A。以下的所有步驟無論使用哪種語音都相同。
階段 H:錄製與合成
node video/final.mjs --rate 2 # 全部:tts、驗證、錄製所有場景、合成兩種版本
node video/final.mjs --rate 2 --reuse-proofs # 跳過重新執行驗證
node video/final.mjs --scenes sim,close --reuse-tts --reuse-proofs # 僅重新錄製某些場景
node video/final.mjs --assemble-only # 重新合成(字幕、音訊)而不重新錄製
node video/final.mjs --tts-dir video/out/tts-human --name my_video --single # 從另一個音訊資料夾建置
完整運行需要 6-10 分鐘;請在背景執行並輪詢日誌。如果總時長達到 180 秒或以上,腳本會失敗。目標設定為 170 秒。
重要:如果您變更了旁白文字或語音,持續時間會改變,因此每個場景都必須重新錄製(標記來自持續時間)。僅變更一個場景的視覺效果,則使用 --scenes <id> --reuse-tts。
階段 I:品質檢查(絕不省略,絕不信任日誌)
- 從最終的 MP4 檔案中,取出每個旁白行結束時的幀:
ffmpeg -ss <markEnd-0.3> -i final.mp4 -frames:v 1 frame.png(時間線在video/out/timeline.json中)。查看它們。檢查在 1080p 下的可讀性、空白區域、裁剪的文字、字幕位置與大小、同步(字幕事件在螢幕上)、無機密、無供應商提及、存在誠實標籤。 ffmpeg -i final.mp4 -af volumedetect -f null -:平均值約 -22 dB,最大值低於 0 dB。- 兩種版本的持續時間均在限制內;兩個檔案都有影片和音訊串流。
grep -ril "<供應商詞彙>" video/pages video/narration.mjs <輸出文件>除了允許的模型 ID 外,不應返回任何結果。- 再次執行專案的檢查(
npm run verify;如果 UI 有變更,則執行 e2e)。 - 修復並重新渲染,直到全部通過。
階段 J:交付項目
使用 node video/docs.mjs <outdir> 產生 narration.md 和 shotlist.md(真實時間戳記)。複製 MP4、SRT、縮圖(node video/thumbnail.mjs <png>)、QA 幀以及變更的儲存庫檔案(原始檔案與您的複本之間的所有差異檔案,以對應的儲存庫路徑呈現)。撰寫 COPY_LIST.md(錯誤修復優先,然後是新檔案、建議的提交訊息、「複製後執行建置與驗證」)、README-snippet.md(執行指令、進入點檔案:行號、客戶端檔案:行號、技能檔案、影片連結佔位符)以及 REPORT.md(已完成事項、使用者必須執行的操作、風險)。明確說明您無法檢查的內容(例如,您無法收聽語音)。
3. 管線運作方式(變更前請先閱讀)
錄製器(lib/recorder.mjs)。Playwright 自身的 recordVideo 會限制位元率並模糊小文字,因此這個腳本使用 Chromium 螢幕截取:JPEG 幀(品質 92)在到達時加上時間戳記,寫入磁碟,並以恆定 30 fps 使用 libx264 crf 17 編碼。靜態畫面產生的幀數較少;最後一幀會持續顯示。時間戳記來自同一個時鐘(performance.timeOrigin + performance.now()),也是導演使用的同一個時鐘。
導演(lib/director.mjs)。每個旁白行都是一個片段。d.seg(id, actions, {tail}) 會記錄開始時間,執行螢幕上的動作,然後持續直到該行播放完畢再加上 tail * 0.45 秒(尾音會全域修剪以符合限制)。標記(id、開始、結束、文字)會儲存到 video/out/<scene>.marks.json。因為音訊之後會根據記錄的標記放置,所以畫面和語音不會偏移。關於節拍的規則:在片段內執行動作,並確保動作在旁白結束前完成;如果台詞比動作短,則將其拆分為兩個台詞(這就是為什麼一個故事節拍變成了 sim9 和 sim10)。
音訊(final.mjs)。每個 WAV 會使用 adelay 延遲到其全域開始時間,使用 amix=normalize=0 混合,使用 alimiter 限制,並使用 apad=whole_dur=<total> 補齊(如果沒有補齊,-shortest 會剪掉影片的結尾)。
字幕。 提示是根據旁白文字在句子結尾處拆分(例如「2.0」這樣的小數會被保護),以及在超過 82 個字元時的逗號處拆分,時間按長度比例分配。.srt 檔案會交付。內嵌版本使用一個生成的 ASS 檔案,其中包含 PlayResX: 1920, PlayResY: 1080 和字型大小 36。不要使用 force_style 來內嵌 SRT:libass 會根據 288 像素高的畫布進行縮放,導致文字約為原本大小的四倍。
疊加層(lib/overlay.mjs),僅注入到錄製的應用程式頁面中(應用程式本身未經編輯,因此 Playwright 選擇器保持不變)。疊加層包含:狀態列的 SIM 標記、一個提示錄製正在進行並附有倒數計時的資訊列、一個根據 tickerLines 更新的軟性跑馬燈,以及供導演使用的遠端控制 API(seg、prop、state)。這些 API 呼叫不會出現在影片中。
附錄 A:使用 ElevenLabs 的人聲語音
- 從 ElevenLabs 取得一個 API 金鑰。
- 設定
ELEVENLABS_API_KEY環境變數。 - 在
video/narration.mjs的頂端,將export const TTS = 'elevenlabs';設為非註解。 - 您可以透過在
video/narration.mjs中編輯export const ELEVENLABS_VOICE = '<voice_id>';來選擇語音。 - ElevenLabs 會將文字轉錄為音訊;它與
templates/lib/tts.*中的相同邏輯相容。 - 使用
node video/final.mjs --rate 2 --elevenlabs來使用 ElevenLabs 語音。
注意:ElevenLabs 並非免費;您需要一個付費帳號。
附錄 B:疑難排解
- 「找不到 Chromium」錯誤:
npx playwright install chromium - 「ffmpeg 未找到」錯誤:檢查
ffmpeg-static是否已安裝,以及node -e "import('ffmpeg-static').then(m=>console.log(m.default))"是否回傳路徑。 - 語音問題:在 Windows 上,確保 PowerShell 執行原則允許執行
Add-Type -AssemblyName System.Speech。執行powershell -Command "Add-Type -AssemblyName System.Speech; $s=New-Object System.Speech.Synthesis.SpeechSynthesizer; $s.Speak('test')"進行測試。 - 字幕時間偏移:如果字幕與語音不同步,請檢查
video/out/timeline.json中的markEnd值。如果它們與實際的語音持續時間不匹配,請使用--reuse-tts重新錄製語音。 - 影片太長:減少場景長度,或使用更快的語音速率。您也可以透過在
video/narration.mjs中編輯seg物件的tail屬性來修剪尾音。 - 「權杖已洩漏」錯誤:檢查
video/pages/和video/narration.mjs中的檔案是否有任何機密內容。使用grep -ri遍歷video/目錄。