#用途
把一批原始实拍素材(手机拍的工厂/车间/仓库/产品视频)全自动加工成可直接投放 Facebook Reels、TikTok、Instagram 的竖屏爆款视频:多段素材随机混剪 → AI 配音朗读 → 逐句大字幕 → 卡点 BGM → 成品 MP4。
核心特点: - 零人工剪辑:给一个素材文件夹就能出片,每次运行随机换素材组合,可反复出不同版本。 - 语言选型:配音与字幕按目标市场语言生成(英语为主,也支持西/葡/阿/日/韩等),外贸获客场景默认英文。 - 爆款节奏:默认 20 秒左右短版(对齐短视频平台高完播时长),支持长版 40 秒。 - 免版权音乐:BGM 由脚本纯 Python 合成(124 BPM 电子鼓点),无版权风险。
#何时使用
- 用户给出素材文件夹并说"给我剪成爆款视频 / 做成 Facebook 视频 / 自动剪辑"。
- 用户已有成片但嫌"太长"、要求"加字幕""加配音""换外语版本""批量多出几条"。
- 用户是外贸/批发商,需要面向海外买家的获客短视频(如女包、五金件、服装源头工厂)。
#使用方式
#1. 确认输入
需向用户确认三件事(缺省则用默认值,不要卡住):
- 素材文件夹路径(可为含子文件夹的根目录,脚本自动遍历子文件夹随机选片)。
- 目标语言/音色(默认 en-US-GuyNeural 英语男声,外贸获客首选)。
- 成品时长偏好(默认 bag-wholesale-short 约 20s;要详细卖点用 bag-wholesale-long 约 40s)。
#2. 运行流水线
python ~/.workbuddy/skills/fb-reels-factory/scripts/make_reels.py \
--src "D:\视频素材\工厂视频" \
--out "D:\out\facebook-reels.mp4" \
--preset bag-wholesale-short \
--scenes 5
常用参数:
| 参数 | 说明 |
|---|---|
--src |
素材根文件夹(自动遍历其子文件夹) |
--out |
成品 MP4 路径 |
--preset |
bag-host(真人讲解) / bag-baigou(白沟产业带) / bag-price-shock(价格带冲击) / bag-viral-a(价格冲击) / bag-viral-b(POV 故事) / bag-viral-c(提问式) / bag-wholesale-short(~20s) / bag-wholesale-long(~40s) / factory-cn |
--lines-json |
自定义文案 JSON:[["Line","hook"],["Line","norm"],["CTA","cta"]],覆盖 preset |
--voice |
edge-tts 音色,如 en-US-AriaNeural(女声)、zh-CN-YunjianNeural(中文男声,Yunxi 已失效见 2.10)、es-ES-AlvaroNeural |
--rate |
语速,默认 +10%(爆款节奏建议 +15%) |
--scenes |
用几段素材混剪,默认 5 |
--gap / --min-line |
句间停顿(默认 0.30s)/ 每句最短屏显(默认 1.5s),控制总时长(快节奏建议 0.15 / 1.4) |
--caption-style |
top-yellow(顶部黄字黑边,爆款常用)/ center-white(居中白字) |
--transition |
转场:mix=随机池(默认)/ none=硬切 / 或指定 fade,slideleft,circleopen,zoomin,dissolve,radial 等 |
--xd |
转场时长,默认 0.35s |
--no-stickers |
关闭贴图(默认开启价格贴纸/徽章/箭头/CTA 按钮) |
--contact |
底部常驻贴纸条文字,默认 DM 'PRICE' > 2026 CATALOG |
--zoom |
每镜头推近幅度,默认 0.12(0=关闭) |
--music-vol |
BGM 音量,默认 0.16 |
--music-style |
配乐曲风(默认 edm):edm=明快四踩 / trap=深808沉稳 / industrial=机械打击 / pluck=轻快拨弦。一个账号下的多条片子务必换着用,免得 BGM 全是同一首 |
--no-sfx |
关闭动效音效层(默认开启:开场冲击 / 切点 whoosh / 价格卡重击 / DM 气泡 pop / CTA ding,与贴图时间轴逐点对齐) |
--sfx-style |
音效音色包(默认 deep):deep=低频冲击+暗色气流+温暖钟声 / metal=金属敲击+机械咔哒+工业门铃 / pop=明亮zap+空气上扫+木琴。每条片子务必换着用(用户明确要求"每个视频的音效都不一样") |
--sfx-vol |
动效音效音量,默认 0.55 |
--work |
中间产物目录 |
--ff-dir / --tts-pkg |
ffmpeg 与 edge-tts 所在目录(换机器时按 references/pitfalls.md 改) |
#2.1 提高"爆款率"的参数组合(推荐)
爆款视频与广告片的区别在于前 3 秒留存与互动率,务必用下面这组参数:
python make_reels.py --src "素材文件夹" --out "out.mp4" \
--preset bag-viral-a --scenes 6 \
--rate "+15%" --gap 0.15 --min-line 1.4 \
--transition mix --zoom 0.12
要点:
- 钩子必须用价格冲击 / POV 故事 / 提问(bag-viral-a/b/c),不要用"我们是一家工厂"这类自我介绍开场;
- CTA 用互动指令(Comment PRICE and I'll send you the catalog / Which one is your favorite? 1 or 2?)而不是 DM us,评论率直接影响推流;
- 时长 15~22 秒为最佳完播区间;
- 同一素材一次出 2~3 个不同钩子的版本做 A/B,隔 1~2 天各发一次对比数据。
#2.2 贴图与转场(默认开启)
- 贴图(ASS 矢量绘制 + 色块,不依赖 emoji 字体):左上
FACTORY DIRECT徽章、右上$2.00 - $3.00 / PC价格贴纸(脉冲)、中部MOQ 50 PCS/OEM / ODM OK/SHIP WORLDWIDE徽章(左右滑入)、底部常驻联系条(--contact)、结尾绿色DM 'PRICE'按钮 + 金色脉动箭头(目标是让老外私信询盘,CTA 一律导向 DM,不放外链)。位置全部在字幕带以下/以上,不会压住大字幕。 - 转场:场景之间用
xfade链(随机池:slideleft / circleopen / smoothup / dissolve / zoomin / wipeleft / radial),转场时长--xd。每段素材会多裁--xd秒以留给转场,时间轴仍与配音严格对齐。 - 运镜:每个镜头
zoompan缓慢推近(--zoom),静止画面会显著拉低完播率。
#2.3 说话清晰(人声处理链,默认开启)
用户反馈"说话听不清"时,问题通常不在 TTS 音量,而在频段被 BGM 盖住。脚本已内置两级处理:
- 单句人声(
build_voice):highpass=90去隆隆声 →equalizer 250Hz -2dB去浑浊 →equalizer 2800Hz +3.5dB提清晰度(存在感频段) →equalizer 4500Hz -2dB抑齿音 →acompressor压缩动态 →loudnorm I=-15统一响度。 - 终混(
final_mix):BGM 先做equalizer 2200Hz -4dB给人声让路 → 人声volume=1.25→ 混合后整体acompressor(threshold=-14dB, ratio=2.5)→alimiter 0.95防爆。
配套参数:语速不要超过 +10%(要清晰用 +8%),--music-vol 0.12~0.16。成品音量自检:ffmpeg -i out.mp4 -af volumedetect -f null NUL,mean_volume 应在 -20 ~ -16dB(达标参考:-19.5dB / 峰值 -0.7dB)。
#2.4 抓眼球的三条硬规则
- 前 1 秒必须"有人 + 有货":优先选真人出镜或一整排产品的画面做钩子镜头(比空镜车间强得多)。用
bag-host/bag-price-shock预设。 - 钩子字幕 3 秒内出现且最强:钩子句入场动画更快更猛(90ms 放大 + 二次弹跳),正文句更柔和。
- 0.5 秒一个视觉变化:
--scenes给到 7~8,配合 xfade 转场 + 每镜头推近,让画面持续在动。25 秒视频用 8 段素材是合适的密度。 - 声音要清楚大于快:
+8%语速 +gap 0.22比+15%+0.15更留得住人(听不清会直接划走)。
#2.5 4K 横屏素材 / 多子文件夹素材
- 4K 横屏(如微信导出的 3840x2160 HEVC)可直接用,脚本已改为"先裁 9:16 再缩放",不再生成 26MP 中间帧。 实测 2.5s 的 4K 横屏片段约 2.6s 出片,速度与 720p 同级。裁的是中间竖条(1214x2160), 真人出镜、仓库货架纵深这类"主体居中"的镜头构图依然成立;首次使用某批横屏素材时先抽一帧确认构图。
--src给到含分类子夹的根目录(如人物/仓库/装货)时,脚本会轮转取样,保证每个子夹都有素材入选。 想让"装货/发货"这类小文件夹一定出现在结尾(信任感收尾),就把它放在子夹里、并让--scenes≥ 子夹数×2。- 一批素材可一次出 2~3 个不同钩子的版本做 A/B:换
--lines-json的钩子句即可,其余参数不动。
#2.6 人工审片选素材(用户说"只要拍得好的 / 不要有笑的"时必须做)
当用户对素材有内容性要求("选拍得好的""不要那种笑的""不要乱七八糟的")时,
不能直接让脚本随机抽——必须先把素材看一遍,再用 --clips-file 锁定素材池:
# ① 生成带编号的审片表(每帧左上角烧编号 #序号.帧号)
python scripts/contact_sheet.py --src "D:\视频素材\某批素材\仓库" \
--outdir _cs --batch 8 --frames 2 --cols 4 --prefix 仓库
# ② 看图挑素材,写素材池文件(见下)
# ③ 出片时指定素材池
python make_reels.py --src "D:\视频素材\某批素材" --out out.mp4 \
--clips-file _pool.txt --scenes 7 ...
--clips-file 格式(# 注释、空行忽略):
# @group 发货现场-货柜装载/叉车/卡车 ← 组标记:定义一类画面
!D:\素材\装柜\a.mp4 ← "!" 开头 = 钩子候选,优先用作第 1 个镜头
D:\素材\装柜\b.mp4
# @group 产品细节-五金特写/防尘袋
D:\素材\包装\c.mp4
D:\素材\包装\d.mp4
# @group 组名 是关键:素材池里相似画面往往扎堆(几十条白色编织袋货堆、几十条货架),
纯随机抽会让一条片子连续出现 3 段几乎一样的画面 —— 文件名不重复,但观感依然是"重复"。
打了组标记后脚本会分组轮转:每条片子的每个镜头来自不同的内容组,画面层次立刻拉满。
建议的分组维度(8 组即可覆盖一条片子的 7 个镜头):
| 组 | 画面 | 作用 |
|---|---|---|
| 发货现场 | 货柜装载、叉车、卡车 | 钩子(最有批发说服力) |
| 工厂全景 | 厂房外景、车间中景 | 钩子 |
| 库房货量 | 整齐码放的现货 | 实力背书 |
| 自有设备 | 绣花机、绗缝机 | 自产能力 |
| 产线作业 | 缝制、装五金、打钉 | 真实生产 |
| 原材料 | 五金配件墙、织带、皮料 | 产业链自给 |
| 产品细节 | 五金特写、防尘袋包装 | 收尾镜头 |
| 样品陈列 | 货架女包 | 产品展示 |
钩子只标给"发货现场 / 工厂全景":开场第 1 秒决定完播,绝不能抽到五金特写这种弱画面。 钩子所在组在第一轮会被跳过,避免开场连着两段同类画面。
规则: - 每条片子固定从钩子池里抽 1 条开场,其余从整个池子里随机抽,既保证开场强度、又保持每条片子的组合不同; - 素材池里不要写进被排除的镜头,并在文件里用注释记录排除原因(下次不用重新审片); - 审片要点:① 画面清晰、不糊不暗;② 构图干净,没有杂物/风扇/外卖盒/杂乱堆入镜;③ 有内容信息量(货量、产品特写、作业动作)优于空镜;④ 正脸对镜头笑的、表情不可控的说话镜头一律排除(用户明确反感"那种笑的"); - 排除正脸镜头后,仍要保证产品特写和货量镜头够用——它们才是外贸获客真正有效的画面。
#2.7 批量出片:用 --used-file 保证「多条片子零重复」
这是出多条片子时最容易翻车的地方:素材池不够大 + 没有跨片台账,几条片子会互相重复画面。 真实案例:25 条池子出 4 条(每条 7 镜头 = 28 个镜头),9 条素材被复用、累计复用 21 次, 用户直接反馈"好多片段都重复"。
python make_reels.py ... --clips-file _pool.txt --used-file _used.txt
还想让"开场画面"也不撞?给每条片子写一个「只带 1 个钩子」的池文件。
素材池里有 N 个 ! 钩子时,脚本每次随机抽 1 个 —— N 条片子抽到互不相同的概率只有 N!/N^N
(4 个钩子 → 仅 9%),大概率有两条片子开场是同一画面。做法:复制池文件 N 份,
每份只保留一个 !(其余钩子行去掉 ! 变成普通行),每条片子用自己那份:
for ln in open(POOL, encoding="utf-8-sig"): # 生成单钩子池
t = ln.strip()
if t.startswith("!") and not t.startswith("#"):
p = t.lstrip("!").strip()
lines.append(("!" if normcase(p) == normcase(hook) else "") + p)
else:
lines.append(ln.rstrip("\n"))
这样出片时钩子唯一、但池子内容不变(其余组照常轮转),实测 4 条片子开场 4 个不同画面。
并池/换池前,先查历史批次用过的素材名单。 台账只覆盖"用本脚本出的片子"; 更早用别的流水线(如手写 plan.json 的批次)做过的片子不会进台账, 直接并池就会让观众在新片里看到老片子的同一画面。查法:
grep -rn "segments\|\"src\"" 老批次的 plan.json # 或直接读 plan.json 的 segments[].src
把得到的文件名清单与新素材求交,命中就剔除(本项目实例:26.8.21 的 31 条里有 13 条已被早期 A/B/C 用过)。
--used-file是 append 台账(每行一个素材路径),每出一条片子自动追加该片用过的素材;- 下一条片子选片时按「已用次数」分桶,优先取 0 次的素材;池子够大时跨片零重复;
- 池子耗尽时自动降级为「复用最少」,不会崩溃、也不会突然全撞车;
- 容量铁律:池子素材量 ≥ 片数 × 每片镜头数 × 2.5。出 4 条 × 7 镜头 → 池子至少 70 条;
- 片内同样唯一(同一素材不会在一部片子里出现两次);
- 想重新洗牌,删掉台账文件即可。
怎么攒够池子:200+ 条的素材库,人工审片后通常能留下 100~120 条 (浩业 241 条 → 审片后留 118 条),足够出 16 条互不重复的片子。
小心「小组耗尽」:分组轮转要求每一组都有未用素材。若某个小组(如 样品陈列 只有 6 条)
在两轮出片后被整组用完,脚本只能按"复用最少"降级选取 —— 全组都是 1 次时随机抽,
有几率抽到已交付片子用过的同一条,观众就看到"重复画面"。
出片前逐组核对剩余未用数量:
# 各组剩余未用数量(组内剩余 < 本次片数 时先补素材)
python -c "..." # 或直接看 build.log 里的 WARN 行
脚本遇到"某组素材已全部用过"时会打印
WARN: 内容组「X」素材已全部用过(最少 N 次)... —— 出片后 grep 一下 build.log,
有这条就说明该补素材了。小组(< 8 条)是重灾区,建议每个组至少留 10 条。
交付前再跑一次「两两交集」检查,确认新片与旧片的素材集合没有交集。
注意 iPhone MOV 的"伪横屏":竖持手机拍的 MOV 常写成 1920x1080 + rotation:-90,
ffprobe 只看 width/height 会误判成横屏。实际上 ffmpeg 会自动旋转,
画面就是原生 1080x1920 竖屏,裁 9:16 后 1:1 输出、画质零损失。
探测时要同时读 stream_side_data=rotation,非 0 就把宽高对调。
#2.8 封面(决定点击率,务必单独做)
Facebook Reels 的封面就是用户在信息流里第一眼看到的画面,比视频本身更影响点击率。 如果不管,平台会默认取视频首帧——那往往是一张毫无信息量的空镜。所以每条片子都要配一张专门做的封面。
用户明确要求的四条硬规则(2026-09-21 强调):
1. 每次生成的封面都不一样 —— 跨片、跨批都不许撞底图,也不许用同一句钩子文案;
2. 封面文案必须是「勾好奇心」的钩子,不是卖点罗列(两者差别见下);
3. 图上出现价格一律标美元 —— 本项目是按款式的价格带 $2.00 - $3.00 USD。写 $2.00 - $3.00 时
usd() 会自动补 USD,且把区间当一个整体只在末尾补一次(不会插成 $2.00 USD - $3.00 USD);
4. 底图必须取自该条视频自己的素材(否则 Facebook 判"封面与内容不符"降权)。
# ① 抽帧挑底图;--used-base 会把「与已用封面雷同」的帧标红 X 并排除出推荐
python scripts/cover_pick.py --vids "爆款O-现货库存版.mp4" --outdir _cover --n 12 \
--used-base _used_covers.txt # 跑完看 _cover/pick.txt 的推荐清单(按评分排序)
# ② 生成封面(台账去重:同一底图第二次出现直接 SKIP,观感太像给 WARN)
python scripts/cover_make.py --jobs _cover_jobs.json --used-base _used_covers.txt
# 钩子写不出来时用内置主题库自动挑(自动避开历史用过的钩子)
python scripts/cover_make.py --base 底图.jpg --out 封面.jpg --theme stock --seed O
# ③(可选)把封面烧成视频首帧,上传 Facebook 即自带封面,无需手动选
python scripts/apply_cover.py --jobs _cover_apply.json
# 台账一览:看哪张封面用了哪个底图/钩子
python scripts/cover_make.py --used-base _used_covers.txt --list
好奇心钩子怎么写(与"卖点罗列"最大的区别就是留不留悬念):
| 写法 | 例子 | 效果 |
|---|---|---|
| ✅ 留悬念 | $2 OR $3? / SAME FACTORY. / GUESS WHY.;YOU'LL NEVER / GUESS THE / FACTORY PRICE. |
逼观众点开找答案 |
| ✅ 直呼买家处境 | HOW MUCH DID YOU PAY FOR THIS BAG? / YOU PAID A MIDDLEMAN THIS WHOLE TIME |
戳痛处 |
| ✅ 反常识 / 数字冲击 | 50 PIECES. / THAT'S THE WHOLE MOQ.、10 YEARS. / 1 FACTORY. / 0 MIDDLEMEN. |
好记、有反转 |
| ❌ 卖点罗列 | 10 YEARS / ONE FACTORY / NO MIDDLEMAN |
像说明书,划过去就忘 |
脚本内置 11 个主题共 33 条备选钩子(--theme:warehouse / price-shock / insider / stock /
profit / new / speed / moq / origin / selfmade / years),--seed 决定挑哪条,且自动跳过历史用过的。
每行 ≤ 16 字符(超长会迫使整块缩字号,冲击力下降)。
去重机制("每次都不一样"靠它保证):_used_covers.txt 台账逐行记
封面文件 <TAB> 底图路径 <TAB> dHash 指纹 <TAB> 钩子文案,生成时过三道闸:
- 底图路径命中曾用记录 → SKIP;
- dHash 汉明距离 ≤
--dup-fail(默认 0)→ 判为"同一画面",SKIP。 这条抓的是不同素材但同机位同场景的情况——2026-09-21 就靠它发现 J / M 两张封面 其实取自同一条素材(距离 = 0),肉眼在缩略图上根本看不出来; - 距离 ≤
--dup-thresh(默认 10)→ 只 WARN,留给人工判断"观感是不是太像"; - 钩子文案与历史记录完全相同 → WARN(同一句话贴两张封面同样算重复)。
底图怎么挑(按吸引力排序):
1. 满屏产品:整面货架的女包、一排同款包、成堆印花包、带吊牌的成品包 —— 买家看的是包,
产品图点击率最高(IMG_2683 多色女包墙、acdbd8f7 成堆印花包、422850274 黑包特写都是好底图);
2. 工厂实力:货柜装载、叉车装柜、整排货柜车、满载三轮车;
3. 材料/工艺:五金配件墙、织带墙、皮料(色彩/密度抓眼,适合"全链自产"主题);
4. 空镜、地板、白墙、有正脸微笑的真人 —— 直接排除(用户对"那种笑的"零容忍)。
注意底图"可见区"只有画面中上部:钩子块压在 y 786~1200、价格条 1222~1318、CTA 1340~1436,
底图真正露得出来的主要是 y 200~786。所以选帧时主体最好落在这一带;
cover_pick.py 的评分就是只统计这个可见区的清晰度/色彩,避免挑到"好画面全被字压住"的帧。
两条硬规则:
- 底图必须取自该条视频用到的素材。build.log 里的 clips: 行列出了每条片子的镜头来源文件,
顺着它回到原始素材抽帧即可。用视频里没出现的画面做封面,会被 Facebook 判定"封面与内容不符"而降权;
- 从原始素材抽帧,不要从成片抽帧 —— 成片帧上已经烧了字幕和贴纸,再叠封面文字会出现两处文字打架。
版式硬约束(cover_make.py 已内置,改版式只动文件顶部常量):
- Facebook Reels 主页网格按 1:1 居中裁切显示封面 → y 480~1440 必须完整包含核心信息。
脚本已把钩子块、价格条、CTA 全部压进这个区间,顶部的徽章条可以被裁掉(Feed 里仍可见);
- 配色沿用视频内字幕那一套:黄字黑边 (#FFD43B) + 黑底半透明块 + 黄底价格条 + 绿底 CTA,保证账号视觉一致性;
- 价格条与 CTA 按钮等宽居中(视觉对齐);钩子末行用白字,和前两行黄字拉开层次;
- 价格条统一带 USD:默认 $2.00 - $3.00 USD / PC MOQ 50 PCS。
本项目没有「零售价 vs 工厂价」的对比口径 —— 用户否掉了 $45 → $3 那套说法,别再写 $45;
- 封面文案必须与视频内容一致(同义即可):封面写 YOU'LL NEVER GUESS THE FACTORY PRICE.,
视频里就得真的把价格报出来。
封面与内容对不上,用户会感觉被骗,完播率反而掉。
封面文案三行结构:钩子起句 + 悬念/冲突 + 落点,例如(2026-09-21 实际在用的 16 条)
| 片 | 封面钩子(底图 = 该片自己的素材) | 价格条 |
|---|---|---|
| X 价格带直击 | $2 OR $3. / BOTH COME / FROM HERE.(叉车装柜) |
$2.00 - $3.00 USD / PC MOQ 50 PCS |
| Y POV 寻厂 | YOU'LL NEVER / GUESS THE / FACTORY PRICE.(电脑绣花机) |
同上 |
| Z 私信询价 | WHY DO BUYERS / DM US ONE / WORD ONLY?(老花手提包) |
同上 |
H~W 那 16 张(2026-09-21 上午)用的是
$3.00 USD / PC与$45对比口径,现已作废; 重做时按上表的新口径,并走_used_covers.txt去重。
-带封面.mp4 的做法是:封面静帧(默认 0.45s)与原视频 concat,音频整体 adelay 后移保持同步。
只多出 0.45 秒,不影响完播,但省掉了发布时手动选封面的步骤。
#2.9 已交付片子的价格/文案小改 —— 原位补丁(别重剪)
用户事后改价(如 $3.00 一口价 → $2.00 - $3.00 价格带)、换联系方式、改一个词,千万不要重跑 make_reels.py:
重跑会重新随机选素材 —— 画面全变、还可能吃掉素材池里的未用素材、甚至和别的片子撞镜头。
用 scripts/patch_price.py 原位补丁:画面帧、转场、时间轴一个字都不动,只改该改的那一帧文字与那一句配音。
python scripts/patch_price.py --work _b_Q --out "爆款Q-价格对比版.mp4" \
--replace "three dollars=two to three dollars" \
--price '$2.00 - $3.00 / PC' --voice en-US-GuyNeural --rate "+10%" \
--lines-json _s_Q.json # 连带同步源文案 JSON,否则重跑会复活旧价
# 先只体检不改动:打印每句的新旧时长与槽位余量
python scripts/patch_price.py ... --dry-run
它做的事:
1. 从 subs.ass 反推原时间轴(span = d − 0.10,见 pitfalls)并复用 base.mp4(画面/转场/运镜);
2. 只对文本发生变化的句子重录 TTS;新句塞不进原槽位时只给该句提速(+6% 步进,上限 +20%),
绝不改动整条时间轴(--rate-map "2=+16%" 可手改);
3. 用同一条人声链 + 同一条终混命令重新合成,原产物备份到 <work>/_pricepatch_bak/。
改完必做:成品时长应与原片完全一致(Δ < 0.05s)—— 不一致就说明时间轴推错了,回滚重来;
再抽帧确认贴纸与字幕已更新,并同步重做封面(cover_make.py --jobs,封面上的价格也要改),
封面有改动时 apply_cover.py 重烧 -带封面.mp4。
反推原片的 TTS 参数(要重录就必须同音色同语速,否则一句配音会明显跳出来):
把该句原文用候选 voice + rate 重合成一遍,比 mp3 时长——完全相同(差 < 0.02s)即是原参数。
本项目的成片用的是 en-US-GuyNeural,语速 +10%(个别批次 +8%),可按此法逐片确认。
旧价格出现的位置要全查(grep -rn "2\.90\|two ninety\|three dollars"):① 右上角价格贴纸的 ASS 绘制文字;
② 正文大字幕;③ 配音文本;④ 封面图上的价格条/钩子;⑤ 文案 JSON(_s_*.json)。
只改 ①②③ 而漏掉 ④,封面和视频就会自相矛盾;漏掉 ⑤ 则下次重跑 make_reels.py 时旧价复活 ——
⑤ 现在用 --lines-json 自动同步(见上)。改完顺手跑一句校验:
python scripts/verify_lines.py --ws . --ignore _b_R2 --ignore _b_R3
# 输出 OK 全部一致(N 组通过)才算干净;它会自动把 _s_*.json 与各 work 目录的 subs.ass 配对
(废弃的旧版 work dir 用的是同一份 JSON 的旧版本,正常会报 FAIL,用 --ignore 跳过,以交付版为准。)
#2.10 发帖文案 + 标签(每条片子必配,用户明确要求)
视频只是半个交付物 —— 没有帖子文案和标签,发行时还得手写。用 scripts/caption_make.py 出片后顺手生成:
python scripts/caption_make.py --jobs _cap_t4/jobs.json --outdir 文案_TUVW --batch TUVW
jobs.json(每条片子一项):
[{"tag": "T", "title": "爆款T-发货速度版", "script": "_s_T.json",
"theme": "shipping", "zh": "钩子=这周就发货"}]
产出三份:
<tag>_caption.txt—— 纯英文(不含任何中文,全选复制即可贴到 Facebook);first_comment.txt—— 首评(发布后自己第一条评论):可点击的https://wa.me/...链接 + 工厂信息,直接复制粘贴;caption_sheet.md—— 汇总卡片:每条的原钩子 + 3 个备选钩子 + 正文 + 标签池 + 首评模板 + 发布建议。
联系方式分工(2026-09-23 用户给号后固化,别再手填):
- 正文只写可读的纯文本号码(
📱 WhatsApp: +86 166 3339 7622)—— 纯文本不是外链,不触发 FB 外链降权,客户还能直接存号; - 可点击的
wa.me链接放首评(first_comment.txt); - 号存在
caption_make.py的CONTACT常量里,需要换号时用--whatsapp/--wa-link覆盖,或直接改常量。
事实来源只认该条片子自己的配音 JSON(--lines-json 用的 _s_X.json):
钩子取 style=="hook" 那句、CTA 取 "cta" 那句、卖点取 "norm" 各句,
价格 / MOQ / 年限 / 产地用正则从这些句子里抽,抽不到才用内置兜底($2.00 - $3.00 USD / 50 / 10 年 / Baigou, China)。
这样帖子写的就是视频里说的,不会出现「帖子和画面不是一回事」(FB 判封面/内容不符会降权)。
正文会自动去掉与导语重复的句子(如 Ten years of OEM in Baigou 与导语撞车)和收尾句已说的 middlemen。
标签做法(照抄即可,别拍脑袋堆词):
- 6 层分组:
核心品类(买手会主动搜)/工厂身份/定制能力/地区产业带/买家身份/泛流量; - 每条片子取 8 个(FB 官方建议 3~5,B2B 获客实测 5~8 最稳;堆 30 个反而降权);
- 按片子序号错位轮转 —— 同批几条片子的标签组合必须互不相同,完全一致的标签容易被判重复内容、互相抢流量。
发布侧提醒(写进 caption_sheet.md,也要在交付回复里说):
正文不放可点击链接(FB 压降带外链的帖子),wa.me / 网站链接放自己抢的首评(first_comment.txt);
正文里的纯文本 WhatsApp 号可以留(不算外链,方便客户存号);
同批片子间隔 1~2 天发;关掉 FB 自动字幕(字幕已烧进画面,会叠字打架)。
交付前先自检:caption_make.py 的事实来自文案 JSON,所以先跑 verify_lines.py
确认 JSON 与成片逐句一致(改过价/改过文案的片子尤其要跑),否则生成的帖子会和视频打架。
#2.11 中文成片(中文配音/字幕/贴图 + 品牌水印)
中文版与英文版的差异点(都踩过坑):
- 音色选型:
zh-CN-YunxiNeural在 edge-tts 7.x 上直接报NoAudioReceived(服务端已下线该名)。 实测可用:男声zh-CN-YunjianNeural(短视频节奏感强,首选)、zh-CN-YunyangNeural(播音腔、更沉稳)、 女声zh-CN-XiaoxiaoNeural。换音色前先单句试合成一次再跑整条流水线,否则 16 句逐句重试 4 次后才报错,白等几分钟。 - 中文字体必须换:
make_reels.py的CAPTION_STYLES/STICKER_STYLES与cover_make.py的FONT_BLACK都写死Arial Black,缺中文字形会显示成方框。 做法:复制脚本到工作区改副本(不要改共享 skill 源码),把字体统一换成Microsoft YaHei(Bold 已开)。 - 中文字幕每行 ≤ 12 字(84/76px 字号、960px 安全宽度),
wrap2的limit=26是按拉丁字符设计的, 中文长句不会自动折行、只会横向溢出。写--lines-json时逐句控制在 8~12 字,标点用全角。 - 图片素材可以进素材池:
pick_clips只认视频文件,先把产品图/车间图用 ffmpeg 做成 1080x1920 / 30fps / 6~8s 的静态片段(-loop 1 -i img -t 8),管线会自动叠加 zoompan 运镜,不要自己做双份运镜。 方形/横图直接裁 9:16 会切掉主体,用「模糊底 + 居中原图」fill:split→gblur=sigma=28 打底→overlay 居中。 - 素材里混有第三方品牌 Logo / 仿牌包(LV、Coach 等)必须整张排除:既有过 Meta 广告拒审与限流风险, 也有商标侵权风险。审片时按「画面里有没有别人的 Logo」单独过一遍。
- 品牌水印:在
sticker_events里加一条全程事件即可(注意 ASS 标签在 raw string 里只用单反斜杠, 写成\\pos会原样渲染出两个反斜杠):add(0.05, total, "WM", r"{\pos(975,1845)\alpha&H40&}小张", layer=1), 并在STICKER_STYLES里补一条Style: WM,...。样式行必须加在"""收尾之前,加到外面会直接 SyntaxError。 - 贴图/CTA 文案全部中文化(
源头工厂 · 小张/工厂直供/小单可接/来图来样定制/支持外贸单证/评论区留言 · 私信我),底部常驻条走--contact参数即可,不用改代码。
#2.12 转场体检(加转场前必跑,否则整条片子静默构建失败)
ffmpeg 个别 xfade 转场会直接段错误(exit 3221225477),必崩、与线程数无关。
2026-09-21 实测 ffmpeg 9.0.1 上 squeezev 必崩,同批 fade / pixelize 完全正常。
迷惑之处在于报错现场完全指错方向:日志只有一句 CMD FAIL ... xfade,产物是个没有 moov atom 的残文件,
紧接着终混报 moov atom not found —— 看起来像"素材坏了"或"磁盘满了",实际是转场有毒。
更贵的是它白吃一遍选片(TTS 和素材都跑完了才崩)。
python scripts/check_transitions.py # 体检 TRANSITIONS + TRANSITIONS_V2 两个池
python scripts/check_transitions.py --all # 体检 ffmpeg 全部 58 个内置转场(升级 ffmpeg 后跑)
它用 lavfi 造两段测试素材逐条试跑,并用 ffprobe 兜一道"退出码 0 但产物是残文件"的情况,
最后给出可直接粘回 make_reels.py 的安全/禁用清单。升级 ffmpeg 后务必重跑一次重建白名单。
#2.13 素材台账重建 —— --used-file 会漏记,别信它
--used-file 是 append 模式,它只记自己每次运行的选择,不知道历史上用过什么。
换过池、换过批次、或某批出片时没带上台账,台账里就缺了那批素材的记录 →
脚本把它们当"未用",随机抽到就与已交付片子撞画面。
真实事故(2026-09-21):_used_haoye.txt 里 26.8.11 仓库素材只记了 4 条,而 H~M 那批实际用了十几条,
于是 X 抽到 微信视频2026-08-11_162741_881.mp4,与 H 撞车。
所以 出片后要用各片的真实选片记录重建台账,而不是只在账上追加:
# 全量重建:把已交付片子的素材都记进台账
python scripts/sync_used.py --work _build_h18 _b_J _b_K ... --out _used_haoye.txt --pool _pool_v923.txt
# 某条片子要重做 -> 先把它的素材排除在台账外(释放回池),再重建它
python scripts/sync_used.py --work ... --out _ledger_tmp.txt --exclude _b_X
--pool 会顺带报每个内容组还剩多少条没用过,以及按最小组成交量算"最多还能出几条"。
小组余量是硬上限:原材料 / 样品陈列 / 自有设备 只要见底,整批就该先补素材。
#2.14 素材组耗尽怎么办:把整组摘掉,而不是降级复用
某个内容组用完后,脚本会降级成"复用最少",平手时随机抽 → 抽到已交付片子用过的素材, 观众就看到重复画面。而且剩下的尾料往往是当组里最差的那些(好画面先被抽走), 硬用反而踩"构图要干净"的红线。
出片数是 7 镜头 / 8 个组,本来就有一组用不上 —— 所以正确做法是把耗尽的那一组从池里整个删掉:
python - <<'EOF'
src = open('_pool_X.txt', encoding='utf-8').read().splitlines()
out, drop = [], False
for ln in src:
if ln.startswith('# @group'):
drop = '原材料' in ln # 耗尽/尾料差的组
if not drop:
out.append(ln)
open('_pool_X2.txt', 'w', encoding='utf-8').write('\n'.join(out) + '\n')
EOF
删完仍是 7 个组对 7 个镜头,正好一组一条,既不撞车也不降级。
注意别把钩子行(!)所在的组删掉 —— 那是开场镜头。
#2.15 配乐曲风 + 动效音效(音频也要"每条不一样")
同一账号下多条 Reels 共用同一首 BGM / 同一套音效,观众翻主页时会有"全是同一条"的既视感。
出片时每条都要换 --music-style(edm/trap/industrial/pluck)和 --sfx-style
(deep/metal/pop,音色包决定开场冲击、切点 whoosh、价格卡、气泡 pop、CTA ding 的音色本身,
不只是换配乐)。事件时间点与 viral2 贴图逐一对齐(用 lens 算切点,不是句子边界)。
曲风 × 音色包要配套(听感统一):
| 曲风 | 配套音色包 | 气质 |
|---|---|---|
| trap | deep | 暗黑低频,价格揭晓有分量 |
| industrial | metal | 工厂机械感,切点像金属敲击 |
| pluck | pop | 明亮轻快,DM 气泡像糖纸 |
| edm | deep / pop | 明快通用 |
已交付片子只想换音频?用 --reburn:默认会重合成 music.wav + sfx.wav
(画面 base.mp4 / 配音 voice.wav 复用,秒级完成、零素材消耗)。要保留原音频层就加 --keep-music。
# 三条片子三种曲风+音色包(画面/字幕/选片全部不动,只换音频)
python scripts/make_reels.py --work _b_X --out "爆款X.mp4" --reburn --style viral2 \
--price '$2.00 - $3.00 / PC' --price-band '$2.00 - $3.00 USD' \
--contact "DM 'PRICE' FOR THE 2026 CATALOG" --music-style trap --sfx-style deep
# 改完务必把封面重新烧进首帧(-带封面.mp4)
合成原理(免版权、纯 Python):BGM 由 kick/clap/hat/bass/pluck 按曲风编排; whoosh 用单极点低通噪声 + 下滑正弦;boom 用频率下滑正弦 + 起振瞬态。 人声永远优先:final_mix 会在 2.2kHz 给 BGM 让位,音效层音量默认 0.55,永远盖不过解说。
⚠ --reburn 一定要带上 --lines-json(或确认 preset 就是想要的文案):
它复用画面与配音、但会重新生成字幕,漏传就回退到 preset 的英文,且行数与 plan.json 的
durs 对不上 → 字幕语言变错、末句消失。重烧后回看 subs.ass 的 Dialogue 行再抽帧。
#2.16 其它语言版本(俄语已验证,西/葡/阿/日韩照抄)
目标市场不是英语时,做一份工作区副本(别改共享源码),细节见 references/pitfalls.md 第 66 条:
- 复制
make_reels.py→ 运行器目录(如_ru/make_reels_ru.py),贴图文案逐条换成目标语言: 身份徽章 / MOQ 徽章 / 全球发货徽章 / 价格带大卡标签 / CTA 按钮 / 私信气泡 ×2; 底部常驻条走--contact参数,不用改代码。 - 字幕字号 76/84 → 66/72、
wrap2的limit26 → 20(西里尔字形比拉丁宽约 1.3 倍)。Arial Black自带西里尔字形,无需换字体(中文才必须换Microsoft YaHei)。 - 音色示例:
ru-RU-DmitryNeural(男)/ru-RU-SvetlanaNeural(女);先单句试合成, 若报NoAudioReceived先间隔重试(假失败),再看list_voices()确认音色是否真下线。 - 句数按 7 句起步(钩子 + 4 卖点 + 2 CTA):8 句俄语约 27s,7 句约 23s(对齐 18~23s 完播区间)。
- 参数写进 UTF-8 的 python 运行器(
sys.argv = [...]后import目标模块调main()), 别用 shell 直传非 ASCII 参数(PowerShell 有转码风险)。 - 封面:
cover_make.py --hook/--badge/--tag/--cta传目标语言文案(脚本会自适应缩字号)。 发帖文案:caption_make.py的取词与模板只认英文(WORDNUM/dollars/moq正则), 非英语版手写发布文案-<lang>/<TAG>_caption.txt(纯目标语言,零中文)+ 中文caption_sheet.md。 - 出片前用
sync_used.py --exclude <要重做的 work>重建台账,把上一版素材释放回池再跑。
#2.17 广告版(Facebook Ads)交付
用户说「要投 Facebook 广告」时,除了正片还要交付投放包,并遵守这些差异:
- 画面里放联系方式:底部常驻条走
--contact "WHATSAPP +86 1XX XXXX XXXX"(Strip = Arial Black 44, 25 字符拉丁+数字安全;号码问用户要,别把真实号码写进技能文档);CTA 大按钮仍导私信(НАПИШИ ЦЕНА之类), 封面 CTA 条同放号码。 - 时长收到 21~23s:广告完播率敏感。先把候选句跑一遍时长表再定稿(见
pitfalls.md68), 实测俄语 7 句可达 21.6s / 22.9s,处在区间内。 - 素材仍要过三层去重:两条 A/B 之间也必须零撞镜头 → 先
sync_used.py重建台账再依次出片, 出片后check_overlap.py复核。 - A/B 差异集中在钩子:同受众/同预算/同版位,只换素材。两条的钩子类型要不同 (价格悬念 / 身份反差 / 反常识数字 / 买家处境),配音音色与价格口径保持一致,曲风+音效包换掉。
- 受众:俄语素材绝不勾 Russia:
- Meta 自 2022-03-04 起永久暂停「定位俄罗斯用户」的广告,俄境内广告主也被禁止投放;
- 俄联邦法 72-ФЗ(2025-09-01 生效)禁止在 Facebook / Instagram 投广告;
- WhatsApp 自 2026-02-11 起在俄境内被全面封禁(2025-08 起限制通话 → 12 月限速 70~80% → 2026-02-11 移出国家 DNS;
Telegram 同期被限速,俄官方推 Max)→ 画面烧 WhatsApp 号对俄境内用户已无意义。
→ 投 CIS(哈/乌/吉/塔/阿塞/亚美/格/白俄/蒙)+ 俄语侨民(土/阿联酋/以色列/欧盟/美加)。详见
pitfalls.md70。 - 交付:
爆款RX-xxx版-带封面.mp4×2 +封面-RX-xxx版.jpg×2 +发布文案-XX/RXn_ad.txt(纯目标语言、可直接粘进 Ads Manager)+ad_sheet_XX.md(广告字段:主要文本 / 标题 ≤40 / 描述 ≤30 / 行动按钮 / 受众建议 / 合规自检 / A-B 差异对照 / 操作清单)。 - 出片前若要与同批其它片子错开素材,仍先
sync_used.py --exclude <本次 work>重建台账。
#3. 交付
出片后必须:
1. 用 ffprobe 校验成品(时长/分辨率/编码),确认 1080x1920 / 30fps / h264+aac。
2. 抽帧(第 1.5s、中段、结尾)自检字幕烧录正确、无截断。
3. 每条片子配一张封面图(见 2.8),并在 1:1 裁切下复核过核心信息完整;
需要"上传即自带封面"时再产出 -带封面.mp4(校验首帧确实是封面、时长只多 0.45s)。
4. 跑一次跨片撞车检查(出多条片子时必做)——规格和画面自检都发现不了这个问题:
bash
python scripts/check_overlap.py --work _b_N _b_O _b_P _b_Q --names N O P Q
它取各片实际交付选片的交集,逐对报出被两条片子共用的素材,
并汇总 唯一素材 / 镜头总数(两者相等才叫真正的零重复);
同时会把 build.log 里的「内容组素材已全部用过」预警一并列出来。退出码 1 表示有撞车。
⚠ 取选片的来源有讲究:优先读 plan.json(构建成功时写下的权威记录);
没有它才回退 build.log,且必须取最后一个「既有 clips 行、又出现 SUCCESS」的块。
早期失败重试会在日志里留下废弃的 clips: 行(例如转场段错误那次),
取第一条会读到没被采用的选片,误报跨片撞车(2026-09-21 的 X ∩ Z = 3 假警报就是这么来的)。
5. 每条片子配一份发帖文案(见 2.10):用 caption_make.py 生成钩子 + 正文 + CTA + 标签,
纯英文 txt 可直接粘贴;顺带把首评模板和发布注意事项一并交给用户。
6. 用 present_files 把成品 MP4 和文案文件一起交给用户,并在回复中说明:文案内容、用了哪几段素材、时长规格,以及"换一批素材/换语言/换文案"的下一步选项。
#文案撰写要点(外贸获客向)
面向海外批发买家的爆款结构:钩子 → 卖点 ×4 → 行动号召 ×2
默认商家背景(已内置到所有 preset,勿写错):河北保定白沟(Baigou, Hebei, China)—— 中国箱包之都,女包源头工厂,10 年 OEM/ODM 经验,MOQ 50 件起。文案里的产地一律写 Baigou, China(老外批发商认这个产业带),不要写 Guangzhou 等其它城市。若用户提到其它产地,按用户实际所在地改写。
- 钩子(首句,最重要):制造"源头工厂"稀缺感,如
The factory behind your favorite bags、POV: you just found the factory... - 卖点:工厂身份 + 材质(PU/vegan/genuine leather)+ MOQ + 定制(OEM/ODM、logo、包装)+ 价格锚点(
$2.00 - $3.00 USD per piece,按款式不同)+ 质检/交期 - 行动号召:
DM "PRICE" for our 2026 catalog、WhatsApp us, link in bio、Comment your style number for a quote - 每句 6~9 个英文单词,字幕最多两行(脚本自动折行);口语化、有节奏,避免书面长句。
换品类:改钩子与卖点里的产品词即可(五金件 hardware parts、服装 apparel),或用 --lines-json 传入定制文案。
#关键实现要点(改脚本前必读)
- 素材编码:iPhone 常见
.MOV+ HEVC,ffmpeg 可处理;若要给浏览器使用需-c:v libx264转码。 - 时长对齐:字幕与配音时间轴按 TTS 实际时长计算;画面用
tpad=stop_mode=clone冻结末帧 +-t 总时长精确截断,避免"最后几句被吞"(曾因-shortest+ 场景偏短丢失结尾 12 秒)。 - 场景时长兜底:素材短于场景时长时用
-stream_loop -1循环铺满,保证每段精确等于目标时长。 - ASS 字段数:
Dialogue:字段数必须与Format:完全一致,多一个逗号会导致字幕行首出现逗号。 - 中文字幕字体:默认
Arial Black对拉丁字符最佳;做中文成片时把Fontname换成Microsoft YaHei。 - BGM 与人声:BGM 音量 0.16 +
alimiter防爆音;如需闪避(说话时音乐变小)可接sidechaincompress。
环境配置与已知坑见 references/pitfalls.md(首次在新机器上运行前必读)。
#抖音原创强化版(竖版知识/口播类 · 反转载查重)
触发:用户说"发抖音被判定转载 / 重复 / 非原创""要全抖音独一份""同一条文案再出一版", 或者要把这套流水线用于中文知识口播(不是带货)时。
先判根因,再动手——"被判转载"有四种完全不同的成因,用错药白忙一场:
| 成因 | 特征 | 治法 |
|---|---|---|
| 画面里没有真实拍摄内容 | 纯程序生成的卡片/文字帧,画面几乎全静止 | 换成实拍素材(权重最高的一层) |
| 与账号内已发片子高度相似 | 同版式 + 同音色 + 同结构,只换了文字 | 换画幅(9:16)、换版式、换文案结构、跑随机种子 |
| 音轨可被比对 | 纯人声 TTS 无 BGM,音轨只由 TTS 模板决定 | 加独家合成 BGM(音量 0.13)+ 音效层,人声仍占主导 |
| 反复放同一条素材 | 素材池小 → 单镜头被拉到 7~15 秒 | 用 derive_pool.py 派生扩容到 15 条 |
做法(四步,脚本都已就位):
# 1) 生成抖音原创强化版流水线(不修改共享源码,只在工作区产一份副本)
python scripts/patch_douyin.py "<工作区>/douyin"
# 2) 派生扩容素材池(原片 6 条 → 15 条多取景镜头)
python scripts/derive_pool.py --src "<原片目录>" --out "<工作区>/douyin/pool_dy2" \
--kind "factory_wide=factory,factory_detail=factory,host_a=lowres,bag_black=product"
# 3) 写一个 UTF-8 运行器传参(中文参数别走 shell,PowerShell 有转码风险)
# 关键参数见下表;--work 建议每次换新目录(见 pitfalls 第 74 条)
# 4) 出片;换 --seed 就等于出一条全新画面(音轨旋律也跟着换)
python "<工作区>/douyin/run_dy2.py" # 种子默认取时间戳
python "<工作区>/douyin/run_dy2.py" 90066976 # 复现指定版本
参数基线(中文知识口播,实测 22 句 → 69.8 秒 / 13 镜头 / 平均 5.2 秒一切):
| 参数 | 值 | 理由 |
|---|---|---|
--caption-style |
dy-cn |
底部 430px 安全区大字幕,避开抖音底部文案条与右侧互动按钮 |
--no-stickers |
开 | 知识类不要价格贴纸/私信按钮,那是带货视觉层 |
--step-badges |
开 | 文案以「第X步」开头时左上角弹大号徽章,提升完播 |
--watermark |
如 小张 · 源头工厂 |
落在右侧中部(\pos(1010,980)),避让互动按钮带 |
--rhythm |
punch |
开场快切 + 结尾留白,CTA 有足够时间被读进去 |
--zoom |
0.08 |
极缓推近;实拍素材自带运动,不需要 0.12 的大推拉 |
--gap / --min-line |
0.22 / 1.35 |
中文句短,快节奏 |
--music-style / --sfx-style |
pluck / pop |
轻快拨弦不抢人声 |
素材配比按选题定:知识向选题画面主体必须是车间实拍,配比约 车间 10 : 真人 2 : 产品 3。 第一版把产品类配成 7/15,满屏反复是同一块背景,观感明显重复——配比是按内容配的,不是平均分。
知识口播的两条既有规矩会被打破,要主动跟用户说清:
① 原"纯人声无 BGM" → 这版必须加独家合成 BGM(纯人声是音轨层面最容易被比对的形态),
人声仍占绝对主导;仍想要纯人声就把 --music-vol 0、去掉 --sfx-vol 重烧,画面不用重跑。
② 原"静态画面无推拉" → 实拍素材自带运动,只叠 8% 极缓推近即可,不必靠静止求稳。
还没通过时的下一档手段:换音色(如云健 → zh-CN-YunyangNeural / 女声)。
音色是最后动的一刀,因为它会影响账号的声音识别度。
合规红线仍然适用:派生只改取景/速度/调色,不会把原片里的第三方品牌 logo 或仿牌货品"派生掉"——这类素材必须在原片层面就筛掉。
已知坑见 references/pitfalls.md 第 74~79 条。