产品视频 是做什么的?
构建一个 60 至 120 秒的叙事视频,使用产品的真实屏幕来展示它。输出是一个 HyperFrames 项目(一个生成的 index.html 加上素材)和一个渲染后的 MP4 文件。
所有交付物(脚本、屏幕文字、README)均以观众的语言编写。本文件仅因指令性质使用英文。
输出布局
每个视频创建一个文件夹,放在任何代码仓库之外,除非用户另有说明:
<Product>-video/
ROTEIRO.md | SCRIPT.md 脚本、声明表格、待确认点
LEIA-ME.md | README.md 哪些是真实的、哪些是重建的、限制、如何重新运行
01-.. 0N-.. 捕获的素材,每个来源一个文件夹
voz/ | voice/ 旁白片段(文本和每个场景的音频)
ferramentas/ | tools/ 配置文件和 gen.js(合成生成器)
hyperframes/
index.html 生成的文件。切勿手动编辑。
assets/{img,video,audio,fonts}
renders/
工作流
按顺序执行各个阶段。每个阶段都以检查结束;检查通过之前不要开始下一个阶段。
P1. 来源
- 找到用户提到的每个来源。来自其他机器的路径将不存在:在当前用户的文件夹下搜索同一项目,并说明你使用了哪一个。
- 分类产品如何展示:
- 在线应用,可访问: 捕获它(P3,
site-capture.cjs,record-interaction.cjs)。 - 公共 Figma 文件: 通过原型视图捕获帧(P3,
figma-frames.cjs)。无需令牌。 - 需要登录且无法访问: 索要屏幕截图或测试账号。不要凭记忆重建屏幕。
- 在线应用,可访问: 捕获它(P3,
- 提取产品自己的文字:落地页文案、功能名称、数字、FAQ、术语。这些是声明唯一允许的来源。
检查: 你能够说出观众是谁、产品的模块以及每个计划使用的事实的来源。
P2. 脚本
阅读 references/script.md 并根据其模板编写脚本文件。最重要的规则:
- 每秒钟 2.3 到 2.6 个口语词。250 个词大约对应 100 秒。
- 每个场景一个想法,每个场景 8 到 16 秒,共 6 到 9 个场景。
- 旁白中的每个声明在声明表格中都有一行及其来源。去掉任何来源无法支持的内容。
- 为语音而写:品牌名称按发音书写,数字拼写出来,缩写要展开(避免语音逐字母读出)。
检查: 声明表格中没有空的来源单元格,“待确认点”列表列出了所有来自营销文案的数字。
P3. 素材
阅读 references/capture.md 了解配置示例。
| 来源 | 脚本 | 输出 |
|---|---|---|
| 在线 Web 应用,静态 | scripts/site-capture.cjs | 首屏、整页、步骤截图、滚动视频、裁剪图 |
| 在线 Web 应用,流程 | scripts/record-interaction.cjs | 带光标或点击覆盖的交互片段 |
| 公共 Figma 文件 | scripts/figma-frames.cjs | 每帧一个 PNG,裁剪到帧边界 |
同时收集品牌元素:标志(优先矢量格式)、品牌字体文件及其许可证、以及来自产品 CSS 的颜色令牌。使用 scripts/page-styles.cjs <url> 读取产品自身页面使用的样式:标题字体族、粗细、行高和字间距、按钮样式以及任何毛玻璃表面。视频复制这些样式而不是猜测。
检查: 打开几个捕获结果并查看。显示登录墙、Cookie 横幅或开发覆盖层的捕获是失败的捕获。
P4. 语音
阅读 references/voice.md。在生成任何内容之前询问用户想要的语音:首先是通过 MCP 或 API 密钥连接的付费服务,其次是用户提供的旁白。免费本地模型仅适用于主动要求它的用户,因为它会在用户机器上安装模型。
- 通过 MCP 的语音服务: 将整个旁白生成为一个连续的录音(每个场景一个段落,开头一个传递标签)。使用
scripts/voice.py --existing对齐,使用scripts/split-take.py --level在暂停处分割,然后使用scripts/voice.py --parts "voz/cena-{id}.wav"合并场景文件。场景边界精确,从第一个场景到最后一个场景语气保持一致。逐场景请求的旁白会在请求之间改变情绪。 - 通过 API 密钥的语音服务:
scripts/voice.py --provider elevenlabs --el-voice <id>生成并合并。scripts/elevenlabs.py检查连接、对语音进行排名并合成音乐。 - 用户提供的旁白: 传入
--existing。场景边界来自将转录稿与脚本对齐。
使用富有表现力的模型时,在录音开头设置一个 [audio tag] 传递标签。每个场景携带 text(干净的、用于字幕)和可选的 tts(语音实际接收的内容,当两者不同时)。
在所有模式下,字词时间戳都与脚本文本对齐,因此字幕永远不会显示转录错误。阅读它打印的 misheard 列表:除了拼写出来的数字和同音词之外,任何内容都是发音问题。修正脚本文本并重新生成。
在合成之前将旁白标准化到大约 −18 LUFS。
检查: voiceover.json 存在,总时长在目标的 10% 以内,misheard 已解释,且 scripts/voice-tone.py --scenes 报告场景之间的中位音高差约为 3 个半音或更少。
P5. 合成
阅读 references/hyperframes.md(渲染器执行的规则)并从 assets/gen-template.js 开始。
- 从脚本生成
index.html。每次语音变化时时序都会变化;生成器能吸收这种变化,手动编辑则不能。 - 将每个动画锚定到旁白的短语(
at(3, 'No painel')),切勿锚定到单个短词。“no”、“e”和“um”在每个场景中出现多次,第一个匹配项是错误的。 - 保留帧底部 17% 的区域用于字幕。其他任何内容不得进入该区域。
- 在浏览器窗口或设备样机内显示真实屏幕。将解释放在其旁边,而不是覆盖在上面。
- 优先选择每句话一个清晰的亮点(一个环、一个缩放、一次光标移动),而不是很多小亮点。
- 标题采用产品自身的标题样式,标签采用其表面语言(均在 P3 中读取)。不要默认选择最粗的权重。
- 对于故事、Reels 或 Shorts,阅读
references/vertical.md并从同一旁白构建第二个竖版合成,从assets/gen-vertical-template.js开始。切勿裁剪 16:9 的版本。
检查: npx hyperframes lint 返回 0 个错误。
P6. 验证
npx hyperframes check必须以“Check passed”结束(lint、运行时、布局、动画、对比度)。npx hyperframes snapshot --no-end --at <每个场景一个时间点,外加每个亮点>并查看每个帧。使用 ffmpeg 构建 2×2 审查板以加快审查速度。- 将每个帧与旁白在该秒所说的内容进行比较。常见缺陷:错误的屏幕对应句子、亮点在错误元素上、文本换行、元素进入字幕带。
- 渲染一个草稿(
render -q draft),然后验证 MP4 本身:使用ffprobe检查时长和流,使用loudnorm检查响度(目标 −16 到 −17 LUFS 带音乐),以及使用 ffmpeg 提取帧。
检查: 你已查看了从 MP4 中提取的每个场景的至少一个帧,而不仅仅是来自快照。
P7. 交付
编写 README 包括:哪些是真实的(捕获的)、哪些是使用真实数据在 HTML 中重建的、哪些是概念性的、已知限制、预览、检查和渲染的命令,以及如何更换语音。然后向用户报告:已制作了什么、已验证了什么,以及仍需要人为决定的事项(未经确认的数字、音乐、语音质量)。
已付过代价的陷阱
| 代码 | 症状 | 原因和修复 |
|---|---|---|
| X1 | 屏幕提前一秒切换 | at() 匹配了第一个“no”或“e”。锚定到短语。 |
| X2 | 元素在整个场景中保持隐藏 | 同一元素上有两个 fromTo 动画;后面的动画会立即渲染其“from”状态。每次出现使用一个元素,或从 CSS 初始状态使用 to()。 |
| X3 | 网站对脚本返回 403 但能在浏览器中打开 | 无头用户代理。使用 lib/browser.cjs,它发送桌面 UA。 |
| X4 | 启动时“Executable doesn't exist” | 安装的浏览器构建与 Playwright 版本不同。lib/browser.cjs 回退到最新的本地 Chromium,然后是 Chrome。 |
| X5 | 整页截图在一个折叠处被截断 | 应用滚动内部容器。找到最大的可滚动元素并将视口扩展到其高度。 |
| X6 | 滚动录制卡顿 | 屏幕投影帧率不稳定。改为逐帧渲染滚动。 |
| X7 | 你不断检查一个从未改变的文件 | 录制器写入的文件夹与你检查的不同。打印并验证输出路径。 |
| X8 | 计数器在巨大的“%”旁边显示得很小 | 后代选择器(.num span)也匹配了内部计数器 span。对标签使用子选择器。 |
| X9 | 字幕显示错误的词 | 字幕来自原始转录稿。将时间戳与脚本文本对齐(voice.py 完成此操作)。 |
| X10 | 语音错误发音品牌或术语 | 按口语形式书写(一个写成一个词的品牌会被读成两个词,外来术语用本地短语表达),拼写数字,展开缩写。 |
| X11 | 第二次捕获运行擦除了第一次的索引 | 跨运行合并索引文件而不是覆盖。 |
| X12 | 定时包装内的视频检查失败 | 定时媒体自身携带 class="clip";其容器没有时序。 |
| X13 | 原型捕获包含 toast 或 Cookie 栏 | 截图前关闭并隐藏覆盖层,然后裁剪到帧。 |
| X14 | 录制显示仅原型按钮 | 录制前通过选择器或文本前缀隐藏测试和开发控件。 |
| X15 | 产品无法证实的声明 | “第一”、“总计”、“保证”需要来源。去掉它们或移到“待确认点”。 |
| X16 | 营销数字没有依据 | 使用前检查其来源:搜索文档和页面的 git 历史(git log -S'<number>')。手工在文案编辑中改动的数字不是证据。丢弃它。 |
| X17 | 竖版裁剪难以阅读 | 9:16 中的桌面屏幕有 9 像素文本,裁剪后看起来仍然像桌面。像产品的移动端落地页那样,在面板上一次展示一个适配后的元素(references/vertical.md)。 |
| X18 | 旁白听起来戏剧化或兴奋 | 非语言标签([sighs])、[excited] 和 [serious] 在产品视频中过度。使用 [calm] 和 [warm],仅重新生成指定的场景,并使用 scripts/voice-tone.py 比较录音。 |
| X19 | 布局读起来像网页 | 左对齐列、部分标签在标题之上、小标志在角落。在竖版视频中:居中所有内容,去掉标签,使标志更大并居中。 |
| X20 | 标语距离标志很远 | 标志导出有透明填充。裁剪文件并相对于实际标记定位。 |
| X21 | 旁白在某些场景平淡,在其他场景兴奋 | 每个场景是带有自己标签的单独请求,因此每次都是独立的表演。生成一个连续录音,使用一个标签,用 scripts/split-take.py 在暂停处切割,并用 scripts/voice-tone.py --scenes 测量。场景之间 5 半音的分散听起来像情绪波动,2 半音则均匀。 |
| X22 | 标题看起来很粗且不像产品 | 粗细被假设为 800。使用 scripts/page-styles.cjs 读取产品自身的标题样式,复制字体族、粗细、行高和字间距。 |
| X23 | 标题中词连在一起 | 从网页复制的紧密字间距在视频尺寸下关闭了词间距。添加 word-spacing: .07em。 |
| X24 | 玻璃标签看起来像纯白,或其模糊在淡入后突然出现 | 背景太不透明(保持下半部分接近 0.6),或者淡入在父元素上:祖先的不透明度低于 1 会切断元素与背景的联系。动画应放在带有 backdrop-filter 的元素上。 |
| X25 | 对比度检查在看起来正常的文本上失败 | 检查采样了淡入中间的帧。在报告的时间点拍摄快照。如果元素正在进入,将进入时间移动几帧并重新运行检查。非常短的合成最容易遇到此问题。 |
本技能包含的文件
references/script.md:脚本模板、节奏、声明表格、发音表格。references/capture.md:三个捕获脚本的配置、捕获规格、品牌资产。references/voice.md:TTS 设置、语音、对齐、响度、音乐、更换旁白。references/hyperframes.md:合成规则、CLI 命令、布局区域。references/vertical.md:适合故事的 9:16 竖版:安全区域、屏幕上的相机、字幕、拆分为多部分。assets/gen-template.js:16:9 合成生成器,复制到项目的 tools 文件夹。assets/gen-vertical-template.js:竖版生成器,包含完整版和短版。scripts/lib/browser.cjs,scripts/site-capture.cjs,scripts/record-interaction.cjs,scripts/figma-frames.cjs,scripts/element-shots.cjs,scripts/page-styles.cjs,scripts/voice.py,scripts/split-take.py,scripts/voice-tone.py,scripts/elevenlabs.py,scripts/lib/eleven.py。
ElevenLabs API 代码(--provider elevenlabs,elevenlabs.py)编写时没有用于测试的密钥。首次实际运行可能需要修复;--parts 路由和本地路由已经过测试。