Reels(竖屏短视频)
从长视频中提取竖屏片段(或独立拍摄的Reels)。
配置:在此设定你的品牌参数
首次运行前一次性编辑以下内容。后续所有操作均引用这些名称。
| 设置 | 位置 | 默认值 |
|---|---|---|
| 强调色 | scripts/motion.html、scripts/thisorthat.html、scripts/hook.html 顶部的 --brand | #3b82f6 |
| 显示字体(钩子卡片、标题、关键词) | 同一文件中的 --display-font | Anton(免费,Google Fonts) |
| 字幕字体 | scripts/caption.html | Montserrat ExtraBold 800(免费) |
| 相机LUT | reel-params.json 中的 "lut",以及 preview_reel.py 的 LUT 环境变量 | <你的LUT路径>.cube |
| 新素材存放文件夹 | 步骤“新素材存放位置” | <你的素材盘>/<收件箱文件夹>/ |
| B-roll素材库 | 动画风格,步骤6 | <你的素材盘>/<拍摄>/<相机>/full/ |
| Reels跟踪器 | 步骤0 | 项目中的 drafts/social/reel-tracker.md |
| Whisper模型 | WHISPER_MODEL 环境变量 | ~/.cache/whisper-models/ggml-small.en.bin |
| 评论关键词行动号召 | cta 计划项 | 你自己的关键词(例如 GUIDE) |
| 背景音乐参考 | 音乐步骤 | 你喜欢的参考曲目(例如用于 SearchRecordings 的Spotify曲目ID) |
字体:将 Montserrat 和你选定的显示字体安装到 ~/Library/Fonts(或者保留每个HTML文件中的Google Fonts @import,在线渲染)。如果显示字体需要授权,请勿将其包含在仓库中。
工具:whisper-cli(whisper.cpp)、ffmpeg、Node + puppeteer-core、Google Chrome、DaVinci Resolve Studio(已启用脚本支持,并带有可执行 execute_resolve_code 的Resolve MCP)、macOS(用于 cutout.swift)。
步骤0:先读取数据看板
在选择或剪辑任何内容之前,先读取Reels跟踪器。
- 填写统计信息:对于已发布超过7天且仍为空白的行,从你的分析工具(例如Metricool的
getAnalyticsDataByMetrics,针对Instagram Reels)拉取数据。包括:观看次数、平均观看时长、3秒停留率(如可用)、分享数、保存数、评论数、关注数。 - 按可控因素比较: 格式、钩子样式(卡片/构建/标题)、长度、行动号召类型、缩放动效、主题。仅当每侧至少有3条Reels时才称为一种模式,并说明差距大小。
- 在跟踪器中写下结论,标注日期,写在“数据目前说明了什么”下方。
- 将结论应用于当前批次并告知用户(在展示给用户的Reels列表中说明:“构建钩子的3秒停留率比卡片钩子高1.4倍,因此我在8条里用了6条构建钩子”)。如果某个优胜模式与已定样式冲突,请先询问再更改。
- 排期后,为每条Reels添加一行,记录其剪辑方式。
最重要的规则:钩子优先
前3秒决定分发效果,多达一半的观众在前3秒内离开,钩子必须瞬间吸引人,而不是慢慢铺陈。以“第二条建议是...”开头的Reels注定失败。
因此不要剪辑按时间顺序的摘录。将每条Reels构建为:
- 钩子(2-4秒): 取自视频任何位置(包括结尾)最有力、最独立的句子。
- 展开: 背景与解释,按阅读最流畅的顺序排列。
- 收尾: 如果有,一个简短的结束句。
按时间顺序的片段会产生弱的钩子。重新排列相同的素材(无需重拍)即可解决。重新排列就是全部工作。
好的钩子模式示例:“我们再也不跳过腿部训练了”、“跳过的计划不是计划”、“每天10分钟等于一年60小时”。简短、陈述句、无需铺垫。
新素材存放位置
新的Reels批次出现在 <你的素材盘>/<收件箱文件夹>/,通常是 untitled folder。先查看那里。确认内容后,将文件夹重命名为“日期-名称”格式:9-28-2026 gear reels。在重命名前进行转写,然后修复 clips.ndjson 中的路径。
独立Reels:动画风格
对于作为Reels拍摄的片段(无长视频、无纸质编辑),动画风格更具吸引力:画面中的产品、照片卡、按钮上的箭头或圆环、顶部打字机笔记、头部上方弹出的微小图片、带有小副标题的大胆钩子。先制作一条并获得批准,然后再批量处理。
- 无需纸质编辑。 将
segments-v2.json写为[],并为每个部分设置"raw": true, "exact": true。 - 不要完全信任整段素材的Whisper转写结果。 行与行之间重置时间较长的拍摄段,时间偏移可能达到2-5秒(Whisper可能将一句短语放在5.4秒的静音中)。运行
GAP=0.18 SUF=.fine blobs.py <workdir> <clip>,并在blob边界处剪切。对于最后一个单词,检查50ms RMS图:单词中间的骤降可能是摩擦音(如“free”中的“f”),因此重新检查字幕尾部。在单词尾部低于-50 dB时结束最后一部分。Whisper还可能在最后一张卡片的边界后凭空生成单词,因此请对照音频检查SRT的最后一条提示。 - 缩放节奏: 在原生竖屏素材上,将各部分交替设置为广角(缩放1.25,纵倾0)和聚焦(缩放1.6,纵倾-90)。对于说话者倾斜的镜头(例如钩子:纵倾180),平移该部分。
- V2层的动画:
motion.json(元素列表,与reels-built.json中的单词时间对齐)→render_motion.mjs(逐帧渲染,30秒约50秒)。元素类型在motion.html中有文档说明:单词构建、猛击、打字机注释、摇晃、产品抠图(弹出/飞入/缩小)、按钮上的脉动圆环、删除线,以及B-roll卡片(播放真实素材,通过ffmpeg的lut3d应用LUT进行调色)。每行一个视觉元素,全部位于头部上方区域(y 200-550;在y 100时顶部单词会被裁切)。为留出此空间,将每个部分的纵倾降低100(广角1.25 / 纵倾-100,聚焦1.6 / 纵倾-190)。关键词使用blue类(使用--brand),会在填充色后方绘制粗白边框,使强调色不会在浅色墙壁上消失。使用resolve_captions.py放置,"track": 2。 - 产品图片: 优先使用Wikimedia Commons API,其次是品牌OG图片。使用
scripts/cutout进行抠图,裁剪掉桌面反光。保存在<workdir>/gear/中。 - 你的B-roll素材:
<你的素材盘>/<拍摄>/<相机>/full/。如果素材是log格式,使用全分辨率文件加上LUT;烘焙代理文件看起来可能很粗糙。 - A2轨的音效:
build_sfx.py motion.json out.wav。它会使用video-reels/sfx/<name>.wav(如果存在:pop、whoosh、slam、tick、click、type、swipe、wobble、shrink),否则使用合成的替代音效,因此无需文件即可开箱即用。ding始终为scripts/ding.wav(附送合成叮咚声;可用你自己的声音替换)。合成的pop是相机快门声(音调扫描式pop往往听起来“像外星人”),MASTER = -3dB确保每个音效略低于人声。若需更好音效,创建sfx/目录并放入你已授权的音效,然后在LIBGAIN中调整每个文件的增益。使用Epidemic Sound连接器:通过SearchSoundEffects搜索,通过DownloadSoundEffect(WAV)下载,然后curl获取assetUrl。 7b. A3轨的柔和背景音乐: 选择一个氛围并保持一致(例如梦幻爵士/萨克斯,无演唱)。使用Epidemic Sound:SearchRecordings时使用query.externalID = {type: SPOTIFY_TRACK, id: <参考Spotify曲目ID>}且vocals: false,跳过任何标记为奇怪/恐怖/无人机/管弦乐的曲目,并为每条Reels选择不同的曲目。然后使用EditRecording,设置targetDurationMs= Reels长度 + 约0.3秒 并启用forceDuration,使曲目自然结束而不是被截断。使用ffmpeg将 -21 dB的音量和1.2秒淡化混入WAV,因为Resolve API无法设置剪辑音量(SetProperty("Volume")返回 False)。这样背景音乐大约比人声低15 dB。 - Resolve审查前的预览:
preview_reel.py(相同的裁剪、LUT、叠加、音效、音乐)可快速生成一个mp4。将每个片段修剪至Resolve的精确帧数(先fps,然后trim=end_frame,最后atrim=duration);单纯的-ss/-t输入会运行约0.45秒,导致预览丢失最后一行字幕。使用1.3秒接触页进行验证,并Whisper最后1.5秒以确认结束部分完整。 - 保持结束行如果说话者在行动号召后添加了结束语:在结束语后的静默处剪切,而不是在行动号召词后。
build_reels.py会在音频后填充1秒静音,否则Whisper会丢弃字幕的最后几个单词。重新渲染会使用新文件名(-v3),这样Resolve无法提供缓存的旧剪辑。
批量处理独立Reels
首先让一条Reels的样式获得批准,然后并行运行其余部分:
- 每条Reels一个文件夹(
~/Movies/reels-<MMDD>/<NN>-<slug>/),包含符号链接<clip>.wav/.json、clips.ndjson、segments-v2.json=[]。分隔文件夹可防止Reels互相覆盖彼此的reels-built.json。 - 编写简短说明,指向已批准Reels的文件(
motion.json、构图、音效),并为每条Reels启动一个后台代理,一次最多4个(过多会在解码4K时导致笔记本卡顿)。向每个代理提供该片段转写结果、产品的确切型号名称,以及需要剪掉的任何行(错误事实、重新开始、闲聊)。 - 代理在无音乐预览处停止。集中处理音乐(每条Reels编辑一次,选择不同曲目),将电平调整至约平均-38 dB并添加淡化,渲染最终预览,然后一次一条放入Resolve。
- 在构建前亲自验证每个代理的接触页。将最终预览收集到一个
previews/文件夹供审查。 - 构图因设置而异。示例:某个设置的广角缩放1.2 / 纵倾-190,聚焦1.5 / 纵倾-380;较宽松的设置使用1.25/-100,1.6/-190。检查每个片段的静态帧。
- 将每个代理的“待检查”项(误听、它剪掉的事实、屏幕上声明的信息)收集到你的摘要中。Whisper经常误听快速讲话(例如将“free”听成“for you”)。
从视频中挖掘每条Reels
挖掘整个视频,绝不重复内容。
- 首先盘点。 浏览整个转写文本,列出每个可以独立成段的想法单元:每条技巧、故事、数据、脚本、自白、反直觉观点、客户成功案例。预期每1.2-1.5分钟成片生成一条Reels。
- 寻找以下钩子类型:
- 数字:“300个面包”、“我购物账单的40%”、“从5K到50K粉丝”
- 自白:“我曾经只是坐着等他们联系我”
- 反直觉观点:“不要随便送出去......那是糟糕的赠送”
- 内行话:“没人做的秘诀”
- 客户成功案例:具名结果(经许可)
- 朗读脚本:私信内容、推荐请求
- “从未”:“我从未当面被人拒绝过”
- 钩子必须点明主题。 “没人做的秘诀”单独出现会失败,因为观众无法判断在说什么。选择在第一秒就说明Reels主题的句子。
- 一句话不能出现在两条Reels中。 两条Reels可以共享一个主题,只要钩子和角度不同。
- 按层级呈现: A名单用于构建,B名单作为备用,剩余部分注明原因(“Reel 4的后半部分”、“无钩子”、“在镜头前啰嗦”)。用户选择。
- 内容开始重复时就停止。 超过该点,每条Reels都会稀释其他Reels的效果。
每条Reels遵循的结构
“钩子需要钩住人,然后Reels的其余部分解释钩子,解释价值,也就是核心内容。”
- 钩子: 停止滚动并点明主题。
- 解释钩子: 下一行回答钩子提出的问题。如果钩子是“我没去健身房减了20磅”,接下来要说明如何做到的。
- 核心内容: 实际价值、操作指南、脚本、证据。这是Reels的大部分内容。
- 在收尾句上停止。
一个好的钩子后面跟着无法兑现的行,比平淡的钩子更糟糕,因为观众会感觉被引诱。根据两个部分评判每条Reels:钩子能否吸引我,以及之后的内容是否合理并兑现承诺?
流程检查:在构建前像陌生人一样阅读
将每条Reels按顺序写成纯句子,然后像一个从未看过长视频的人一样阅读。 只有这样才能查看时间。以下是手动修剪常遵循的规则:
- 在收尾处结束,一次就好。 最有力的那一句时停下。不要在它后面添加口号或再次强调观点。
- 没有任何内容可以指向Reels未设置过的信息。 “我就直接问他们,他们几乎都会分享”但Reels中没有“问了他们什么”,必须剪掉。检查每个“她”、“他们”、“这个”、“那个”和“所以”是否与Reels中前面出现的内容呼应。
- 故事按故事顺序展开。 一个成功案例作为口语钩子,后面才提到人名,会让听众困惑。先提人名,再说成功。文字卡片负责钩住观众。
- 每条Reels只包含一个想法。 两个想法加上重复说明应浓缩为一个想法加一个证据。
- 剪掉关于说话者的离题话和段落内的口齿不清。 将段落从中分开。
- 口语钩子必须用文字点明主题。 “这也是接触新社区的绝佳方式”中的“这”未定义。“随着你周日继续准备餐食……”才说明Reels的主题。
- 目标时长20-35秒。 超过40秒需要有充分理由。
制作精良:无“嗯”、无空白、无重复。 在构建前:
- 领夹/室内音频:运行
segments.py(来自paper-edit),设置NOISE_DB=-30。 ffmpeg的silencedetect基于峰值,领夹麦克风的室内噪音峰值高于-36默认值,因此在-36下0.5-0.8秒的真正停顿无法被检测到。 - Whisper会完全忽略“嗯”,即使有填充提示。检查每个部分的第一个有声片段:第一个实词前的一个短(0.2-0.7秒)孤立片段就是“嗯”或假开头。剪切至下一个停顿。
- 段落内的重复会在字幕阅读中显现为重复单词(“从那个从那个”、“不是的不是的”)。在第一次尝试处将段落分开。
尾部口头禅(“对吧?”、“好吗?”)。 两个陷阱:如果段落结尾在句子的实际结束时间点,就会保留口头禅;如果段落开头向后对齐0.6秒,就会带入前一句的口头禅。Whisper对这些词的时间估计可能偏差高达0.8秒,因此通过2秒窗口上的 silencedetect=noise=-40dB:d=0.06 找到真实间隙,在间隙中设置边界,并标记为 "exact":"end" 或 "exact":"start"。然后重新阅读字幕:口头禅应消失。
构建
1. 获取转写
whisper-cli --no-prints --progress false --language en --model whisper-small.en \
--file <视频文件> --output-srt --output-file <转写文件>.srt
2. 在 clips.ndjson 中列出要使用的剪辑
格式每行一个剪辑:
{"file": "<视频文件>", "start": <起始时间秒数>, "end": <结束时间秒数>}
3. 运行 build_reels.py
该脚本将剪辑拆分为段落,构建垂直时间序列,并为每条Reels生成 reels-built.json。
4. 在Resolve中放置
resolve_captions.py 将字幕和钩子放置到时间线上。
5. 预览和渲染
preview_reel.py 生成预览,render_reel.py 渲染最终版本。
故障排除
- 时间线未对齐: 检查
clips.ndjson中的时间戳。 - 字幕丢失: 确保whisper模型已正确安装。
- Resolve错误: 确保Resolve MCP正在运行且
execute_resolve_code可用。