預告影片製作
為正在運行的應用程式製作一個具備發布品質的預告片(約 30-45 秒):規劃腳本、透過驅動真實 UI 來拍攝、使用 ffmpeg 剪輯,最後交付 MP4 以及一個適用於 README 的 GIF。
輸出成品必須看起來像產品影片,而不是螢幕錄影。兩者的區別在於導向——場景安排、畫面構圖和節奏控制——而非解析度。
參考實作位於 reference/ 目錄;它們是可運行的 ESM 模組,不是虛擬碼。請在暫存目錄中設定,切勿在專案內執行:
cp -r <skill>/reference/* "$SCRATCH/" && cd "$SCRATCH" && npm install
export TEASER_BASE_URL="https://the-app.example.com"
reference/package.json 宣告了 "type": "module" 以及三個依賴套件。每個腳本都會讀取 TEASER_BASE_URL,因此不會對任何特定應用程式進行硬編碼。
首要之務:兩條嚴格規則
1. 絕不要在未經明確、逐項同意下觸發真實世界的副作用。
拍攝真實應用程式意味著點擊真實的按鈕。其中有些按鈕可能會發送電子郵件、公開發布內容、收取費用,或向第三方提交表單。在您觸碰應用程式之前:
- 逐一列出腳本中所有離開本機的操作。
- 告訴使用者每個操作的具體作用及執行次數,然後取得使用者對次數的同意。
- 優先選擇能在不可逆步驟前停止的模式(預覽、模擬執行、一個您可以拍攝並隨後拒絕的確認閘門)。
在即時系統上不存在所謂的唯讀探測。一個落在已執行工作階段中的「診斷性」點擊,將會真實觸發。如果您正在除錯某個操作為何未生效,請假設您的下一次點擊將會生效,並做好準備。
如果某個操作意外觸發:立即從源頭停止程序(終止工作階段/處理程序,而不僅是 UI 按鈕,因為後者可能無效),透過讀取狀態來驗證已停止,並在您的最終訊息中向使用者清楚說明——包括次數。
2. 假設每一幀畫面都是公開的且充滿 PII。
真實帳戶會攜帶姓名、電子郵件、地址和文件。請預先與使用者共同決定政策:
- 以畫面構圖避開(預設)- 構圖時排除個人身分。在 ffmpeg 階段裁切持續存在的瀏覽器元件(側邊欄、頭像、帳戶選單)。
- 在拍攝前隱藏 - 在開拍之前,使用
page.evaluate對有問題的卡片設定visibility: hidden。切勿拍攝後再希望繞過它。 - 使用預先準備的演示帳戶 - 如果有的話,這是最乾淨的方式。
在交付前,以全解析度審查最終剪輯的每一幀畫面。不是檢視縮圖,而是檢視實際畫格。
工作流程
1. 偵查與腳本規劃
首先對每個候選畫面進行經過認證的截圖(reference/scout.js)。檢視這些截圖。然後撰寫一個腳本表格:節拍、鏡頭、觀眾應該感受到什麼,以及長度。一個沒有指定任務的鏡頭,在拍攝前就會被剪掉。
有效的敘事弧線:緊張(問題)→ 揭露(產品)→ 證明(它真的能做到)→ 規模(成果)→ 信任(它在哪裡運行)→ 結尾。
在腳本中標註哪些畫面包含 PII,哪些是安全的。
2. 佈置場景
在拍攝前整理,而不是之後:
- 選擇資料看起來最佳的畫面 - 完整的圖表、活躍的清單、可識別的名稱、非零的計數。切勿拍攝載入中、空白狀態或半渲染的圖表。
- 透過初始化腳本中的
localStorage或拍攝前的page.evaluate,有意識地設定 UI 狀態(面板寬度、展開/收合、篩選器、滾動位置)。 - 在鏡頭外關閉提示訊息、橫幅和 Cookie 同意視窗。
- 讓資料穩定下來:
networkidle再加上幾秒鐘。
3. 拍攝
使用 CDP screencast(Page.startScreencast),而不是螢幕錄影。它會以約 100fps 的速率在螢幕外捕捉頁面,因此使用者正在進行的任何操作都不會出現在畫面中,且機器仍可正常使用。Playwright 負責驅動;CDP 負責拍攝。相關實作請參考 reference/rig.js。
- 每個鏡頭只有一個主體。拍攝這個事物本身,而不是「整個應用程式」。
- 鏡頭移動來自頁面內部的腳本化緩動滾動(
cameraScroll),而不是任何濾鏡。請參考下方關於 zoompan 的警告。 - 游標是一個演員:使用內建的 SVG 游標(
cursorClick),它會以緩動方式滑動並在按壓時下沉,或者將其隱藏。不要讓它無目的地飄移。 - 對每個鏡頭進行排練,提取畫格,檢視它們,調整,然後正式拍攝。
拍攝外部視窗(應用程式自身開啟的瀏覽器、桌面應用程式)需要使用作業系統層級的畫面擷取 - gdigrab/x11grab/avfoundation(在 rig 中為 recordDesktop)。這會捕捉螢幕上的一切,包括使用者自己的工作。警告使用者,請他們離開,並逐幀審查結果;如果捕捉到任何私人內容,請立即刪除。
4. 標題卡片
使用在瀏覽器中拍攝的動態 HTML/CSS,而非靜態圖片。在應用程式自身的來源(origin)上,將卡片標記注入一個空白頁面,以便解析其網頁字型和調色盤,然後進行 screencast 拍攝。從即時頁面(getComputedStyle)提取真實的顯示字型,並固定一個已知良好的等寬字型堆疊,而不是去猜測一個。
匹配應用程式的動畫語言(其自身的緩動曲線和持續時間)。在設計卡片前,如果專案沒有既定的卡片風格,請先載入 frontend-design 技能。
5. 剪輯
reference/build.js 將每個片段標準化為統一的尺寸/幀率,然後對整個序列進行交叉淡入淡出。保持每約 2.5-4 秒一個剪輯。讓高潮時刻以正常速度有足夠的展現空間,同時壓縮其周圍的例行操作。
6. 審查,然後交付
- 產生一個 contact sheet 以及每秒的全解析度畫格。
- 檢查畫格中是否有 PII、被裁切的面板以及難以閱讀的文字。
- 使用探測工具驗證持續時間和尺寸。
- 交付一個 MP4,以及針對 README 的、經過調色板最佳化的 GIF(GitHub 不會內嵌播放提交的 MP4)。將 GIF 保持在約 10 MB 以下。
- 除非要求,否則保持無聲;您無法授權音樂。請說明這一點。
寶貴的實戰經驗
切勿對推近鏡頭使用 zoompan。 它會逐幀重新計算縮放,並將裁切視窗四捨五入到整數像素,導致影像前後抖動——看起來像地震。請使用鎖定鏡頭加上真實的頁面內部滾動。如果必須使用推近鏡頭,請先將來源預先放大 4 倍,並使用畫格差異測量工具(reference/jitter.js)進行驗證:一個鎖定的靜態鏡頭其平均差異應遠低於 1.0;可見的抖動會讀取到 3 以上。
當動畫靜止時,Screencast 會停止發送畫格。 一個動畫在 1.2 秒後結束的卡片,只會產生一個 1.2 秒的片段。請使用 tpad=stop_mode=clone:stop_duration=N 進行填補,並用 -t 剪裁到您想要的長度。
組合帶有時間戳的畫格,而非固定幀率。 Screencast 畫格會攜帶 metadata.timestamp;請撰寫一個 ffmpeg concat 列表,其中包含真實的每幀持續時間,然後重新取樣為固定的 60fps。假設固定間隔會產生抖動。
將 ffmpeg-static 作為開發依賴,並透過模組路徑解析。永遠不要依賴 ffmpeg 在 PATH 上。(ffprobe 不在該套件中——請改用 ffmpeg -i 的標準錯誤輸出進行解析。)
認證畫面擷取: 以有頭模式登入一次,儲存 storageState,並在每次拍攝中重複使用(reference/login.js)。工作階段會過期——請在同一天完成拍攝,或重新執行。同時也在那裡植入任何 localStorage 的 UI 偏好設定。
從已部署的來源(origin)存取 localhost 會被 Chrome 的本機網路存取政策封鎖。 如果頁面必須連接到本機服務,請使用 --disable-features=LocalNetworkAccessChecks,PrivateNetworkAccessChecks,BlockInsecurePrivateNetworkRequests 啟動。
以固定偏移量裁切持續存在的瀏覽器元件。 以 1920 + railWidth 的 viewport 寬度進行拍攝,然後使用 crop=1920:1080:railWidth:0 來移除側邊欄並精確對齊 1080p。
交付成果
回報檔案路徑、持續時間和尺寸;清楚說明使用者仍需提供什麼(音樂、GitHub 原生影片上傳);並揭露拍攝過程中發生的任何使用者可能想知道的事情。