旁白视频
用户录制一段自己根据故事板讲述的旁白。代理编辑该片段,并将真实的产品截图动画化以匹配旁白:每个场景开始时,每次点击的位置,以及每次搜索时输入的单词都与旁白的发音同步。整个视频由代码构成(一个HyperFrames合成加一个编辑列表),因此重新录制、更换截图或调整顺序只需重新运行,无需重新编辑。
视频始终适应旁白,而非相反。用户决定风格;代理负责剪辑、显示帧并进行询问。
设置
- 路径中需包含
ffmpeg、uv、Node 20+ 和npx(HyperFrames 0.8.62 通过 npx 运行),以及用于捕获的 Playwright 和 Chromium。支持 macOS、Linux 和 Windows。 - Whisper 本地运行:Apple Silicon 上使用 mlx-whisper,其他平台使用 faster-whisper。
uv run会自动安装正确的版本。 SK=<此技能目录>/scripts。所有工具均以uv run $SK/<tool>.py形式运行;每个工具声明自己的依赖。- 在仓库追踪文件之外的文件夹中工作(例如
.logs/<video>/或临时文件夹):
<work>/
assets/ 捕获文件、背景、结束卡片
storyboard.json 场景 -> storyboard/index.html
takes/<name>/ 每个录制的片段文件夹:探测输出、转录、edit.json、剪辑后的旁白和摄像头视频
video.py 合成脚本 -> comp/ (index.html, renders/)
NOTES.md 所有决定:剪辑及其原因、场景开始时间、重建命令
1. 简要确认
通过一次简短交流确认,并提供默认值:
- 格式:发布视频、功能演示或教程(references/formats.md)。发布视频时长需控制在2分钟以内(含结束卡片)。
- 目标受众、视频必须传达的三到四个要点以及行动召唤(CTA)。
- 适合演示的示例数据:一个示例业务或搜索,能让受众一眼理解。
- 品牌:背景图片(或默认渐变)、结束卡片(产品OG图效果不错)、窗口标题栏中显示的URL。
2. 捕获
基于 $SK/capture.mjs 构建的 Playwright 脚本,以1440x900、2x分辨率捕获运行中的产品真实截图:为屏幕上输入的任何内容生成逐帧帧序列(每个按键对应一帧),为点击操作生成前后状态,为滚动操作生成高截图,以及为指针生成测量好的点击目标。如果应用需要登录,传递 login 参数,首次运行时会打开一个浏览器窗口供用户一次登录。切勿绘制UI或编辑像素。使用前仔细检查每个捕获结果。详情及隐私说明:references/capture.md。
3. 故事板
编写 storyboard.json,每个场景一张卡片:截图、屏幕上显示的内容、三到六条讲述要点(简短笔记,非完整句子)、大致时长。构建并展示:
uv run $SK/storyboard.py storyboard.json storyboard/
这是一个简单的页面,每个场景一个区块。当工具可以发布页面时,发布 storyboard/(index.html 加上 frames/),否则在本地打开。反复迭代直到用户准备好录制,然后提供录制说明(参见 references/storyboard-and-recording.md)。一旦用户开始据此录制,未经询问不得修改故事板。
4. 录制
用户录制一次单个片段,摄像头和声音合在一个文件(任何网络摄像头录制工具:QuickTime、Photo Booth、Windows Camera、OBS),对着故事板讲述。口误没关系:重新开始句子继续即可。允许多次录制;每次录制会放在一个单独的文件夹中。
5. 编辑片段
uv run $SK/probe.py <recording> takes/<name> # take.wav, cam.mp4, take.json; 检查 still.jpg,如果需要裁剪则添加 --crop 重新运行
uv run $SK/transcribe.py takes/<name> # 两次转录:普通版,以及将填充词写出的版本
uv run $SK/analyze.py takes/<name> # 候选:长停顿(含剪切点)、填充词、重录、杂音
uv run $SK/trace.py takes/<name> <a>:<b> # 将每个剪切点定位到能量轨迹上(添加 --fine 以获得5毫秒精度)
uv run $SK/splicetest.py takes/<name> c1=<a>:<cut start>,<cut end>:<b> # 试听剪切效果:必须听上去是预期的句子
uv run $SK/jump.py takes/<name> <cut start>:<cut end> --slide 0.3 # 检查剪切点在摄像头气泡中的可见程度
# 编写 takes/<name>/edit.json,然后:
uv run $SK/edit.py takes/<name> # edl.json;输出编辑后的旁白和保留的填充词:完整朗读一遍
uv run $SK/build.py takes/<name> # narration-edit.wav, cam-edit.mp4(每次剪切处包含交叉淡入淡出和摄像头溶解效果)
uv run $SK/master.py takes/<name> # 生成 -16 LUFS 的 narration.wav
两种编辑级别(references/editing.md):
- 轻度编辑(默认):删除重录和假开始,修剪死寂(超过约1.1秒的停顿保留0.7秒,场景切换处保留0.9秒)。每个“嗯”和短暂犹豫保留。
- 减少填充词(应要求):额外删除大多数独立于短语之间的“嗯”,并将超过约0.8秒的停顿修剪至约0.55秒。保留任何与相邻词语粘合的填充词。
- 每个剪切点必须位于能量轨迹中的房间噪音区内,并且在纳入前必须通过拼接测试。重录剪切点尽可能在场景切换处进行,以便画面剪切隐藏气泡中的跳动。
6. 根据文字合成
uv run $SK/cues.py takes/<name> # words.json,提示表(最终旁白的片段级转录)
NV_SCRIPTS=$SK ASSETS=assets uv run --with numpy python video.py # 合成脚本 -> comp/;在上下文中打印每个提示
uv run $SK/render.py comp lint
uv run $SK/render.py comp snapshot 1,14.2,22.5,40.1
基于示例编写 video.py(examples/openseo-launch/video.py):从提示表中获取场景开始时间和提示时间(cue times),将摄像头层叠加在捕获之上,并创建与文字同步的编排。示例中的提示位置仅适用于其自己的编辑:需要重新定位每个提示,并在每次编辑更改后读取打印出的提示表。时间规则参见 references/composition.md;最重要的几条:
- 场景在其第一个词语前0.3秒开始,或恰好在该词之前的停顿中的剪切点处开始。
- 每个动作落在命名它的词语上:点击在“复制”一词上,打字在说出搜索词时,缩放数字在其被说出时,指针在命名每个项目时指向它。
- 第一帧是海报帧:整个应用,无淡入。每帧都有内容在移动。
- 每个节拍一个简短标签。对每个场景开始、点击和标签进行截图,并在渲染前查看它们。
7. 渲染并验证
uv run $SK/render.py comp render
uv run $SK/composite.py comp takes/<name> comp/renders/<video>-1080p.mp4
uv run $SK/check.py comp/renders/<video>-1080p.mp4 comp takes/<name>
uv run $SK/filmstrip.py comp/renders/<video>-1080p.mp4 comp/renders/filmstrip.png
在满足以下条件之前,不要将结果交给用户:检查显示每一帧都有窗口、每个摄像头点位于帧偏移0处、音频中位数为0毫秒、音频电平约为 -16 LUFS,并且你已阅读转录并查看了胶片条(references/verification.md)。
8. 审阅与迭代
- 向用户提供 MP4 路径、被剪辑的内容以及需要留意的地方。
- 保持
NOTES.md最新:每个剪辑及其原因、场景开始时间、重建命令。预期会经历多轮修改。 - 变体成本很低:另一种录制、一种轻度或减少填充词的编辑、不同的场景顺序。每个都是独立的编辑文件夹或合成脚本;使用子代理时,以独立文件名并行构建,避免冲突。
硬性规则
- 仅使用真实截图。隐藏不属于故事的部分(如横幅、Cookie 通知);切勿覆盖像素。
- 在渲染前检查捕获和录制内容是否包含秘密或个人数据(API 密钥、他人的邮件和姓名、客户数据、localhost URL);通过画面裁剪或重新捕获来规避,并撤销任何出现在屏幕上的密钥。用户自己的姓名或邮件出现在应用中可以,但需经其确认。
- 用户的录制和登录会话保留在其计算机上。未经要求不得发布任何内容。
常见陷阱:references/gotchas.md。