AutoClip 出片质量工程化与可发布导出实战:从「切片是素材」到「成片直接能发」
【免费下载链接】autoclipAutoClip : AI-powered video clipping and highlight generation · 一款智能高光提取与剪辑的二创工具项目地址: https://gitcode.com/GitHub_Trending/autoc/autoclip
导读:AutoClip 是一个 AI 驱动的视频高光提取与二创工具,其核心流水线把长视频拆成大纲、时间线、评分、标题、聚类、切割六个步骤。本文围绕仓库中的 docs/QUALITY_AND_PUBLISH_PLAN.md 方案文档,完整讲解「出片质量工程化」与「产出物可直接发布」两条改造主线:如何用时长画像(DurationProfile)取代写死的播客参数、如何用程序化校正把 LLM 给出的时间区间吸附到字幕 cue 边界、如何用评分兜底消灭「切片为 0」,以及如何通过一次 ffmpeg 调用把素材切片渲染成带竖屏画幅、烧字幕、标题卡的抖音 / 小红书 / Shorts / B 站成片。读完你将掌握这套方案的完整参数、实现位置与命令行用法,并能在自己的项目里直接复用。
一、为什么两条线要一起看
方案文档在开头提出一个关键判断:用户看到的不是「流水线跑成功」,而是切出来的东西能不能直接用。文档引用了 issue #59 的典型反馈——「一个 5 分钟的视频,剪出来三个视频每个 2 分钟,几个大章节都识别不出来」——这句话其实同时暴露了两类问题:
- 边界切得不对:切片时长失控、起点落在句子中间,属于出片质量问题;
- 切出来也只是素材:即使切对了,产出物是 16:9 无字幕的原始切片,不能直接上传抖音 / 小红书 / Shorts / B 站,属于发布问题。
因此方案把「质量工程化」和「可发布导出」放在同一份文档里推进,共享同一个目标:让产出物离「可直接发布」更近一步。这也是本文两条主线的由来。
二、诊断:现在的流水线为什么会出这些片
方案的第一步是诊断。读完 backend/pipeline/step1_outline.py、step2_timeline.py、step3_scoring.py 与根目录 prompt/时间点.txt 后,文档给出的结论是:问题不在模型,在工程。七类现象与根因可以归纳如下:
| # | 现象 | 根因 | 代码位置 |
|---|---|---|---|
| 1 | 5 分钟视频切出 3×2 分钟;长视频切得还行 | 提示词按 60 分钟播客写死「硬性最小 90 秒」「目标 3–6 分钟」「30 分钟块提取 2–5 个话题」,程序没有任何地方感知视频总时长,短视频被硬套长视频参数 | prompt/时间点.txt 时长控制节、prompt/大纲.txt 数量控制节、step1_outline.py 固定分块逻辑 |
| 2 | 边界落在句子中间 / 早了几秒 | 时间戳完全由 LLM「算」,程序只做块内 clamp、不对齐字幕 cue;且-ss放在-i前加-c:v copy会导致起点吸附到前一个关键帧,GOP 大时可提前数秒 | step2_timeline.py 的解析校验;video_processor.py 的切割参数 |
| 3 | 切片为 0 | 三处「一错全丢」:评分返回数量与输入不等 → 整块丢弃;全部低于 0.7 → 0 片;JSON 解析失败 → 跳过块,没有任何兜底 | step3_scoring.py、阈值判定逻辑 |
| 4 | 评分不准 | 评分只看outline+ 要点列表,不看转写原文;「传播潜力」这类维度靶子太虚 | step3_scoring.py、prompt/推荐理由.txt |
| 5 | 选了「知识 / 商业」类型没区别 | 桌面端SimplePipelineAdapter从不传prompt_files,prompt 下的<category>/目录形同虚设 | simple_pipeline_adapter.py |
| 6 | 重叠 / 重复片段 | 各块独立定位,跨块无去重;同块内 LLM 也可能返回重叠区间 | step2_timeline.py 只做了排序 |
| 7 | 改了提示词不知道变好还是变坏 | 没有回归集、没有指标、LLM 输出不可复现 | — |
方案给出的总原则非常明确:先把「程序能做的事」从 LLM 手里拿回来——算时长、对齐边界、去重、兜底都交给确定性的代码,然后再谈提示词与模型。下面两节分别展开两条改造线。
三、线 1:出片质量工程化
3.1 质量目标
- 5 分钟视频出 3–6 条 30–90 秒的片;60 分钟播客出 6–12 条 2–6 分钟的片(时长自适应);
- 每条片的起止都落在字幕 cue 边界,不切半句;
- 「切片为 0」只在字幕为空时发生;其余情况至少保底 top-K;
- 任何提示词 / 阈值改动都能在回归集上跑出数字。
3.2 A. 时长画像(DurationProfile)
核心实现位于 backend/pipeline/quality.py。它从 SRT 总时长算出 tier,每个 tier 给一组切片参数,然后生成一段中文「本次任务参数」追加到 step1 / step2 提示词末尾,覆盖提示词里写死的 90 秒 / 3–6 分钟规则。
分档逻辑见profile_for()(quality.py):
| tier | 判定(总时长) | min_clip_sec | target_clip_sec | max_clip_sec | topics_hint | min_keep / max_clips |
|---|---|---|---|---|---|---|
short | < 8 分钟 | 20 s | 30–90 s | 150 s | 3–6 | 2 / 6 |
medium | 8–30 分钟 | 45 s | 60–180 s | 300 s | 4–10 | 3 / 10 |
long | > 30 分钟 | 90 s | 120–360 s | 480 s | 按小时数线性放大(max(6, 6×h)起) | 3 /max(12, 16×h) |
DurationProfile还带三个算法参数:snap_window_sec=3.0(吸附到最近 cue 的搜索窗口)、merge_gap_sec=5.0(过短片段与相邻段间隔小于此则合并)、overlap_merge_ratio=0.5(重叠超过较短者此比例 → 合并)。
prompt_hint()(quality.py)生成的追加段落关键内容如下:
--- ## 本次任务参数(优先级高于上文所有时长与数量规则) - 视频总时长:5 分 0 秒(short 类型) - 整条视频建议提取 3–6 个话题;话题之间不要重叠 - 每个片段目标时长 30 秒–90 秒,最短不少于 20 秒,最长不超过 150 秒 - 上文中「至少 90 秒」「3–6 分钟」等具体数字一律以本节为准 - 起止时间必须落在字幕行的边界上,直接引用字幕行的时间戳,不要自行推算接入点有两处:step1 在extract_outline里用profile_from_srt + save_profile落盘并拼接到大纲提示词(step1_outline.py),同时分块间隔从「长视频固定 30 分钟」改为按 tier 计算——长视频 30 分钟一块,短 / 中视频整条一块;step2 用load_profile读取并拼接到时间线提示词(step2_timeline.py)。画像会写入metadata/duration_profile.json,供后续步骤与质量报告复用。
3.3 B. 程序化校正(clip_refiner)
同样位于 quality.py,核心函数是refine_timeline()(quality.py),全部是纯函数、可单测。它对 step2 的 LLM 时间区间依次执行五步:
- 吸附(snap):起点吸附到最近 cue 的
start、终点吸附到最近 cue 的end;±3 s 窗口内取最近,否则取包含该时刻(或其后第一条)的 cue; - 时长下限:沿 cue 向后延到
min_clip_sec,撞到下一段或视频末尾就停;仍不够 → 与相邻段间隔 < 5 s 时合并,否则丢弃; - 时长上限:按 cue 边界截到
max_clip_sec; - 去重:按起点排序后,重叠 > 较短者 50% → 合并(保留前者 outline、拼接 content);小重叠 → 后者起点推到前者终点所在 cue;
- 重新编号,输出
metadata/quality_report.json:包含吸附偏移分布(snap_offsets_sec、snap_offset_p50/p90)、合并 / 丢弃条目与原因、时长分布与coverage覆盖率。
接入点在run_step2_timeline的末尾(step2_timeline.py),所有调用方——桌面端、Celery、CLI——都经过这里,因此一处接入全局生效。校正失败时降级为沿用原始结果并打日志,不会让流水线崩溃。
3.4 C. 评分兜底 + 类别提示词
方案文档给评分环节的修复包含四点,全部能在 step3_scoring.py 与 quality.py 中找到实现:
- 数量对齐:
align_scores()(quality.py)在 LLM 返回数量与输入不一致时按outline文本对齐,对不上的给 0.5 + 「未评分(自动兜底)」,不再整块丢; - 选片兜底:
select_clips()(quality.py)阈值之上全留(标记selected_by="threshold");不足min_keep时按分数补齐并标selected_by="fallback";超过max_clips时按分截断; - 评分输入加转写原文:
ClipScorer._excerpt()用excerpt_between()(quality.py)把时间范围内的转写原文截断到约 600 字喂给模型,让分数落在内容上而不是标题上; - 类别提示词:
SimplePipelineAdapter._prompt_files()(simple_pipeline_adapter.py)读取项目video_category,通过get_prompt_files(category)(shared_config.py)把prompt/<category>/目录下的专用提示词传给各步,目录缺失时逐文件回退到默认提示词。
此外评分阈值解析也有了完整优先级:CLI 显式--min-score> 设置页「最低评分阈值」(settings.json,热重载)> 代码默认0.7,见resolve_min_score_threshold()(step3_scoring.py)。
3.5 D. 回归集与指标
质量改造没有指标就没有闭环。方案落地了两件配套工程:
LLM 录制 / 回放缓存:设置AUTOCLIP_LLM_CACHE_DIR环境变量后,LLMClient按sha1(prompt+input)把响应落盘 / 读盘;CI 里只回放,零 API 费用、结果可复现。
eval 框架(backend/eval):
- 每个 case 是一个目录
eval/cases/<name>/,内含input.srt、timeline.json、expect.json;expect.json写的是约束而不是标准答案,字段包括clips_min、clips_max、duration_min、duration_max、must_not_zero、coverage_min(可选人工标的黄金区间); - eval/metrics.py 计算片数、时长分布、覆盖率、零片率等指标并逐项判定;
- 运行方式:
python -m backend.eval跑全部 case 打印表格并写eval/reports/<date>.json;--live预留真调模型并录制(当前会提示先录制再回放,见 eval/main.py)。
仓库自带的首个 case eval/cases/short-synthetic/expect.json 的约束是:片数 3–6、单条时长 20–150 s、不允许零片、覆盖率 ≥ 0.35——正好对应方案文档验收表里「5 分钟视频不再出 2 分钟片」这一条。
3.6 E.(后续)可插拔演进
方案还预留了后续方向:Step 3 评分后端可插拔(接纳 #75 思路)、ASR 可插拔(#67)、基于回归集做提示词 A/B。
四、线 2:产出物「可直接发布」
4.1 设计原则
- 默认流水线不变:仍出 16:9 原始切片(stream copy,快);发布导出是按需、单条,用户点了才编码;
- 一个 ffmpeg 调用出一个成片:filter graph 由代码拼,不引入 MoviePy 之类的重依赖;
- 预设即规格包:画幅 / 分辨率 / 时长上限 / 字幕样式 / 是否标题卡都封装在预设里。
4.2 导出服务与预设规格
核心实现在 backend/services/publish_export.py。预设定义(publish_export.py):
| 预设 key | 平台 / 形态 | 分辨率 | 布局 layout | 时长上限 |
|---|---|---|---|---|
douyin | 抖音 9:16 | 1080×1920 | blur(原片等宽居中 + 高斯模糊放大底) | 无 |
xiaohongshu | 小红书 9:16 | 1080×1920 | blur | 无 |
shorts | YouTube Shorts | 1080×1920 | crop(居中裁切) | 60 s(超长截断并告警) |
bilibili | B 站横屏 | 1920×1080 | fit(等比缩放 + 黑边填充) | 无 |
original | 原画重编码 | 跟随源 | none | 无 |
三种竖屏 / 横屏布局的 filter 实现在_layout_filters()(publish_export.py):blur用split=2分路、背景scale+force_original_aspect_ratio=increase+crop+gblur=sigma=24、前景scale后居中overlay;crop直接scale+crop;fit用scale+pad加黑边。
编码参数(export_clip(),publish_export.py)同时解决了线 1 的关键帧吸附问题:-ss前置快速定位 + 重新编码(libx264 veryfast crf 20+aac 160k++faststart),帧精确输出。
烧字幕:从项目 SRT 切出片段区间、平移时间、写临时 SRT(slice_srt(),publish_export.py),通过subtitles=filter 与force_style控制字号 / 描边 / 底部边距(Fontsize=16, Outline=2, MarginV=48)。
标题卡:drawtext前 4 秒显示generated_title(用textfile=规避转义问题),半透明底条(boxcolor=black@0.45)。
中文字体:resolve_cjk_font()(publish_export.py)按平台探测——macOS 用PingFang.ttc,Linux / Docker 用NotoSansCJK,Windows 用msyh.ttc;找不到时跳过标题卡并返回警告。
输出与幂等:输出到output/exports/{clip_id}_{preset}.mp4(关闭字幕 / 标题卡或覆盖 layout 时文件名追加_nosub/_notitle/_{layout}后缀),同参数已存在直接返回cached=True,不重复编码;ffmpeg / ffprobe 路径可通过AUTOCLIP_FFMPEG_PATH/AUTOCLIP_FFPROBE_PATH环境变量指定(见 backend/utils/ffmpeg_utils.py)。
4.3 入口:API / CLI / MCP
REST API(backend/api/v1/projects.py):
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /projects/{id}/export-presets | 列出可用预设 |
| POST | /projects/{id}/clips/{clip_id}/export | 后台线程启动导出,返回 job_id(body 可传preset/subtitles/title_card/layout) |
| GET | /projects/{id}/exports/{job_id} | 查询任务状态与结果(queued/running/completed/failed,含 percent) |
| GET | /projects/{id}/exports/{job_id}/download | 下载成片文件(未完成返回 409) |
CLI(backend/cli.py 与参数定义 cli.py):
# 列出全部预设 autoclip export <project_id> --list-presets # 导出单个切片为抖音竖屏(默认预设即 douyin) autoclip export <project_id> --clip 2 --clip 5 --preset douyin # 导出全部切片为 B 站横屏,不带字幕与标题卡 autoclip export <project_id> --preset bilibili --no-subtitles --no-title # 给脚本 / agent 用:JSON 输出 autoclip export <project_id> --preset shorts --jsonMCP 工具:export_clip(project_id, clip_id, preset, subtitles, title_card),定义于 backend/mcp_server.py,返回成片路径,同参数再导走缓存。
4.4 桌面端入口
ClipCard 的占位「投稿」被换成「发布导出」:Dialog 里用Segmented选预设、开关字幕 / 标题卡,ProgressLine显示进度,完成后一个按钮下载,全部使用frontend/src/ui原语并按 DESIGN.md 落地。
4.5 D.(后续)演进方向
方案规划了后续能力:说话人居中裁切(人脸检测轨迹 + 平滑)、封面图(高能量帧 + 标题)、多平台直传(B 站已有半成品)。
五、验收标准与当前进度
5.1 验收方式
| 项 | 怎么验 |
|---|---|
| 5 分钟视频不再出 2 分钟片 | eval caseshort-*:片数 3–6,时长 20–150 s |
| 边界对齐 | quality_report.snap_offsetsp90 < 0.5 s;人工抽看 5 条无半句 |
| 零片率 | eval 全部 caseclips >= min_keep;fallback_rate有数但不为 100% |
| 发布导出 | 三个预设各导一条:ffprobe 分辨率正确、字幕可见、标题卡前 4 秒出现、时长与元数据一致(±0.1 s) |
| 不伤旧路径 | 现有单测 + docker-smoke 全绿;默认导出仍是 stream copy 的 16:9 |
5.2 推进记录
- 2026-09-07:方案起草并落地线 1 A–D、线 2 A–C;
- 已合入:
backend/pipeline/quality.py,step1/2/3 与SimplePipelineAdapter接入;LLM 缓存;backend/eval(short-synthetic绿);publish_export.py+ API/CLI/MCP + ClipCard Dialog; - 单测通过(含 backend/tests/test_quality.py 与 backend/tests/test_publish_export.py,其中
test_export_original_reencodes_and_is_idempotent用 lavfi 生成 3 秒测试视频验证了 original 预设的帧精确重编码与幂等返回);frontendtsc干净; - 未做:真 5 分钟视频对照、竖屏预设人工看片、说话人跟踪、封面图。
六、从方案到代码的验证路径
如果你想深入验证这套方案,仓库里可以直接对照阅读的链路如下:
- 参数与算法:backend/pipeline/quality.py(
DurationProfile/refine_timeline/align_scores/select_clips); - 流水线接入:step1_outline.py、step2_timeline.py、step3_scoring.py、simple_pipeline_adapter.py;
- 回归指标:backend/eval/main.py、backend/eval/metrics.py、backend/eval/cases/short-synthetic/expect.json;
- 发布导出:backend/services/publish_export.py、backend/api/v1/projects.py、backend/cli.py、backend/mcp_server.py;
- 测试佐证:backend/tests/test_quality.py、backend/tests/test_publish_export.py。
这五条链路合起来,就是「把程序能算的事从 LLM 手里拿回来 + 一个 ffmpeg 调用出成片」这份方案从设计文档到可运行代码的完整落地过程。
【免费下载链接】autoclipAutoClip : AI-powered video clipping and highlight generation · 一款智能高光提取与剪辑的二创工具项目地址: https://gitcode.com/GitHub_Trending/autoc/autoclip
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考