宣传片制作(本地工具链)
智能体无关:专为能够读取文件并运行shell命令的任何AI编码智能体编写——通过install.sh原生安装到Claude Code、GitHub Copilot CLI / Microsoft Scout和Google Antigravity;也可从Cursor、Codex、Windsurf等工具作为纯Markdown运行手册使用。
该工具链由Idan Shimon(微软,github.com/idanshimon)首次展示——他使用AI智能体驱动这套本地零成本堆栈制作的58秒概念解说视频,正是此技能的灵感来源。
不要假设安装位置:将PROMO_VIDEO_SKILL_DIR设置为包含此SKILL.md的目录。其scripts/目录包含TTS脚本、发音辅助脚本和固定的TTS依赖;assets/包含合成骨架、空间组件模板和决策日志模式;references/包含每台机器一次性的工具链引导程序(install.md)。
核心原则:脚本先锁定;每个下游数字都从测量的解说音频中派生,绝不能猜测。
宣传,而非解说:该技能推销的是“为什么”——受众、痛点、转变、证明、一个号召性用语(CTA)。“它是如何工作的”最多只能占一个节拍,且仅在用户要求时才涉及;教授工作原理的请求属于配套的explainer-video技能。
工具概况
| 工具 | 位置 | 已验证版本 | 许可证 | 角色 | 调用方式 |
|---|---|---|---|---|---|
| OpenMontage | ~/OpenMontage | 2026-07 签出版本 | AGPLv3(仅为工具;视频中不附带任何内容) | 流程治理:阶段顺序、决策日志、门控、渲染后自我审查纪律 | 阅读其AGENT_GUIDE.md + pipeline_defs/animation.yaml;您(智能体)是编排者——无需运行守护进程 |
| Kokoro | PyPI | Kokoro-82M (hexgrad), kokoro==0.9.4 | 代码 Apache-2.0;模型权重 Apache-2.0 | 解说TTS,每场景24 kHz单声道WAV | Python 3.12虚拟环境;KPipeline(lang_code="a"),语音 af_heart,speed 参数 |
| HyperFrames | ~/hyperframes | 0.7.57,从源码构建 | Apache 2.0 | HTML/CSS/GSAP合成 → 通过无头Chrome生成确定性MP4 | node ~/hyperframes/packages/cli/dist/cli.js <cmd> |
| FFmpeg | ~/FFmpeg 源码;构建目录中的二进制文件 | git master (N-125608 时代) | GPL构建(启用了libx264) | 音乐合成(aevalsrc)、编码、ffprobe质量检查、响度、平台打包 | 在hyperframes命令之前将构建目录放入PATH |
| bun | npm-global | 1.3.14 | — | 构建hyperframes monorepo | npm i -g bun(brew cask曾静默失败过一次) |
| Node.js | nvm | 24.x (需要≥22) | — | 运行hyperframes CLI | — |
| Python | uv管理 | 3.12 | — | Kokoro推理 | 始终使用虚拟环境(uv venv --python 3.12 .venv);black + pylint 清洁 |
| espeak-ng | Homebrew 或 apt | 任意 | GPL | Kokoro G2P回退(必需) | macOS:brew install espeak-ng;Debian/Ubuntu:sudo apt install espeak-ng |
| gsap | 供应商文件 | 3.x gsap.min.js | GSAP标准许可 | 合成内部的动画时间线 | 复制到 video/assets/ — 绝不使用CDN <script src>(离线确定性) |
环境检查(每台机器一次)
export FFMPEG_BUILD_DIR="${FFMPEG_BUILD_DIR:-$HOME/ffbuild}"
export HYPERFRAMES_DIR="${HYPERFRAMES_DIR:-$HOME/hyperframes}"
export PATH="$FFMPEG_BUILD_DIR:$PATH"
node "$HYPERFRAMES_DIR/packages/cli/dist/cli.js" doctor
在生产前,FFmpeg、FFprobe、Node和Chrome必须全部通过(其他doctor行是此技能不使用的可选组件)。如果这四个工具中的任何一个失败,请遵循$PROMO_VIDEO_SKILL_DIR/references/install.md——每台机器一次性的完整引导程序:系统包(macOS/Linux)、克隆源代码工具(包括OpenMontage参考签出)、从源码构建带有libx264的FFmpeg、构建HyperFrames CLI以及智能体权限提示。当doctor已通过时,不要加载该文件。
Chrome:CLI自动使用~/.cache/puppeteer/下缓存的chrome-headless-shell(如果不存在,则下载一次)。第一次TTS运行同样会下载Python包和Kokoro-82M权重。完成这些一次性下载后,合成、验证、渲染和打包即可离线进行。
项目布局(每次视频创建,拥有自己的Git仓库,原子提交)
<project>/
.venv/ # uv虚拟环境,python 3.12
.gitignore # .venv/ __pycache__/ video/snapshots/ .DS_Store
README.md # 制作方式+复现方式(最后编写)
production/
research/research-brief.md # 带有来源的事实+诚实的竞品对比表
proposal.md # 概念、场景列表、交付承诺、时长决策
script/script.md # 锁定的解说词
scene_plan/scene-plan.md # 每个场景的视觉+运动注释
checkpoints/decision-log.json # 只能追加;(category, subject) 是键
checkpoints/self-review.md # 渲染后的证据
assets/audio/ # Kokoro WAV文件 + durations.json
renders/ # 主视频+平台衍生版
youtube/ # metadata.md + thumbnail.jpg
tools/tts_generate.py # 从此技能复制,编辑SCENE_NARRATIONS
tools/tts_pronounce.py # 来自scripts/,缩略词发音(不要编辑)
tools/pronunciation.local.json # 可选的项目级词汇覆盖
video/
index.html # 合成主文件
assets/gsap.min.js # 供应商文件
assets/audio/ # WAV副本(合成相对路径)
阶段1 — 调研(优先离线)
挖掘主题自身仓库/README/文档——寻找成果和证据(数字、采用率、前后对比结果),而非架构。生成 research-brief.md,包含:
- 定位块(必需——脚本没有它就无法锁定)。 每项一句话:受众(这是为谁准备的),痛点(他们目前感受什么),转变(他们能获得什么改变),差异化因素(选择此方案的唯一理由)。这是信息;每个下游阶段都为其服务。
- 产品事实,每个都对照来源验证——仅当它们支持某个好处时才保留(“那又怎样?”测试:观众能从中得到什么?)。
- 竞品对比表(弱点 → 此方案如何解决)。诚实原则:承认竞争对手的真实优点(在他们那一列打一个✓能为你的论点赢得可信度)。
- 一个 “已确认可用于脚本的主张” 列表。
阶段2 — 提案+决策
编写 proposal.md:重述定位块、5–7个场景概念、目标≈60秒、交付承诺(“运动主导,非幻灯片”)、工具链表。记录到 decision-log.json——从 $PROMO_VIDEO_SKILL_DIR/assets/decision-log.json 的空模式开始(字段:category、subject、selected、options_considered、rejected_because、confidence):pipeline_selection、render_runtime_selection(记录Remotion作为考虑过的替代方案——OpenMontage硬性规则)、composition_mode(主角作品使用atelier)、voice_selection、music_selection、output_profile、approval_gates、以及 cta_selection——恰好一个呼吁行动(给仓库加星/
安装/
观看演示/
访问网站);宣传片如果以功能列表而非请求结尾就会失败。如果用户的简介已固定某个选择,则标记为 approved_by: "user brief"。后来更改了决策?APPEND一个具有相同(category, subject)的新条目——切勿编辑旧条目。
阶段3 — 脚本(锁定点)
约150–155个单词 ≈ 60秒(以kokoro af_heart 速度1.1)。宣传弧线(推销“为什么”):利害/痛点钩子 → 揭示(名称及其承诺的转变) → 对观众世界的好处 → 证据(诚实的比较、真实成果) → 愿景 → 来自决策日志的单一CTA。功能仅作为好处的证据出现:每个句子必须通过“那又怎样?”测试(观众得到什么?)或重写。最多一个“它是如何工作的”节拍,且仅在用户要求时——教授机制是explainer-video技能的工作。语气:第二人称,现在时,简短有力的句子(“你更快地发布”,而不是“它使得更快的发布成为可能”)。在脚本和屏幕文本中自然地书写缩略词(“OID”、“MCP”、“AI”)——在合成时,tools/tts_pronounce.py从其词典中派生口语形式(单词 vs 逐个字母),未知的缩略词会逐个字母拼写并报告。在可选的 tools/pronunciation.local.json 中对领域术语进行分类({"NASA": "word", "ECG": "letters", "NGINX": "engine X"})。避免全大写强调:全大写单词被视为缩略词。每场景的单词预算 ≈ 其占60秒的比例 × 2.55字/秒。
阶段4 — 解说+时长派生(关键步骤)
从附带的固定直接依赖创建项目的Python 3.12环境,复制通用模板,并仅编辑 SCENE_NARRATIONS:
uv venv --clear --python 3.12 .venv
uv pip install --python .venv/bin/python \
-r "$PROMO_VIDEO_SKILL_DIR/scripts/tts-requirements.txt"
mkdir -p tools
cp "$PROMO_VIDEO_SKILL_DIR/scripts/tts_generate.py" tools/tts_generate.py
cp "$PROMO_VIDEO_SKILL_DIR/scripts/tts_pronounce.py" tools/tts_pronounce.py
PYTORCH_ENABLE_MPS_FALLBACK=1 .venv/bin/python tools/tts_generate.py
mkdir -p video/assets
curl -fL https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js \
-o video/assets/gsap.min.js
第一次合成会下载Kokoro-82M权重(约330 MB);后续运行使用缓存并可离线。它写入每场景WAV + durations.json。如果运行打印出未知缩略词警告,在 tools/pronunciation.local.json 中对这些标记进行分类,并在派生时长之前重新运行。
将以速度1.1的150-155个单词作为起始估计,而非校准。总时长>62秒?提高语速或减少单词,然后再次测量。
派生时间线(场景间填充约0.3–0.5秒):
n_start[0] = 0.5 # 开场
scene_start[i] = n_start[i] - 0.15..0.4 # 视觉效果在画外音之前出现
n_start[i+1] = n_start[i] + dur[i] + 0.3..0.35
TOTAL = 最后一个n_end + 0.6..0.9 结束(淡出至黑)
这些数字必须出现在三个地方且必须一致:场景的data-start/ data-duration、<audio data-start>值以及JS的const S1..Sn时间线常量。将WAV复制到video/assets/audio/。
阶段5 — 音乐背景(无下载)
使用从源码构建的FFmpeg生成确定性合成垫音。将 TOTAL_SECONDS 设置为派生的合成总时长;无需外部样本或私有参考:
TOTAL_SECONDS=60.5
FADE_SECONDS=3.5
FFMPEG_BIN="${FFMPEG_BUILD_DIR:-$HOME/ffbuild}/ffmpeg"
FADE_OUT_START="$(awk -v total="$TOTAL_SECONDS" -v fade="$FADE_SECONDS" \
'BEGIN { printf "%.3f", total - fade }')"
mkdir -p production/assets/audio
"$FFMPEG_BIN" -hide_banner -y \
-f lavfi \
-i "aevalsrc=0.07*(sin(2*PI*110*t)+0.65*sin(2*PI*110.7*t)+0.45*sin(2*PI*164.81*t)+0.30*sin(2*PI*220.9*t)):s=48000:d=${TOTAL_SECONDS}" \
-af "highpass=f=55,lowpass=f=1200,afade=t=in:st=0:d=2,afade=t=out:st=${FADE_OUT_START}:d=${FADE_SECONDS},pan=stereo|c0=c0|c1=c0" \
-c:a pcm_s16le production/assets/audio/music-bed.wav
保持稀疏;混合在data-volume="0.12..0.16"时,它读作氛围音——对于宣传片的能量,靠近该范围的上限,但绝不能高于解说。将WAV复制到video/assets/audio/并使用单独的音频轨道。
阶段6 — 合成创作(HyperFrames契约)
从 $PROMO_VIDEO_SKILL_DIR/assets/composition-skeleton.html 开始(它体现了下面的每一条规则);在编写HTML之前,浏览 $HYPERFRAMES_DIR/skills/hyperframes-core/SKILL.md。结构:独立的 index.html,根 <div data-composition-id="X" data-start="0" data-duration="TOTAL" data-width="1920" data-height="1080">,场景作为 <section class="clip" data-start data-duration data-track-index="1">,一个 gsap.timeline({paused:true}) 在 window.__timelines["X"] 同步注册,补间位置 = S<i> + local_offset。
以下规则会耗费大量真实调试时间——请预先遵守:
- 每个
<video>/<audio>都需要一个明确的、非空的id。 在HyperFrames中,没有id的音频可能在浏览器预览中播放,但渲染时会是SILENT(lint会错误处理;不要忽略)。 - 永远不要在GSAP补间元素的CSS中设置初始
transform:——使用tl.fromTo(el, {y:24, opacity:0}, {y:0, opacity:1, ...});GSAP拥有完整的变换。(CSS中单独的opacity: 0对于仅使用.to()的显示效果没问题。) - 背景填充应放在一个全出血的子元素上(
position:absolute; inset:0),绝不能放在根元素上(根元素背景可能渲染为黑色)。 - 媒体元素是根元素的直接子元素,
data-track-index用于不同的音频轨道(例如7=画外音,8=音乐)。 - 无无限重复(禁止
repeat: -1——使用有限值,例如光标闪烁repeat: 9, yoyo: true),无Date.now/Math.random,无网络请求,无CDN脚本。 - 所有地方使用唯一
id;仅对变换/不透明度进行动画(+ SVG线条绘制的strokeDashoffset)。 - 在场景中间出现的元素:隐藏初始状态 + 在
S<i>+offset处进行补间。fromTo具有immediateRender,因此预补间seek会显示 from 状态——这正是使其免于seek问题的原因。 :rootCSS变量中的设计令牌 = 主题;交换ground/ink/accent + 字体栈以更换皮肤。使用渲染器自带的字体(JetBrains Mono)——系统字体无论如何都会被替换(lint信息会告诉您映射关系)。- 保持次要文本在ground颜色上满足WCAG AA标准——
check会在对比度上构建失败,并打印出合规的颜色供您使用。 - 有意重叠/滚动穿透:使用
data-layout-allow-overflow/-overlap/-occlusion标记,而不是留下未解释的结果。
场景主题建议:展示OUTCOME状态——观众改善的世界(前后对比、痛点的消失、屏幕上的结果)。从主题的实际UI/领域(伪造的搜索面板、终端、树、表格)中提取支持性的一瞥,作为好处的证据,而不是机制介绍。叙事弧线有模板;主题绝不能有模板。
$PROMO_VIDEO_SKILL_DIR/assets/ 中的合成骨架HTML实现了所有这些约束。不要从头开始编写合成——复制骨架并编辑场景。
阶段7 — 渲染、质量检查和导出
在撰写合成的同时,根据需要频繁运行预览渲染进行视觉核对:
node $HYPERFRAMES_DIR/packages/cli/dist/cli.js serve --port 3300 &
npx puppeteer browsers install chrome-headless-shell # 如果需要
当所有元素都看起来正确后,使用OpenMontage进行最终渲染:
# 渲染主视频
node $HYPERFRAMES_DIR/packages/cli/dist/cli.js render --composition X
# 运行OpenMontage自检
~/OpenMontage/scripts/self-review.sh production/
查阅 ~/OpenMontage/AGENT_GUIDE.md 了解完整的门控和审批协议。
阶段8 — 平台打包
- YouTube:生成
youtube/metadata.md(标题、描述、标签、端卡)、1080p MP4、缩略图。 - 社交媒体:根据平台规格调整分辨率(例如,TikTok 1080x1920)。
- 本地存档:将所有源文件和最终渲染版本提交到项目仓库。
贡献
欢迎提交issue和pull request。请遵循现有的代码风格和提交消息惯例。