Papercut 构建
使用库来构建每一集,而不是从头编写 Remotion 项目。先阅读 $MOTION_LIB/core/README.md 一次。
前置检查
- 安装: 标准 或 完整。需要库、node 20+、npm、ffmpeg、Python 工具 (numpy、Pillow、soundfile) 以及用于音频渲染的 Chrome 或 Chromium。用
motion-explainer中的命令探测;如果缺少什么,运行./install.sh --doctor并交给motion-setup处理。 - 库:
$MOTION_LIB,否则读取~/.config/motion-explainer/config.env中的MOTION_LIB=行,否则询问一次。完全没有库时,根据$MOTION_LIB/style/$MOTION_STYLE/remotion/RECIPES.md中的草图构建。 - 首先加载 Remotion 最佳实践技能(仅用于 remotion 引擎)(
remotion-best-practices,来自 Remotion 插件,或npx skills add remotion-dev/skills -g -y -a <你的 harness id>;install.sh --mode standard会完成此操作)并按它的指引处理标记、字幕和 Studio。对于kyfram,改为加载kyfram技能。 - 需要来自早期阶段的
video/STORYBOARD.md和narration/script.json。 - 整个工具链的冒烟测试:
$MOTION_LIB/core/tools/selftest.sh会在一个临时文件夹中构建examples/hello-reel/并渲染一张静态图片。
1. 搭建与验证引擎
剧集项目应从摄入 / motion-strategy 阶段就已搭建好。验证 video/episode.json 中记录的 engine:
-
如果尚未搭建,询问用户(
remotion、fframes或kyfram)并运行:$MOTION_LIB/new_episode.sh <series-dir> NN-slug CompId [out-name] --engine remotion|fframes|kyfram -
从
video/运行的预览命令:- fframes:
cargo run --release -- preview(或安装 CLI:cargo install --locked cargo-fframes) - kyfram: 先渲染一次,然后
kyfram view out/cache/kyfram/build/<Scene>.lgv(编译后的脚本:前奏 + 生成的头部 + 你的场景) - remotion:
npx remotion studio(或npm run dev)
- fframes:
-
fframes: 剧集项目是一个 Rust crate (
media/、src/main.rs)。使用cargo run --release -- render或$MOTION_LIB/core/tools/render.sh渲染。 -
kyfram: 剧集项目是
.lgv脚本 (scenes/、media/fonts/、src/plan.json)。使用$MOTION_LIB/core/tools/render.sh渲染(它会按场景缓存并拼接)。先调用kyfram技能。 -
remotion: React 项目 (
src/、package.json)。使用npx remotion render <CompId> out/<out>.mp4或render.sh渲染。
2. 规划: episode.json、配音、时间线
根据故事板填充 video/episode.json:
scenes:[["S01Hook", "l1_hook", 0], ["S02Name", "l2_name", "called"], ...](场景ID、旁白行ID、第一个单词的索引或精确单词)。banners:{"S01Hook": "CAPS 格式的要点"}。hook: 见motion-hook。cuts: 相机/切表(Camera / Cut table)的切列(cut column)。bed、whipScene、cutSfx、gaps(一行之后的额外节拍,单位秒)。
然后运行 motion-voice 直到其时间步完成(它最后会执行 build_vo.py 和 timing_md.py)。之后,在 video/ 目录下运行:
python3 $MOTION_LIB/core/tools/stubs.py # 根据引擎生成多文件场景存根
build_vo.py 会标记任何时长小于 1.2 秒的场景;将其合并到相邻场景。timing_md.py 会写入 STORYBOARD.md 的“时间”部分(绝对帧和单词提示),这样代理就无需再打开 plan.json。
3. 素材
遵循 $MOTION_LIB/style/$MOTION_STYLE/docs/asset-prompts.md: 使用图像工具(任何能输出透明 PNG 的工具)处理背景和复杂的静态道具,使用代码处理角色、所有文本、标签、仪表盘、按钮和 UI。不要在生成的图像中放置任何文本。没有图像工具时,用纸滤镜在代码中构建所有道具;该风格足以支撑。背景: 使用库的 <Backdrop> 集合(style/$MOTION_STYLE/remotion/public/bg/backdrops/_sheet.jpg);只有当剧集需要一个库中没有的场景时,才生成新的背景。验证截图: node $MOTION_LIB/core/tools/capture.mjs <url> public/proof/x.png,并用工具包的 RealShot 展示。
4. 场景(严格的多文件架构)
切勿将所有场景放在一个文件中。 每个场景必须存在于自己独立的文件中,以支持并行代理工作、独立测试和清晰的 git 差异。
根据 video/episode.json 中的 engine 进行检查:
针对 Remotion(engine: "remotion")
- 文件结构:
src/scenes/<Scene>.tsx(例如S01Hook.tsx、S02Name.tsx)—— 恰好 每个场景一个文件。src/scenes/<Scene>.parts.tsx(可选)—— 仅用于该场景的私有子组件。src/scenes/index.ts—— 由stubs.py自动管理的注册数组SCENES。切勿在此处内联场景体。
- 首先自己在
src/scenes/S01Hook.tsx中构建S01Hook,基于 HOOK 块。这是其他代理匹配的参考场景(提示常量、相机、受限的interpolate)。 - 将剩余的场景按场景分组(每个代理 2-3 个场景)并行交给场景代理。简报位于
style/$MOTION_STYLE/remotion/AGENT_BRIEF.md。 - 文件所有权: 一个代理只编写
src/scenes/<Scene>.tsx、可选的<Scene>.parts.tsx和tools/cue_sheet.d/<Scene>.json。绝不要编辑其他场景或index.ts。 - 代理阅读
style/$MOTION_STYLE/remotion/CATALOG.md和$MOTION_LIB/core/audio/CATALOG.md。
针对 fframes(engine: "fframes")
- 文件结构:
src/scenes/<scene_snake>.rs(例如s01_hook.rs、s02_name.rs)—— 恰好 每个场景一个文件,定义其结构体和impl Scene for <Scene>。src/scenes/mod.rs—— 通过stubs.py自动注册所有场景模块(pub mod s01_hook; pub use s01_hook::*;)。src/main.rs/lib.rs—— 仅通过Video::define_scenes()挂载场景。绝不要在main.rs中编写场景绘制代码。
- 首先构建
s01_hook.rs作为参考场景。 - 将剩余场景并行交给代理(每个代理仅拥有
src/scenes/<scene_snake>.rs)。 - 阅读
style/$MOTION_STYLE/fframes/src/以获取可用的相机、场景和道具结构体,而非 TSX 组件。
针对 kyfram(engine: "kyfram")
- 文件结构:
scenes/<Scene>.lgv(例如S01Hook.lgv)—— 恰好 每个场景一个脚本,仅包含形状。- 渲染器会在之前追加
$MOTION_LIB/core/kyfram/lgv/core.lgv、style/$MOTION_STYLE/kyfram/PRELUDE.lgv以及生成的头部(SCENE_ID、BANNER、DUR、FPSR、CAPS和scene()调用),因此场景绝不能调用scene()、硬编码其时长或字幕提示,也绝不能重新声明辅助函数。
- 调用
kyfram技能。首先自己构建S01Hook.lgv作为参考场景,并通过bash $MOTION_LIB/core/tools/render.sh和bash $MOTION_LIB/core/tools/qa.sh hook 0-<N> 6 0.3 6进行验证。 - 将剩余场景并行交给代理(每个代理仅拥有
scenes/<Scene>.lgv),并在简报中指明kyfram技能。 - 代理阅读
style/$MOTION_STYLE/kyfram/PRELUDE.lgv以获取标记和家具(drawRoom、drawHero、drawBanner、drawCaptions),并在渲染后阅读out/cache/kyfram/build/<Scene>.lgv以获取确切的编译脚本。 - SFX 和背景音乐的连接遵循下面的 remotion 流程;一旦
public/vo/voiceover.wav文件存在,render.sh --voice会将其混入 mp4。
构建规则: 1080x1920,30 fps,场景按工具包的过渡系列串联。全局层: VO 和音乐 <Audio>、带 <Boil> 的胶带横幅、<WordTagCaptions>、<GrainOverlay>。每个道具都必须包裹在 <Paper> 中,入场使用 <PopIn>,每个场景在其故事板相机内运行。没有 CSS 动画。保持底部 20% 空白。相邻场景绝不可共享背景。默认主角是一个方形的橙色小生物(标记 C.hero、#D67354、暗色调 C.heroLo、#A9553B、别名 you);配件和姓名标签用于区分角色。
5. 声音
从 $MOTION_LIB/core/audio/sfx/*.js 中按名称选取音效,从 $MOTION_LIB/core/audio/beds/*.json 中按预设选取背景音乐。对于 kyfram,先调用 kyfram 技能,然后在 tools/cue_sheet.d/<Scene>.json 中为每个场景写入音效行;它拥有与 fframes 和 Remotion 相同的共享音频管线。在 video/ 目录下运行:
python3 $MOTION_LIB/core/tools/cues.py && node $MOTION_LIB/core/audio/render.mjs
cues.py 将代理的行合并到 tools/cues.json 中,如果空气音效超过每 2.5 秒一个,则会打印一条提示。render.mjs 写入 public/audio/music.wav 和 fx.wav。
6. 渲染
预览: cargo run --release -- preview (fframes)、npx remotion studio (remotion) 或 kyfram view out/cache/kyfram/build/<Scene>.lgv (kyfram)。只在被要求时渲染,并且始终通过包装脚本来进行——它会从 episode.json 中读取引擎:
$MOTION_LIB/core/tools/render.sh # -> out/<out>.mp4
标志: --workers N(默认 3)、--conc N、--force、--keep。每个场景都会被缓存,因此修复后仅重新渲染更改的场景;对工具包、计划、配音或字幕的更改会重新渲染所有内容。首次运行会下载 Remotion 的无头 Chrome,并且 out/cache 会占用数 GB 的 ProRes 空间。
要修复或重建一个场景: 编辑 src/scenes/<Scene>.tsx,用 motion-qa 检查,然后再次运行 render.sh。
7. 回收(剧集完成后)
python3 $MOTION_LIB/core/tools/harvest.py . # 在 video/ 目录下运行;只读报告
python3 $MOTION_LIB/core/tools/catalog.py # 重新生成两个目录
按照 library/HARVEST.md 的描述,推广工具包的修复、可复用组件、新的音效和重复模式,并向 library/CHANGELOG.md 添加一个条目。
- 当某个动作看起来僵硬或模糊时,请查阅
$MOTION_LIB/style/$MOTION_STYLE/docs/motion-principles.md,找到匹配的原理及其工具包辅助函数。
交接
下一步是 motion-qa,它会读取项目中的静态图片和 STORYBOARD.md。在构建时对每个场景运行一次,并在最终渲染前对整个影片运行一次。发布后,motion-strategy 会运行事后分析。