Morph Edit 动态辅助素材(motion B-roll)
你为创作者的视频制作动态图形剪辑。每个剪辑都是一个从不剪切的形状:它在不同状态间变形大小、圆角与颜色,同时内容通过短暂模糊进行切换。一个光标通过真实的点击和拖拽驱动变化,且每个变化都落在某一个口头词语上。
输出是一组剪辑,创作者可直接拖入其编辑器。你还需提供一段将剪辑嵌入后的视频预览渲染,以及两个 HTML 页面:一个用于逐段查看剪辑,另一个用于对比原始与效果。
你所需的一切均在此技能文件夹中:
engine/:运动引擎 (motion.js)、页面外壳 (base.css)、build.py(将剪辑生成为独立 HTML)、render.js(帧逐帧渲染,含运动模糊)、beats.js(静帧接触表)、字体(Geist,OFL 许可)。scripts/:setup.sh、inspect_video.py、words.py、composite.py、make_pages.py。reference/engine-api.md:如何编写剪辑。在编写第一个剪辑之前请先阅读。examples/opus-aoe2/:针对一个真实 54 秒视频的六个完成剪辑。请将其作为质量基准和起点。
下文中的 $SKILL 表示此技能文件夹的路径。请在用户项目的 motion/ 文件夹中工作。
1. 环境设置(仅首次运行)
bash $SKILL/scripts/setup.sh ./motion
该脚本检查 Node、Python 3 和 ffmpeg,并将 Playwright + Chromium 安装到 motion/node_modules。运行所有引擎脚本时请使用 NODE_PATH=./motion/node_modules。
2. 用户访谈
使用 AskUserQuestion 工具;仅限一轮。你需要了解:
- 视频。一个路径。将其复制或引用为
motion/work/source.*。 - 文本稿。可选。如果没有(或只有纯文本),请使用
scripts/transcribe.py转录音频(参见步骤 3)。这会提供真实的词语时间戳,比 SRT 估计更精确。 - 密度:
- 轻度:每分钟 2–4 个剪辑,仅用于最强时刻。
- 中度(默认):每分钟 4–7 个。
- 重度:覆盖大部分台词,面部画面短暂出现。
- 外观:
- 默认调色板:暖灰画布、黑色和白色组件、一个橙色强调色。
- 或使用他们的品牌:颜色,可选字体。请用户将Logo、截图或品牌色放入
motion/inputs/中,并读取它们。
- 需要避免或包含的内容。 保留在面部画面的台词、需要显示的真实数字或名称、需要重新创建的产品截图。
跳过用户已经回答的任何问题。
3. 检查素材
python3 $SKILL/scripts/inspect_video.py motion/work/source.mp4 motion/work
python3 $SKILL/scripts/words.py transcript.srt > motion/work/words.txt
# 如果没有文本稿(或需要精确词时间):改用音频转录
motion/.venv/bin/python $SKILL/scripts/transcribe.py motion/work/source.mp4 motion/work
transcribe.py 会生成 words.txt 和 transcript.srt。向用户展示转录文本,以便他们在规划前纠正误听的名字或术语。
自己查看 motion/work/contact.png。video.json 提供分辨率、帧率和布局分区:
- full: 演讲者充满画面。
- pip: 演讲者位于小窗中,其余区域为空白。包括小窗位置以及当小窗大小或位置变化时的警告。
剪辑必须与视频的分辨率和帧率匹配。来自 SRT 的文字时间仅为估计值(±0.2 秒);请按此对待。
4. 规划并获得批准后再编写代码
显示一个表格,每行对应一个剪辑:
| # | 入点–出点 | 覆盖的台词 | 形状在哪些词语上做什么 | 处理方式 | 理由 |
创意方向:决定每个剪辑占据画面的多少。 这是最重要的决策;请对每个剪辑单独做出,并说明理由。
- 全幅切换。 画面全部是演讲者,且台词描述的是需要看到的内容:产品、过程、对比、数字、章节变化。
- 在创意持续期间(3–10 秒)切换走,然后切回面部。
- 不要在钩子的第一秒、或涉及个人、情感、观点台词时切换。
- 每次切换之间至少保留约 2 秒的面部画面。
- 在空白区域放置面板。 编辑本身留有空间:画中画框、分割区域、纯色区域。
- 渲染一个透明剪辑(
bg:null),居中放置于空白区域,大小时刻与演讲者小窗保持距离。 - 它可以持续较长时间(15 秒以上)作为单个连续变形,因为演讲者始终在屏幕上。
- 如果
inspect_video.py报告小窗尺寸变化,请询问这是否有意为之,然后选择最大尺寸或在变化点分割面板。
- 渲染一个透明剪辑(
- 不处理。 台词是关于演讲者的,或者与其他剪辑太接近。
内容规则:
- 展示主题的真实对象:文件夹、终端、文件、产品、结果。从文字中提取它们。将每个想法映射到
reference/engine-api.md词汇表中的互动(柱状+点击、进度、检查、状态岛、带拖拽的卡片、滑块、开关、标签页、图表+提示框、搜索/筛选、文件拖放、终端打字、并排对比、章节卡片)。 - 切勿编造数字、引语、价格或结果。 使用相对条、骨架文本行或从转录中提取的标签,并列出哪些是示意性的。只有用户提供真实数据时才放入。
- 每个剪辑一个想法。一个连续片段(如整个画中画区域)可以是一个包含多个状态的单个长剪辑。
- 节奏跟随说话,而非音乐:每个口头节拍对应一次变化,间隔约 0.4–1.2 秒。
等待用户批准或修改规划。
5. 构建
在 motion/clips/NN-name.html 中为每个剪辑编写一个片段,遵循 reference/engine-api.md 和示例。然后构建:
python3 $SKILL/engine/build.py motion/dist motion/clips/*.html
将每个状态变化放在对应的词语上:剪辑本地时间 = 词语时间 − 剪辑入点。
6. 检查关键词语上的静帧
NODE_PATH=./motion/node_modules node $SKILL/engine/beats.js motion/dist/NN-name.html motion/work/NN.png 0.4 1.2 2.1 …
选择每个状态稳定后的时间点以及一些变形过程中的瞬间。亲自检查每个静帧表。修复任何拥挤、裁剪、不可读、偏离词语的问题,或光标移出画面的情况。然后再次检查。面板剪辑在静帧表中以黑色背景渲染,以便你判断。
7. 渲染
NODE_PATH=./motion/node_modules node $SKILL/engine/render.js motion/dist/NN-name.html motion/out/NN-name_0m32s40.mp4 30000/1001
- 使用视频的帧率。
- 面板剪辑(
bg:null)必须保存为.mov,这将生成含 Alpha 通道的 ProRes 4444。 - 按时间线入点命名每个文件(
0m32s40表示 0:32.40)。 - 渲染每帧耗时 4 个子帧,因此比实时慢。在多核机器上可并行运行两个剪辑。
8. 交付
编写 motion/plan.json(模式见 reference/engine-api.md)。对于 UI 音效,为每个剪辑提供 "sfx": [[剪辑本地时间, 类型], …] 列表,从提示中提取:click 和 toggle 在光标点击时、pickup/drop 在拖拽开始/释放时、whoosh 在形状变形时、pop 在介绍和徽章上、rise/fall 在柱状图上、type 在打字时、tick 在元素出现时、shutter 在截图时、success 在检查通过时。然后:
python3 $SKILL/scripts/composite.py motion/plan.json motion/out/preview.mp4
python3 $SKILL/scripts/sfx.py motion/plan.json motion/out/preview.mp4 # UI 音效,如果用户需要
python3 $SKILL/scripts/make_pages.py motion/plan.json motion/out/preview.mp4
python3 $SKILL/scripts/make_gallery.py motion/plan.json motion/out/preview.mp4
用户将收到(均在 motion/out/ 中):
- 剪辑文件
TIMING.md:文件、入点、出点、覆盖的台词、处理方式、以及哪些是示意性的preview.mp4:嵌入剪辑后的视频,硬切,原始音频(以及使用时带 SFX 的版本)sfx/:每个剪辑的单独音效 WAV 文件,用于在编辑器中混音index.html:所有剪辑同时在网格中播放,包含完整预览和指向其他页面的链接(先打开此文件)viewer.html:逐段查看剪辑compare.html:原始版本与预览版本同步对比,支持并排、堆叠或擦拭方式
明确说明预览仅用于审阅:最终剪辑时,他们需将剪辑放入自己的编辑器,在那里可以微调时间、添加过渡和混音。
订阅卡片(可选)
询问创作者是否希望在其行动号召台词上添加订阅结束卡片,以及一两条简短提醒。模板位于 $SKILL/templates/subscribe/(end-card.html、reminder.html)。将其复制到 motion/clips/ 中,然后:
- 替换
__CHANNEL__、__HANDLE__和__AVATAR__(创作者的Logo或照片作为 data URI;若无图片,则使用圆圈中的字母)。请询问这些信息;切勿猜测名称或句柄。 - 根据创作者的实际话语重新调整
<script>顶部的常量(IN、点击时间、T)。结束卡片在创作者说出词语时点击“分享”、“订阅”、“铃铛”、“点赞”和“评论”;提醒卡片点击“订阅”和“铃铛”。 - 像其他剪辑一样构建、检查静帧、渲染并添加到
plan.json。将卡片渲染为透明.mov面板,以便叠加在素材上。
字幕(可选)
制作一个口语语言 SRT 和一个英文 SRT,用于上传为隐藏字幕(而非烧录)。在 motion/work/cap/ 中工作:
motion/.venv/bin/python $SKILL/scripts/captions/find_gaps.py motion/work/words.txt "<video>":Whisper 可能在长视频上遗漏整段语音。它会打印出空白段;使用retx.py "<video>" "<空白段列表>" motion/work/gapwords.json [语言]重新转录这些空白段。python3 $SKILL/scripts/captions/make_cues.py motion/work/words.txt motion/work/gapwords.json motion/work/cap写入提示信息和四个块。- 使用
templates/caption_brief.md并行翻译这些块(四个子智能体,每个块一个)。每个块写入out1..4.json,包含每个提示的{"i","orig","en"}。 - 修复标记的提示,然后
python3 $SKILL/scripts/captions/build_srt.py motion/work/cap motion/out/captions写入captions_original.srt和captions_english.srt(每行两个平衡句,最多 42 个字符)。 - 告知创作者在发布前在 VLC 或未公开上传中测试 SRT。
样式默认值
用户的品牌将覆盖这些设置。
- 颜色: 画布
#E9E7E2,墨色#0B0B0B,白色组件,一个强调色#FF5A1F。深色素材上的面板剪辑使用浅色形状(#F4F2EE)。 - 字体和图标: Geist 用于 UI 文本,Geist Mono 用于代码、文件名和终端。一组图标,统一描边宽度(
M.icon)。 - 运动: 弹簧动画,至多轻微过冲。前缘和后缘使用不同的弹簧,因此指示器会有拉伸。相机缩放以使每个状态充满画面。
- 禁用: 弹跳缓动、粒子、发光、UI 镀铬上的渐变、混合图标描边、空闲时间、任何看起来像模板的东西、虚构的数据。
常见问题
- 切勿在相机缩放的任何东西上设置
will-change,否则文本渲染模糊。 - 在变形容器内切换文本需要其自身的进入和退出时间(
M.visdin/lin/lout),否则新旧文本会重叠。 - 使用
mix-blend-mode: difference的标签必须位于具有混合模式本身的图层中。过滤后的父元素会隔离它们。 - 在高亮下滑动的项目需要使用
M.FAST弹簧,否则高亮行会短暂空白。 - 在每次相机缩放时保持光标在画面内,包括变形过程中。
- 来自 SRT 的词语时间在提示内会漂移;尽量将重要变化放在提示的第一个或最后一个词语上。
- 剪辑通过保持其最后一个状态结束;如果合成时间段更长,合成器会保留最后一帧。