预告片视频
为正在运行的应用制作一份发布级的预告片(约 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 阶段裁剪持久化的 Chrome 元素(侧边栏、头像、账户菜单)。
- 录制前隐藏——在录制开始前使用
page.evaluate将敏感的卡片设置为visibility: hidden。切勿拍摄后指望剪辑跳过。 - 使用预置演示账户——有现成账户时最为干净。
在交付前,以全分辨率检查最终剪辑的每一帧。不是联系表,而是实际帧。
工作流
1. 侦察与故事板
首先为每个候选屏幕拍摄带认证的截图(reference/scout.js)。查看截图。然后编写一个故事板表格:节拍、镜头、观众应 感受 到的情绪以及时长。未分配任务的镜头在拍摄前就被剪掉。
有效的节奏:紧张(问题)→ 揭示(产品)→ 证明(它确实能做到)→ 规模(结果)→ 信任(运行环境)→ 收尾。
在故事板中标注哪些屏幕包含 PII,哪些是安全的。
2. 布置场景
在录制前进行策划,绝不事后补救:
- 选择数据状态最佳的视图——完整的图表、活跃的列表、可识别的名称、非零计数。切勿拍摄旋转加载图标、空状态或半渲染的图表。
- 通过
localStorage在初始化脚本或预录制page.evaluate中刻意设置 UI 状态(面板宽度、折叠/展开、过滤器、滚动位置)。 - 在镜头外关闭提示消息、横幅和 cookie 栏。
- 等待数据稳定:
networkidle后再等几秒。
3. 拍摄
使用 CDP 屏幕捕获(Page.startScreencast),而非屏幕录制。它以约 100fps 在屏幕外捕获页面,因此用户的操作不会出现在画面中,机器仍可正常使用。Playwright 驱动,CDP 拍摄。reference/rig.js 实现了该功能。
- 每个镜头一个主体。聚焦于特定内容,而非“整个应用”。
- 摄像机移动由 页面内部的脚本化缓动滚动(
cameraScroll)实现,绝不使用滤镜。请参阅下面的 zoompan 警告。 - 光标是一个演员:使用注入的 SVG 光标(
cursorClick),它带有缓动效果平滑移动并在按下时下沉,或者隐藏它。不要有闲置漂移。 - 排练每一次拍摄,提取帧,查看,调整,然后正式拍摄。
拍摄外部窗口(应用自身打开的浏览器窗口、桌面应用)需要操作系统级捕获——gdigrab/x11grab/avfoundation(rig 中的 recordDesktop)。这会捕获 屏幕上的所有内容,包括用户自己的操作。警告用户,请其离开,并逐帧检查结果;如果捕获到任何隐私内容则删除。
4. 标题卡片
使用浏览器内拍摄的动画 HTML/CSS,而非静态图片。将卡片标记注入 在应用自身源上 的空白页面中,以便其 Web 字体和调色板生效,然后进行屏幕捕获。从实时页面提取真实显示字体(getComputedStyle)并锁定一个已知良好的等宽字体栈,而非猜测。
匹配应用的运动语言(其自身的缓动曲线和持续时间)。如果项目没有现成的卡片风格,请在设计前加载 frontend-design 技能。
5. 剪辑
reference/build.js 将每个片段标准化为统一的尺寸/帧率,然后将整个链进行交叉淡入淡出。保持剪辑间隔约为 2.5-4 秒。让高潮时刻以正常速度展现,同时压缩其周围的常规环节。
6. 审核,然后交付
- 生成联系表 以及 每秒的全分辨率帧。
- 检查帧中是否有 PII、裁剪面板和不可读文本。
- 使用探测工具验证时长和尺寸。
- 交付 MP4 格式,对于 README 还提供调色板优化的 GIF(GitHub 不会内联播放提交的 MP4)。保持 GIF 在 ~10 MB 以下。
- 除非用户要求,否则保持静音;你无法授权音乐。明确说明这一点。
来之不易的细节
切勿使用 zoompan 实现推近特写。 它会逐帧重新计算缩放并将裁剪窗口四舍五入到整像素,导致图像来回抖动——看起来像地震。使用锁定镜头加上真实页面内滚动。如果必须使用推近特写,请先将源素材预放大 4 倍,并使用帧差测量验证(reference/jitter.js):一个锁定的静态镜头应测得的平均 delta 远小于 1.0;可见抖动读数为 3 以上。
当动画静止时,屏幕捕获会停止发射帧。 动画在 1.2 秒后结束的卡片会生成一个 1.2 秒的片段。使用 tpad=stop_mode=clone:stop_duration=N 进行填充,并使用 -t 剪裁到你想要的长度。
组装带时间戳的帧,而非固定帧率。 屏幕捕获的帧会带有 metadata.timestamp;编写一个 ffmpeg concat 列表,其中包含真实的逐帧持续时间,然后重新采样为恒定的 60fps。假定固定间隔会产生抖动。
ffmpeg-static 作为开发依赖,通过模块路径解析。切勿依赖系统 PATH 中的 ffmpeg。(ffprobe _不_包含在该包中——请改用解析 ffmpeg -i 的 stderr。)
带认证的捕获: 先以可视方式登录一次,保存 storageState,然后跨每次拍摄重复使用(reference/login.js)。会话会过期——在当天完成拍摄或重新运行。同时在那里预填任何 localStorage UI 偏好。
从部署源访问本地主机被阻止 由于 Chrome 的本地网络访问策略。如果页面必须访问本地服务,请使用 --disable-features=LocalNetworkAccessChecks,PrivateNetworkAccessChecks,BlockInsecurePrivateNetworkRequests 启动。
以固定偏移裁剪持久化的 Chrome 元素。 以视口宽度 1920 + railWidth 拍摄,然后使用 crop=1920:1080:railWidth:0 去除侧边栏并精确达到 1080p。
交付物
报告路径、时长和尺寸;明确说明仍需要用户提供的部分(音乐、GitHub 原生视频上传);并披露拍摄过程中发生的任何用户可能需要知道的事情。