短视频高效剪辑
素材进来 → 文本出来 → 决策 → 竖屏成片出去。一条 EDL 贯穿七个阶段。
Overview
把 30 分钟原片压成 60 秒能发的 9:16 竖屏成片,附一份切片清单。
核心不是"加字幕",是让机器读懂画面里说了什么:
whisper 把音轨变成带时间轴的文本,之后"哪段是废话、哪句是金句、说到关键参数时画面在第几秒"
才有判断依据。字幕只是副产品。
定位是初剪,不是终剪。卡点、特效、调色还得进剪映。
抖音竖屏规格(本技能的默认档位)
| 项目 | 标准 | 说明 |
|---|---|---|
| 画幅 | 1080×1920(9:16) | 抖音满屏,不加黑边 |
| 帧率 | 30fps | 25/60 也能过审,30 最保险 |
| 编码 | H.264 High@4.1 + AAC 48kHz 立体声 128k | 平台全兼容 |
| 码率 | CRF 20 + maxrate 10M | 实际输出 4–8Mbps 居多 |
| 响度 | -14 LUFS,峰值 -1.5dB | 抖音/视频号统一标准 |
| moov | +faststart |
秒开,别让平台转码 |
| 封面 | 额外出一份 1080×1440(3:4) | 抖音封面位按 3:4 显示 |
安全区(字幕一定不能踩)
抖音的 UI 会盖住画面边缘,字幕要藏在中间那块干净区域:
| 位置 | 1080×1920 | 720×1280 | 被谁占 |
|---|---|---|---|
| 顶部 | 160 | 107 | 搜索栏、状态栏 |
| 底部 | 420 | 280 | 文案、@、话题、音乐名、进度条 |
| 右侧 | 210 | 140 | 头像、点赞、评论、收藏、分享 |
| 左侧 | 60 | 40 | 安全边距 |
字幕底边距 = 底部安全区 + 高度×1.8%。改字幕位置就在 config.local.json 里覆盖 media.safe,
别去改 package.py 里写死的数字,也别直接改代码。
横屏素材怎么进竖屏
抖音上黑边视频会被判低质,所以默认不用 pad:
| 素材类型 | 处理 | 效果 |
|---|---|---|
| 竖屏 / 近竖屏 | cover:等比放大 + 居中裁切 |
铺满全屏,无损画幅 |
| 横屏 / 方形 | blur:模糊放大铺底 + 原画居中悬浮 |
满屏无黑边,主流做法 |
| 强制指定 | --fit cover/blur/pad |
pad 仅用于存档 |
判断依据是源片宽高比:比 9:16 宽 8% 以上就算横屏,走模糊兜底。
When To Use — 触发词与分流
> 完整触发词全集、逐组适用场景、消歧规则、覆盖缺口,见 references/triggers.md。
> 下面是速查版,判不准就翻那份。
主触发词:全能剪辑
「全能剪辑」是本技能的指定触发词,连写和分开写都触发。
| 写法 | 例子 | 结果 |
|---|---|---|
| 连写 | 全能剪辑 |
✅ |
| 分开(空格) | 全能 剪辑 / 全能 剪辑 |
✅ |
| 分开(连接符) | 全能·剪辑 全能-剪辑 全能_剪辑 全能/剪辑 全能\|剪辑 |
✅ |
| 分开(括号引号) | 全能(剪辑) 全能[剪辑] 「全能 剪辑」 |
✅ |
| 跨行 | 全能↵剪辑 |
✅ |
| 夹在句中 | 帮我用全能 剪辑把这条压到60秒 |
✅ |
不要靠改 description 来"支持两种写法" —— 宿主对 description 的匹配是模糊的、不可控的。
真正的判定必须落在可执行代码上:
# 判定(退出码 0=命中 / 1=未命中)
python "$S/trigger.py" "帮我用全能 剪辑剪一下这条"
# 只看退出码,适合脚本里 if 判断
python "$S/trigger.py" -q "全能剪辑" && echo "走剪辑流程"
# 批量判定,一行一条
printf '全能剪辑\n帮我剪个视频\n' | python "$S/trigger.py" --stdin
语义上的两个边界(都是踩过的坑):
-
不能"删掉所有空白再查子串"。
这个软件全能,剪辑也方便删完空白也是这个软件全能剪辑也方便,
但它是两句话碰巧挨着,不是触发词。所以判定规则是「定位『全能』→ 检查到『剪辑』之间
只隔了分隔符」,而不是全局去空白。 -
分隔符分两级。
连接符(空格 / 中点 / 连字符 / 斜杠 / 括号)默认放行;
句读标点(逗号 / 顿号 / 句号)默认不放行,因为它们标志句子边界。
真要支持「全能,剪辑」加--allow-punct。
| 附加开关 | 作用 | 代价 |
|---|---|---|
--allow-punct |
句读标点也算分隔符 | 少量误判风险 |
--loose |
逐字打散也认(全 能 剪 辑) |
误判明显上升 |
--max-gap N |
允许中间夹 N 个别的字(全能的剪辑) |
同 loose |
改完触发逻辑跑一遍内置用例,全部通过才收工:
python "$S/trigger.py" --selftest
按你嘴上怎么说
| 你会这么讲 | 走到哪一步 |
|---|---|
| "帮我剪个视频" / "这条能发吗" / "这素材怎么搞" | 全套七步 |
| "把这个长视频剪短" / "太长了,剪到 60 秒" | 0→1→3→4→5→6 |
| "这条视频能剪出几条?" / "拆成几条" | 全套 + 多条输出 |
| "把废话去掉" / "只留重点" / "挑金句" | 3 分析 → 4 计划 |
| "中间卡壳了" / "停顿太长" / "太拖沓" | 4 加 --cut-silence |
| "说话太慢" / "提快一点" | 4 加 --speed 1.15 |
| "加个字幕" / "自动上字幕" / "字幕烧进去" | 需先有 1 转写 → 6 |
| "转成文字" / "给我文字稿" / "扒一下他说了啥" | 只跑 1(+2) |
| "下回来看看" / "把这条扒下来" | 只跑 0 |
| "横屏的能转竖屏吗" / "怎么满屏" | 4 加 --fit smart |
| "出个封面" / "封面选哪帧好" | 6 的封面环节 |
| "再剪一版" / "第 2 段删了" | 改 EDL 重渲染,不重跑转写 |
| "能过审吗" / "画质怎么糊了" | 先答,别急着跑脚本 |
| "剪映之前先处理一下" | 0→5,包装留给剪映 |
分流优先级(命中即停)
- 没素材 → 先要素材,别猜
- 要文字还是要成片 → 文字类只跑转写,不进 render
- 有数字吗 → 时长走
--target,数量走--max-clips - 指向内容还是时间 → 内容走
--min-score,时间走--cut-silence/--speed - 指代已有产物 → 返工,改
edit_plan.json - 只有行业词/平台词 → 定
--preset,流程仍走全套 - 只有痛点 → 先给判断,一半情况不需要剪辑
按内容类型
口播 / 知识分享 / 教程 / 直播回放切片 / 访谈 / 播客视频化 /
vlog / 探店 / 开箱 / 产品展示 / 带货讲解 / 测评 /
安装实录 / 施工过程 / 售后上门 / 门店日常 /
课程录屏 / 会议纪要视频化 / 采访素材整理
按平台
抖音(默认 9:16 1080×1920)、快手、视频号、小红书、B站竖屏、TikTok(同样 9:16)
按痛点
素材太多不知道从哪下手 · 原片太长发不出去 · 没有字幕完播率低 ·
说话有口头禅和重复 · 不知道哪句是钩子 · 横屏素材不知道怎么进竖屏 ·
剪完发现字幕被按钮盖住 · 想复用自己以前发过的内容
不用(会明确拒绝并指路)
- 加 BGM / 配乐 / 卡点 → 剪映(
capcut) - 特效 / 滤镜调色 / 贴纸 / 花字 → 剪映
- 只加水印 / 改分辨率 / 压文件大小 → 一句 ffmpeg,别调这个
- 逐字高亮 / 双语字幕 / 说话人分离 → 本技能不支持,说清楚别装能做
- 纯文字脚本创作 → 走
short-video-ops - 视频本体不在这台机器上也没法下载 → 先把素材弄过来
Prerequisites
先跑一次自检,缺什么它自己会说:
python "$S/_env.py"
自检会打印:用的哪个解释器、ffmpeg/ffprobe 从哪来、模型缓存放哪、中文字体找没找到、
当前读的是哪份配置。这些都可以覆盖,见下面两节。
依赖装好(faster-whisper / PyAV / imageio-ffmpeg / yt-dlp):
python -m pip install -r requirements.txt
av 必须锁 13.x,装成 19 会直接崩(它去掉了 av.open(metadata_errors=...))。
不需要 ffprobe —— 找不到会自动用 PyAV 兜底。
配置与本机档位
技能包只带通用规格(9:16 / 1080×1920 / CRF 20 / -14 LUFS 这套公开标准)。
你自己的档位(画幅、码率、是否烧字幕、转场)不进包,放本机配置文件:
cp config.example.json config.local.json # 放技能根目录同级即可
然后在 presets 下加一条,只写要覆盖的字段:
{"presets": {"省流720": {"width": 720, "height": 1280, "crf": 23, "subs": false}}}
python "$S/plan.py" "$W" --preset 省流720
查找顺序(先命中者优先,逐层覆盖):
$VEP_CONFIG → 技能根 config.local.json → 技能上级 <slug>.config.json
→ %APPDATA%\short-video-edit-pipeline\config.json(mac/linux 是对应目录)→ 内置默认。
一份配置都找不到也能跑 —— 回退内置默认,不报错。
--preset 写错名字时会直接告诉你当前加载了哪些档位、配置从哪读的、怎么加新档位。
完整性校验
python "$S/_integrity.py" # 逐文件核对 MANIFEST-SHA256.txt
python "$S/_integrity.py" --json # 机器可读
三种模式(配置项 integrity.mode):off 不校验 / warn 默认(只警告并写进 report.json)/
strict 拒绝运行。自己改过代码是正常的,加 --allow-modified 或设 VEP_ALLOW_MODIFIED=1 放行。
它是「防手滑 + 出纠纷时能举证」,不是防盗 —— 源码是明文,复制粘贴不需要 manifest。
七步流程
> 全程用一个工作目录,产物都落在里面。约定:$W = 你指定的工作目录
> (--out 传入,缺省当前目录),$S = 本技能的 scripts 目录。
0. 收素材
python "$S/acquire.py" "https://www.douyin.com/video/xxx" --out 素材
python "$S/acquire.py" ./素材/VID_001.mp4 --out 素材
python "$S/acquire.py" "<url>" --cookies chrome # 自己的作品/需要登录的
产物:素材/*.mp4 + 素材/acquired.json
1+2. 体检 + 转写
python "$S/transcribe.py" 素材/V1.mp4 --out "$W"
产物:
- media.json 时长/分辨率/fps/音轨/旋转角
- words.json 词级时间轴(后面全靠它)
- transcript.srt 字幕草稿
- transcript.md 带时间码的可读稿 ← AI 判断素材时先读这个
- chunks/ 分段缓存,断了重跑不重复算
长片自动按 8 分钟切片转写;--force 可强制重算。
3. 分析:找废片、找金句
python "$S/analyze.py" "$W"
产物 candidates.json:每句一条,带 score、tags、reasons,外加建议剪掉的静音区间。
规则来自 references/stage-rules.md。
这一步之后要先看 transcript.md 和 candidates 的 Top 5,再决定怎么剪。
不要跳过直接规划 —— 转写有错字、有重复,机器不知道哪句才是重点。
4. 定剪辑计划(EDL)
python "$S/plan.py" "$W" --target 60 # 默认抖音 9:16
python "$S/plan.py" "$W" --preset 抖音-淡入淡出 # 产品展示/开箱
python "$S/plan.py" "$W" --target 45 --speed 1.15 # 口播提速去拖沓
python "$S/plan.py" "$W" --cut-silence # 挖掉句内卡壳停顿
python "$S/plan.py" "$W" --fit blur # 强制模糊背景
# 自有档位:先在 config.local.json 的 presets 里定义,再用 --preset <名字>
--cut-silence 是最该知道的参数。 口播里"我是……(卡壳 5 秒)……谁"这种,
不动它就是一整段死画面,观众早划走了。开了之后自动挖掉句内 ≥0.8s 的停顿,
成片变成多段跳切(剪映里"删除停顿"干的事),静音两端各留 0.12s 防止吃掉尾音。
产物 edit_plan.json —— 唯一真相源。想改哪段、改多长、加多少句字幕,改这个文件重渲染即可,
不用回头重跑转写。
5. 渲染
python "$S/render.py" "$W/edit_plan.json" --out 成片_粗剪.mp4 --loudnorm
产物:成片_粗剪.mp4 + timeline.json(字幕的成片绝对时间,给下一步用)
自动做竖屏适配(竖屏裁切 / 横屏模糊)、按抖音规格编码、统一响度。
6. 包装交付
python "$S/package.py" 成片_粗剪.mp4 --timeline timeline.json
产物:
- 成片_字幕版.mp4 烧好 ASS 字幕,按安全区排版(原片保留)
- 成片_封面候选.jpg 3×3 接触表 + 9 张单独帧(9:16)
- 封面_3比4/ 同批帧的 3:4 版,抖音上传封面用这个
- subtitles.ass 字幕文件(要进剪映也能用)
- report.json 每段来源、时间码、分值、抖音合规自检
数据结构
words.json(词级,转写唯一产物)
{"media": {"duration": 25.1, "width": 720, "height": 1280, "fps": 30},
"segments": [{"start": 2.54, "end": 7.08, "text": "先拆旧面板。",
"words": [{"w": "先", "s": 2.54, "e": 2.9}]}],
"cues": [{"start": 2.54, "end": 7.08, "text": "先拆旧面板。"}]}
edit_plan.json(EDL,渲染层只认这个)
{"output": {"width": 1080, "height": 1920, "fps": 30, "crf": 20, "subs": true,
"loudness": -14, "fit": "smart",
"safe": {"top": 160, "bottom": 420, "left": 60, "right": 210},
"profile": "high", "level": "4.1", "maxrate": "10M", "bufsize": "20M"},
"transitions": {"type": "none", "dur": 0},
"clips": [{"src": "...", "in": 2.39, "out": 7.26, "dur": 4.87, "speed": 1.0,
"text": "先拆旧面板。", "score": 0.78,
"subs": [{"in": 0.15, "out": 3.2, "text": "先拆旧面板。"}]}]}
Options 速查
| 参数 | 在哪 | 说明 |
|---|---|---|
--preset 抖音/抖音-淡入淡出/口播/自动 + config.local.json 自定义档位 |
plan | 决定画幅、转场、是否烧字幕;默认抖音 |
--fit smart/cover/blur/pad |
plan | 竖屏适配策略,默认 smart |
--target 60 |
plan | 目标时长(秒),只是上限参考 |
--speed 1.15 |
plan | 口播提速,视频音频同步 |
--min-score 0.5 |
plan | 分数线,越高越挑 |
--max-clips 12 |
plan | 最多几段 |
--pad 0.15 |
plan | 每段前后留的呼吸(秒) |
--no-snap |
plan | 切点不吸到静音(一般别关) |
--cut-silence |
plan | 挖掉句内长停顿(卡壳/走神/翻稿子),成片变多段跳切 |
--silence-min 0.8 |
plan | 多长的停顿才挖(秒),默认 0.8 |
--silence-keep 0.12 |
plan | 停顿两端各留多少(秒),防吃字头字尾 |
--hardcut |
plan | 强制硬切,忽略预设转场 |
--model tiny/base/small/medium |
transcribe | small 是默认,够用 |
--chunk 480 |
transcribe | 分段转写时长 |
--loudnorm |
render | 统一到 -14 LUFS(抖音/视频号标准) |
--cover-n 9 |
package | 封面候选帧数 |
--no-cover-3x4 |
package | 不出 3:4 封面版 |
--no-subs/--no-cover |
package | 跳过对应步骤 |
Troubleshooting
| 症状 | 原因与处理 |
|---|---|
找不到 ffmpeg |
跑 _env.py 看探测结果;设 FFMPEG_BIN 指到 exe |
| 首次转写卡在下载模型 | HuggingFace 在国内常不可达。跑 fetch_model.py 从 ModelScope(魔搭)拉 |
metadata_errors 报错 |
PyAV 版本太新,pip install av==13.1.0 |
| 音画不同步 | 检查是不是用了 -c copy 切源文件;本技能全程重编码,正常不会 |
| 字幕是方块 | package.py 已带 fontsdir;若还方块,看 _env.py 自检打印的字体路径,必要时在配置里指定 paths.font_dir |
| 字幕被底部 UI 盖住 | 在 config.local.json 覆盖 media.safe.bottom,重跑 plan |
| 字幕穿到点赞按钮上 | 在 config.local.json 覆盖 media.safe.right |
| 横屏素材上下有黑边 | 用 --fit smart 或 --fit blur,别用 pad |
| 成片比计划长 | 段尾 AAC padding,差 <0.5s 忽略;超过 1s 检查切点 |
| 响度偏了(如 -12 而非 -14) | 素材静音占比高时 loudnorm 会飘,现已内置事后复测 + 增益补救;日志会打「补 NdB」 |
| 剪完画面有跳感 | 那是 jump cut,口播正常。嫌生硬就加大 --silence-keep 或改 --preset 抖音-淡入淡出 |
| 转写有错字/重复 | --prompt 塞进行业词能明显改善;重复多半是原片有回声 |
| 切出来像断气 | 关掉 --no-snap,让切点吸到静音 |
yt-dlp.exe 报错 |
某些 Windows 包装器有退出码问题,脚本已统一用 python -m yt_dlp,不受影响 |
设计边界(诚实说明)
- 不做画面级理解。 没有物体检测、没有人脸识别。判断依据只有语音文本 + 静音分布 + 时长。
所以纯画面镜头(拆锁动作、产品特写)只能按时间切,判断不了"哪一帧最好看"。 - 不替代剪映。 卡点、特效、贴纸、变速曲线、调色,这些进剪映做。
- 转写有错字是常态。 small 模型中文准确率约 90%,专有名词靠
--prompt救。
关键数字、价格、型号必须人工核对。 - 自动剪出来是"能看",不是"好看"。 节奏感靠你在 EDL 里调
--pad和段顺序。 - 模糊背景不是万能。 横屏素材进竖屏,原画只占中间约 1/3 高度,
文字密集的横屏内容(PPT、代码)在手机上会看不清 —— 这类内容建议单独做竖版模板。 - 版权自负。 下载别人的视频只用于拆解分析;二次发布前确认授权。