Reel — 故事剪辑(全屏运动)
演讲者全程全屏显示。故事通过穿插创作者自己的剪辑片段、在关键词语上弹出的贴纸/卡片/小工具、一个做出反应的吉祥物,以及景深时刻(巨型文字或品牌背景出现在演讲者背后(人物遮罩))来讲述。案例:examples/story/story_stoodi_inviteraw.py。
参数:$ARGUMENTS
默认值(仅在用户明确要求时更改)
首先读取 reel.config.json(如果不存在则回退到 reel.config.example.json)。
| 项目 | 默认值 |
|---|---|
| 字幕 | 无。 关键词图形可以;绝不使用滚动字幕 |
| 音频 | 使用素材自身的音频混合,不做任何处理——由 finish.py 重新封装。无音效,无背景音乐 |
| 素材 | 假设已剪辑、调色、添加音乐→不进行剪辑或调色。原始/未剪辑→先询问 |
| B-roll | 来自配置文件的 broll_dir;描述在 work/_catalog/<folder>/NOTES.md 中 |
| 品牌 | 来自配置文件的 brand + palette。没有吉祥物文件→不添加吉祥物场景 |
| 行动号召 | 评论关键词以巨大字体显示在屏幕上;关注卡片 = 仅 @<creator.handle> 文字,不使用头像裁剪 |
| 语言 | 来自配置文件的 language(Whisper 识别 + 使用的文本类别) |
输入
从参数中获取素材名称和主题。仅在一次消息中一次性询问缺失的信息:素材是否已剪辑/调色/添加音乐;评论关键词;Whisper 可能误听的人名或专业术语;用户希望包含或排除的片段。
执行流程
PY = .venv/Scripts/python.exe -X utf8(Windows)或 .venv/bin/python -X utf8(macOS/Linux)。长步骤在后台运行。
| # | 步骤 | 命令/操作 | 检查 |
|---|---|---|---|
| 1 | 观察 | 使用 ffprobe 查看素材;联系图 ffmpeg -i raw/<take> -vf fps=1/5,scale=216:384,tile=9x2 -frames:v 1 work/<take>/sheet.jpg 并查看 | 头部上方空间充足;文字在取景框中镜像?;是否已调色? |
| 2 | 转写 | $PY tools/transcribe.py <take> --prompt "<人名, 专业术语>" | 校对专有名词;单词间隔 > 3 秒 → 重新转写该时间段 |
| 3 | 人脸 | $PY tools/face_track.py <take> | 输出 FACE = (x, y) 和头顶位置 |
| 4 | B-roll | $PY tools/story_assets.py <take> catalog;阅读 NOTES.md;查看尚未描述的片段缩略图,并将其行添加到 NOTES.md 中 | HDR / 镜像 / VFR 标志 |
| 5 | 节拍表 | 编写 work/<take>/beats.md:`时间 | 词语 |
| 6 | 遮罩 | 如果有景深时刻:$PY tools/story_assets.py <take> matte(80 秒素材约需 8 分钟 CPU,后台运行,不并行 ffmpeg) | work/<take>/matte/fg.webm |
| 7 | 素材模块 | 复制 examples/story/story_stoodi_inviteraw.py 到 tools/story_<slug>.py(slug:素材名,非字母数字字符替换为 _);设置 NAME、FACE、BROLL、TILES、MIRRORED、场景、SCENES(DUR 可选——已探测)。吉祥物名称 = assets/mascot 中的文件(小写,空格替换为 -) | |
| 8 | 资产 | $PY tools/story_assets.py <take> all(吉祥物、Logo、B-roll 片段、平铺图) | |
| 9 | 构建 | $PY tools/story_build.py <take>;在 work/<take>/sc 中:npx hyperframes lint public → 0 错误(nested_structure 警告是预期的);npx hyperframes snapshot public -o ../snaps --no-end --describe false --at <每一个节拍>;查看联系图,修复,重新构建 | |
| 10 | 快速渲染 | $PY tools/finish.py <take> --seconds 12;查看 work/<take>/sc/quick_sheet.jpg | 覆盖元素进入并离开;颜色不暗淡 |
| 11 | 完整渲染 | $PY tools/finish.py <take>(80 秒约需 12 分钟,后台运行) → final/<take>/,验证,qa/sheet.jpg | 逐帧查看联系图 |
| 12 | 报告 | 路径、时长、节拍、使用的片段、任何翻转或色调映射的信息、验证结果。如果学到新东西,将其添加到此技能中 |
节拍规则
- 每 2-4 秒必须有变化;镜头切换中纯演讲者画面不得超过约 5 秒。
- 混合模式:演讲者 + 贴纸 · B-roll 切入(约占总时长的 25-30%) · 景深时刻(每个 Reel 4-6 次) · 全屏图形+演讲者作为头像气泡(最多 1-2 次)。
- 提示时间 =
transcript.json中词语的开始时间。每个元素需在下一个节拍之前退出。 - 景深文字位于头顶上方(来自步骤 3 的头顶位置):一次一个巨型文字,前一个字飞走。堆叠的行最终会隐藏在头部后面。
- 安全区域:y 150 以上(Instagram 头部)或 y 1560 以下(字幕)不放置文本;在 y 1100-1650 范围内保持 x < 960(点赞/评论按钮区域)。
- 作为事实显示的数字必须来自素材本身。说明性标签可以添加。
- 波斯语/阿拉伯语文字:使用
.fa类(作用域内 RTL——绝不在<html>上使用dir="rtl",否则渲染为黑色),letter-spacing: 0,数字使用fa(),大零使用拉丁文0(波斯零是点)。拉丁语文字:.en;巨型显示文字:.blk。
引擎契约——已解决的 Bug
- 场景 =
def scene(c: Comp):c.clip(layer, id, start, end, html, track),c.broll(name, a, b, mstart),c.matte(n, a, b),c.j(js...)。层:under(气泡后面)、tiles、behind(素材和遮罩之间)、fw、broll、over、fx。 - 剪辑 div 获得 ID
k-<id>;动画作用于#<id>-i或内部 ID——绝不作用在.clip本身。 - 任何从屏幕外滑入的元素需要在初始状态设置
autoAlpha:0,否则会过早露出。 - 元素的第一个补间设置其初始状态(
fromTo立即渲染);后续补间使用.to或immediateRender:false。 - 永远不要使用
from:"random"错开或Math.random——在 Python 中使用random.Random(seed)打乱 DOM 顺序。 - 同一元素的同一属性:一个补间必须在下一个开始之前结束(lint 规则
overlapping_gsap_tweens)。 #cam的 scale/x/y/clipPath/borderRadius 控制推近、气泡和拼接效果。遮罩位于#cam内部,因此景深时刻在任何摄像机缩放下都能保持对齐。- 前置摄像头 B-roll 中如果包含可读文字→添加到
MIRRORED。绝不翻转主素材。 - HLG/HDR 源由
story_assets.py进行色调映射。如果finish.py因 "HDR auto-promotion" 而停止,说明某个源未经过该路径处理。 hyperframes snapshot不能证明渲染效果——步骤 10 才是用于验证的。