活动回顾视频
已在包含 780 个素材的活动库上端到端测试。需要 ffmpeg/ffprobe 在 PATH 中,以及 Python 3.10+,安装 pip install pillow pillow-heif numpy。
脚本与本文件同级。将它们复制到项目中:
scripts/analyze.py、build.py、reel.py、motion.py、fonts.py→ 项目根目录scripts/work/*.py→<项目>/work/examples/*.json→ 项目根目录,然后编辑它们
项目布局:raw/(未修改的原始文件)、music/(音乐)、work/(缓存)、output/(输出)、edit.json(完整编辑文件)。
字体在 Windows/macOS/Linux 上自动查找;可通过 FONT_SERIF、FONT_SERIF_BOLD、FONT_SANS_BOLD、FONT_BLACK、FONT_EMOJI 覆盖。
1. 获取素材
- 将每个导出/压缩包放到
raw/下的单独文件夹中。切勿修改原始文件。 - Google Photos 没有针对已有媒体库的 API:通过 CDP 驱动用户已登录的浏览器(参见本仓库中的
brave-cdp技能)。网格项为a[href*="/photo/"];日期标题为[role=checkbox][aria-label^="从“中全选所有照片"]。 - 使用真实的鼠标事件(
Input.dispatchMouseEvent)选择日期标题,验证"已选择 N 项"文本,按下 Shift+D。标签页必须处于前台(Page.bringToFront),否则网格会变灰。 - Google 在下载后会清除选择,且标题的
aria-checked可能过时;在重新选择前需要重新加载页面。 - 使用
Browser.setDownloadBehavior的allowAndName功能,下载的压缩包会以其 GUID 命名且无扩展名;监控Browser.downloadProgress等待completed状态。
2. 分析与挑选
python analyze.py→work/media.json(从 EXIF/视频creation_time提取拍摄时间并转换为本地时区、模糊度、亮度、重复项、音量)+work/sheets/下的每日联系表。python work/vsheets.py→ 每个视频的三帧条带。必须查看每个联系表。跳过录制中旋转的片段、口袋拍摄和夜间黑暗片段。python work/loudest.py ID:seconds ...找到音量最大的窗口(唱歌、说话、欢呼)。在修剪前用 1 fps 的帧条带验证。- 共享/转发的文件没有拍摄日期(回退到文件修改时间)——根据目测放置。
3. 编辑(受众喜欢的方式)
- 以视频为主(约 90% 的片段);只在强力的合影照片中使用照片。照片过多的剪辑会显得节奏缓慢。
- 冷开场钩子:8-10 个时长 1.2-1.8 秒的片段,使用
hook_xf0.1 接近硬切,然后是标题卡。接着按日期顺序排列,每个日期的第一个片段带有小号"label": "第 N 天 · 日期",片段时长 3-4 秒,并包含少数 9-12 秒的时刻,标记为"speech": true(音乐在此类片段下降低音量)。 - 音乐决定钩子的成败。链接两个节奏相似的曲目(
"music": [a, b],3 秒交叉淡入淡出)而不是循环播放一首。python work/bpm.py 文件.mp3估算节奏。 edit.json键:title(标题)、subtitle(副标题)、closing(结尾文字)、closing_sub(结尾副标题)、title_bg/closing_bg(+_bg_at用于视频 ID)、music/vertical_music(音乐)、music_volume(音乐音量)、duck_volume(降低后的音量)、ambient_volume(环境音量)、photo_seconds(照片时长)、hook_xf(钩子交叉淡入)、closing_seconds(结尾时长)、horizontal_hook(横向钩子)、horizontal(横向)、vertical_hook(纵向钩子)、vertical(纵向)。片段:{"id"、"at"(0-1 中心位置)| "start"、"dur"、"speech"、"label"、"xf"}。python work/shortlist.py→output/shortlist.jpg(每个剪辑点的一帧图像)。在渲染前向用户展示。
4. 渲染与质量检查
python build.py [horizontal|vertical|vertical-full]— 每个片段的缓存保存在work/seg中,使用 xfade 链、loudnorm −14 LUFS、x264 编码。EDIT=other.json OUT_SUFFIX=-v2 python build.py渲染另一个编辑方案,不会影响主编辑。- 对每个渲染进行质量检查:
nb_frames == duration*30(xfade 链可能丢帧)、blackdetect计数为 0、ebur128 约等于 −14 LUFS,以及生成帧拼接图(fps=1/5,tile=8x6)以目测旋转和标题是否被截断。 - ffmpeg 7+ 推荐使用
-/filter_complex file代替已弃用的-filter_complex_script。
5. 节拍同步短视频(IG / TikTok / FB)—— reel.py + reel.json
python reel.py→output/reel.mp4(约 45 秒),基于 build.py 的片段渲染器构建。- 剪辑点精确落在节拍网格上(
bpm提示、自动相位对齐、以帧为单位四舍五入的剪辑时间)。务必再次检查节奏:简单的估算器通常返回一半或两倍的值(如 85 对 154)。 - 第 1 秒使用最令人惊讶的时刻,并配上
"POV: …"文案。4 拍剪辑,高光时刻 6-8 拍,每个片段缓慢推近,节拍部分开始处白色闪烁。 - 文字:粗体无衬线字体,白色带粗黑描边,距顶部约 24% 的位置(避免被 TikTok/IG 按钮遮挡),向上滑动淡入。总共 5-7 行短文案;最后一行是活动名称。
6. 表情包/动态剪辑—— motion.py + motion.json(仅当用户要求表情包/贴纸时)
- 许多人认为这种风格对于真挚的活动来说过于花哨。默认使用第 3-5 节。
- 使用一首较长的稳定曲目(约 3 分钟)而不是链接多首;通过梳状滤波器搜索节拍网格。
- 每个片段:
beats(节拍数)、speed(0.5 慢动作 / 2 快进)、freeze(节拍数;磁带停止 + 静音 + 去色)、cap贴纸(圆角框、倾斜、弹性弹出、彩色表情符号)、day重击标题 + 嗖声、title(标题)、verse(诗句)、shake(抖动)、punch(重击)、flash(闪烁)、zoom(缩放)。 - 如果渲染被终止,请在重新运行前删除未能通过 ffprobe 检测的片段文件(缓存会重用它们)。
7. 竖版与社交媒体交付
- 当有人批准了 16:9 编辑并要求 9:16 时,提供相同编辑的竖版(
python build.py vertical-full)。不要对已批准的编辑进行重新设计;"让它更有吸引力"仅限于润色。 - 平台(尤其是 Facebook)会静音带有未授权音乐的帖子。提供原始声音版本(
"music": null的编辑)和/或无声副本(ffmpeg -i in.mp4 -an -c copy out.mp4),以便用户从平台的授权音乐库中添加音乐。 - 最终润色:
eq=contrast=1.07:saturation=1.2:gamma=0.97,colorbalance=rm=0.03:bm=-0.03:rh=0.02:bh=-0.02,unsharp=5:5:0.45,vignette=angle=PI/5.5+ 音频highpass=80,dynaudnorm,acompressor,loudnorm=I=-14。