旁白动画广告
核心原理
听到什么,就看到什么。 旁白决定时间线。先录音,Whisper 为每个单词提供时间戳,所有文本、场景、通知和特效都对齐到具体单词。代码中不写帧编号:使用 at: '客户',时间自动从 public/words.json 中提取。新录音 = 运行 npm run vo,视频将自动重新对齐。
屏幕上没有一秒是静止的。一秒无变化 = 用户滑动。
铁律
- 你听不到。 只有业务所有者是唯一的耳朵。你所有的音频检查必须是数值的(
silencedetect、ffprobe、重新转录)。禁止在没有引用输出的情况下写“音频正常”。 - 先清理静音,再转录。 Whisper 在静音附近的时间戳容易出错。
prep-vo.sh以正确顺序执行这两个步骤。 - 更改音频后,重新运行
npm run vo。 否则所有同步都会偏移。 - 视频在旁白结束后结束 + 8 帧(
time.ts中的DURATION)。render-all.sh会打印每个文件与旁白的时长对比。如果不同,说明有内容被裁剪。 - 文本仅限 y=260 至 y=1570 之间。 上下区域会被 Instagram 遮挡。
validate()会在渲染时阻止并解释,如果文本超出此区域或宽度超过 1000px。 - 特效低于人声。 固定于
Ad.tsx:旁白 1.15,音乐 0.22,每个特效乘以SFX = 0.38。“特效太强” = 更改其中一个数值。 - 先渲染静帧,再渲染 MP4。
npm run check会渲染 10 帧到out/stills。在完整渲染前,查看所有静帧(读取 PNG)。 - 仅使用真实内容。 客户的数字、付款和消息:必须真实。可以隐藏姓名,但禁止捏造结果。这既符合 Meta 政策,也防止用户察觉虚假。
- 希伯来语: 字体(Rubik、Heebo、可变字体、OFL)已在模板中,并通过
delayRender加载。根元素设置为direction: rtl。标签内的数字设置为direction: ltr。
项目首次安装
cp -R ~/.claude/skills/animated-voiceover-ad/template ~/ads/<广告名称>
cd ~/ads/<广告名称>
npm install
npm run setup # 从 Mixkit 下载特效和音乐
需要:Node 18+、ffmpeg、whisper-cli(brew install ffmpeg whisper-cpp)以及位于 ~/.whisper/models/ggml-large-v3-turbo.bin 的 Whisper 模型(如果缺失,words.mjs 会打印下载命令)。
工作流
| 阶段 | 操作 |
|---|---|
| 1. 脚本 | 参照 references/script-formula.md。每个句子预先映射到场景。先展示给业务所有者,再录音。 |
| 2. 录音 | 用手机或 QuickTime,单文件。说错?停顿一秒,重新开始该句子。之后删除错误片段(详见 references/audio.md)。 |
| 3. 清理与转录 | npm run vo -- recording.m4a。打印所有单词及其时间戳。将转录展示给业务所有者,并直接在 public/words.json 中修正 Whisper 识别错误的单词(仅 w 字段)。 |
| 4. 编排 | 仅编辑 src/Ad.tsx:KTS(动感文本)、通知、SCENES。每个时间点对应一个单词,['更多', 2] 表示第二次出现。组件详情见 references/scenes.md。 |
| 5. 检查 | npm run check(tsc + 静帧)。查看所有静帧。 |
| 6. 渲染 | npm run render。MUSICS 中的每首音乐(Root.tsx) = out/ 中的单独文件,用于 A/B 测试。 |
| 7. 修正 | 业务所有者的每条反馈 = 一个单一的永久更改:不同的 at 单词、音量、until。不重写整个场景。 |
付费广告版本
- 音乐: 已内置,3 组可用。
- 不同开头: 前 3 秒决定效果。录制 2-3 个不同的钩子作为单独文件,每个通过
ffmpeg concat与主体拼接,对每个拼接运行npm run vo,并渲染到单独的目录。只要主体单词相同,所有Ad.tsx保持不变。 - 完整字幕: 在
Ad.tsx中设置CAPTIONS = true。单行,根据标点符号换行。由于大多数付费广告观看不带声音,此功能很实用。
常见问题
| 问题 | 解决方案 |
|---|---|
单词 "X" 未在旁白中找到 | 消息会建议相近的单词。希伯来语中,Whisper 可能合并或拆分前缀(ו/ה/ל/ב)。使用 words.json 中实际存在的单词形式。 |
| 渲染显示旧代码 | $TMPDIR/remotion-webpack-bundle-* 中有旧 bundle。render-all.sh 会删除它。 |
| 安装了 TypeScript 7 | Remotion 需要 [email protected]。 |
delayRender 卡住 | --timeout=180000(已在 render-all 中)。 |
| 图章在落下前收缩 | 在其 spring 中设置 overshootClamping: true(已在文件中)。 |
| 清理静音后单词粘连 | 在 prep-vo.sh 中将 stop_duration 提高至 0.35。 |
| 某一秒“无聊” | 在该单词处添加一行 KT、通知或特效。没有一秒是没有事件的。 |
模板演示
模板附带演示旁白(TTS 语音,public/vo.wav + public/words.json)以及与之同步的 Ad.tsx,因此可以在录音前看到一切正常工作:npm install && npm run setup && npm run check。对于真实广告:npm run vo -- recording.m4a 会覆盖两者,然后根据脚本重写 Ad.tsx。