剪口播/SKILL.md
口播基础素材包生成。转录口播、识别口误、生成审核页;用户确认后剪出新视频,Agent 再基于剪后视频重新转写、AI 校对字幕,输出后续口播成片可用的 source_cut.mp4 和 subtitles.srt。触发词:剪口播、处理口播素材、准备口播素材、识别口误、基础素材包
npx skillsauth add ceeon/videocut-skills chengfeng-videocut-skills:剪口播Install this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
火山引擎转录 + AI 口误识别 + 网页审核 + 剪后重转写 + AI 字幕校对
用户: 帮我剪这个口播视频
用户: 处理一下这个视频
用户: 把这条录屏做成基础素材包
不要再把字幕生成拆成单独 Skill。剪口播 是口播基础素材包的唯一入口。
用户给一条口播录屏后,本 Skill 默认产出后续 口播成片 可直接使用的基础素材包:
source_cut.mp4 # 剪后口播视频
subtitles.srt # 基于剪后视频重新转写并 AI 校对后的字幕
assets/ # 可选截图、评论图、结果页
审核页只负责让用户确认“删哪里”并剪出新视频。字幕不能沿用原始视频字幕,也不能把火山转写初稿直接当最终字幕;Agent 必须基于剪后视频重新转写,并做 AI 校对后,才能写入 subtitles.srt。
output/
└── YYYY-MM-DD_视频名/
├── 剪口播/
│ ├── 1_转录/
│ │ ├── audio.mp3
│ │ ├── volcengine_result.json
│ │ └── subtitles_words.json
│ ├── 2_分析/
│ │ ├── readable.txt
│ │ ├── auto_selected.json
│ │ └── 口误分析.md
│ └── 3_审核/
│ ├── review.html
│ └── video.mp4 → 源视频(符号链接)
└── 字幕/
├── 1_转录/
│ ├── audio.mp3
│ └── volcengine_result.json
├── subtitles_with_time.json
└── 3_输出/
├── video.raw.srt
└── video.srt
规则:已有文件夹则复用,否则新建。
0. 创建输出目录
↓
1. 提取音频 (ffmpeg)
↓
2. 上传获取公网 URL (uguu.se)
↓
3. 火山引擎 API 转录
↓
4. 生成字级别字幕 (subtitles_words.json)
↓
5. AI 分析口误/静音,生成预选列表 (auto_selected.json)
↓
6. 生成审核网页 (review.html)
↓
7. 启动审核服务器,用户网页确认
↓
【等待用户确认】→ 网页点击「执行剪辑」
↓
8. 服务器只剪出新视频
↓
9. Agent 监听剪后视频生成完成
↓
10. 基于剪后视频重新提取音频,发火山转写,生成 video.raw.srt
↓
11. Agent 对照原稿/正文做 AI 校对,写 video.srt
↓
12. 整理基础素材包:source_cut.mp4 + subtitles.srt + 可选 assets/
剪后字幕硬规则:
video.raw.srt。video.srt / subtitles.srt 只能是 Agent AI 校对后的最终字幕。# 变量设置(根据实际视频调整)
VIDEO_PATH="/path/to/视频.mp4"
VIDEO_NAME=$(basename "$VIDEO_PATH" .mp4)
DATE=$(date +%Y-%m-%d)
BASE_DIR="output/${DATE}_${VIDEO_NAME}/剪口播"
# 创建子目录
mkdir -p "$BASE_DIR/1_转录" "$BASE_DIR/2_分析" "$BASE_DIR/3_审核"
cd "$BASE_DIR"
cd 1_转录
# 1. 提取音频(文件名有冒号需加 file: 前缀)
ffmpeg -i "file:$VIDEO_PATH" -vn -acodec libmp3lame -y audio.mp3
# 2. 上传获取公网 URL
curl -s -F "files[][email protected]" https://uguu.se/upload
# 返回: {"success":true,"files":[{"url":"https://h.uguu.se/xxx.mp3"}]}
# 3. 调用火山引擎 API
SKILL_DIR="/Volumes/成峰/代码/剪辑Agent/.claude/skills/剪口播"
"$SKILL_DIR/scripts/volcengine_transcribe.sh" "https://h.uguu.se/xxx.mp3"
# 输出: volcengine_result.json
node "$SKILL_DIR/scripts/generate_subtitles.js" volcengine_result.json
# 输出: subtitles_words.json
cd ..
cd 2_分析
node -e "
const data = require('../1_转录/subtitles_words.json');
let output = [];
data.forEach((w, i) => {
if (w.isGap) {
const dur = (w.end - w.start).toFixed(2);
if (dur >= 0.2) output.push(i + '|[静' + dur + 's]|' + w.start.toFixed(2) + '-' + w.end.toFixed(2));
} else {
output.push(i + '|' + w.text + '|' + w.start.toFixed(2) + '-' + w.end.toFixed(2));
}
});
require('fs').writeFileSync('readable.txt', output.join('\\n'));
"
先读 用户习惯/ 目录下所有规则文件。
必须先分句,再分析。按静音切分成句子列表:
node -e "
const data = require('../1_转录/subtitles_words.json');
let sentences = [];
let curr = { text: '', startIdx: -1, endIdx: -1 };
data.forEach((w, i) => {
const isLongGap = w.isGap && (w.end - w.start) >= 0.5;
if (isLongGap) {
if (curr.text.length > 0) sentences.push({...curr});
curr = { text: '', startIdx: -1, endIdx: -1 };
} else if (!w.isGap) {
if (curr.startIdx === -1) curr.startIdx = i;
curr.text += w.text;
curr.endIdx = i;
}
});
if (curr.text.length > 0) sentences.push(curr);
sentences.forEach((s, i) => {
console.log(i + '|' + s.startIdx + '-' + s.endIdx + '|' + s.text);
});
" > sentences.txt
node -e "
const words = require('../1_转录/subtitles_words.json');
const selected = [];
words.forEach((w, i) => {
if (w.isGap && (w.end - w.start) >= 0.2) selected.push(i);
});
require('fs').writeFileSync('auto_selected.json', JSON.stringify(selected, null, 2));
console.log('≥0.2s静音数量:', selected.length);
"
→ 输出 auto_selected.json(只含静音 idx)
🚨 火山只转语音,结尾的未转录杂音/收尾动作不在字幕里,所有检测器都看不见。必须比对视频时长补出来。见
用户习惯/3-静音段处理.md。
VDUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "file:$VIDEO_PATH")
node -e "
const fs=require('fs');
const w=require('../1_转录/subtitles_words.json');
const auto=require('./auto_selected.json');
const VDUR=$VDUR, last=w[w.length-1];
if(VDUR - last.end > 0.3){ // 结尾未覆盖 → 补尾元素并预选
w.push({text:'',start:last.end,end:VDUR,isGap:true,reason:'结尾未转录(杂音/收尾)'});
auto.push(w.length-1);
fs.writeFileSync('../1_转录/subtitles_words.json',JSON.stringify(w));
fs.writeFileSync('./auto_selected.json',JSON.stringify([...new Set(auto)].sort((a,b)=>a-b),null,1));
console.log('补尾元素 idx',w.length-1,'['+last.end+'→'+VDUR+']');
} else console.log('尾部已覆盖');
if(w[0].start>0.3) console.log('⚠️ 开头',w[0].start,'s 未覆盖,考虑补头元素');
"
🚨 核心原则:删前保后。所有重复/口误,删前面的,保后面的。
按检测类型分工,多 Agent 并行执行:
每个 Agent 只负责一种检测,prompt 更短更精确,避免规则互相干扰。
| Agent | 输入 | 检测内容 | 删除范围 | |-------|------|----------|----------| | A-句间重复 | sentences.txt | 相邻/隔一句开头≥5字相同 | 删前句整句 | | B-句内重复 | sentences.txt | 同一句内 A+中间+A 模式 | 只删前面片段,不删整句 | | C-残句 | sentences.txt | 话说一半+静音+后面重说 | 删残句整句 |
脚本可直接处理(不需要 AI):
复核(verify)—— 按风险投放,不要铺满(见 用户习惯/10-删除风险分层.md):
log/)。verify 很贵,砸在容易删错的地方就好。Agent prompt 模板:
给每个 Agent 的 prompt 包含:
1. 只放该 Agent 对应的一条检测规则(从用户习惯/读取)
2. 完整的 sentences.txt 内容
3. 明确要求:返回要删除的 idx 范围列表
4. 🚨 强调"删前保后":删前面的版本,保留后面更完整的版本
Agent 返回格式:
| 句号 | idx范围 | 类型 | 内容摘要 | 处理 |
|------|---------|------|----------|------|
删除idx列表: [所有要删除的idx]
合并结果:
收集所有 Agent 返回的 idx 列表 → 合并到 auto_selected.json → 去重排序
范围整段删除规则:标记口误时,从 startIdx 到 endIdx 之间的所有元素(含中间的 gap)全部加入 auto_selected。不要逐个挑选文字 idx 而跳过 gap。
🚨 关键警告:行号 ≠ idx
readable.txt 格式: idx|内容|时间
↑ 用这个值
行号1500 → "1568|[静1.02s]|..." ← idx是1568,不是1500!
口误分析.md 格式:
## 句间重复 (Agent A)
| 句号 | idx范围 | 内容摘要 | 处理 |
|------|---------|----------|------|
| 5 | 212-233 | 与句6重复,句6更完整 | 删前句 |
## 句内重复 (Agent B)
| 句号 | idx范围 | 内容摘要 | 处理 |
|------|---------|----------|------|
| 16 | 492-510 | "很多人一提到CLI命令"前半重复 | 删片段 |
## 残句 (Agent C)
| 句号 | idx范围 | 内容摘要 | 处理 |
|------|---------|----------|------|
| 7 | 266-275 | "为了解释为了回答这个"未完成 | 删整句 |
若用户提供口播稿/原文稿(口播是照稿读的),用它做句子级对齐补漏 —— 见 用户习惯/11-口播稿对齐.md。
口播稿 = 语义 ground truth(措辞会变,如"两个模型"↔"两个大模型",别逐字 diff)。把每个转录句对齐到口播稿句,抓纯文本检测漏掉的整句级口误:整句重说、残句、无对应口误 —— 尤其技术名词卡壳(Mindverse / δ-mem / LoRA 念错重来、"Delta Mam"、孤立单字"从")。这类整句删除属高风险 → 走复核,确认保留版覆盖了口播稿原意。
cd ../3_审核
# 6. 生成审核网页(传入视频文件,自动创建符号链接)
node "$SKILL_DIR/scripts/generate_review.js" ../1_转录/subtitles_words.json ../2_分析/auto_selected.json "$VIDEO_PATH"
# 输出: review.html, video.mp4(符号链接)
# 7. 启动审核服务器
node "$SKILL_DIR/scripts/review_server.js" 8899 "$VIDEO_PATH"
# 打开 http://localhost:8899
⚠️ 必须用 review_server.js,不能用
python3 -m http.server替代。 原因:视频播放依赖 HTTP Range 请求(206),python 简易服务器不支持,会导致视频无法播放/无声音。 普通用户保持这个终端窗口开着即可;Claude Code / Codex 由 Agent 使用当前环境可用的后台任务能力托管。不要要求用户安装 tmux。
用户在网页中:
用户点击审核页按钮后,review_server.js 只会剪出新视频,并在审核目录写入 cut_done.json。Agent 必须监听这个文件,确认剪后视频真实生成且大小稳定。
# 在另一个 Agent 任务/终端里等待用户确认后的剪辑结果
node "$SKILL_DIR/scripts/watch_cut_done.js" "output/YYYY-MM-DD_视频名/剪口播/3_审核"
监听结果会返回剪后视频路径:
{
"output": "output/YYYY-MM-DD_视频名/剪口播/3_审核/视频名_cut.mp4",
"newDuration": "123.45",
"outputSize": 12345678
}
把返回里的 output 记为 CUT_VIDEO,后续字幕必须基于这个视频重转写。
不要用 curl /api/cut 或脚本模拟点击,除非用户明确授权。
"$SKILL_DIR/scripts/generate_srt_for_video.sh" "剪后视频.mp4" "output/YYYY-MM-DD_视频名/字幕"
字幕必须基于剪后视频重新转写,不能用原始视频字幕反推。
这一步只产出火山转写初稿:
output/YYYY-MM-DD_视频名/字幕/1_转录/volcengine_result.json
output/YYYY-MM-DD_视频名/字幕/subtitles_with_time.json
output/YYYY-MM-DD_视频名/字幕/3_输出/video.raw.srt
Agent 必须读取 video.raw.srt 或 subtitles_with_time.json,对照用户给的口播稿/正文/专名上下文做 AI 校对。
校对规则:
字幕/3_输出/video.srt。AI 校对完成后,再把剪后视频和最终字幕整理到项目根目录:
OUTPUT_ROOT="output/YYYY-MM-DD_视频名"
ln -sf "$CUT_VIDEO" "$OUTPUT_ROOT/source_cut.mp4"
ln -sf "$OUTPUT_ROOT/字幕/3_输出/video.srt" "$OUTPUT_ROOT/subtitles.srt"
最终产物至少包括:
output/YYYY-MM-DD_视频名/字幕/3_输出/video.srt
output/YYYY-MM-DD_视频名/source_cut.mp4
output/YYYY-MM-DD_视频名/subtitles.srt
如果用户下一步要跑 口播成片,把产物整理成这个心智:
source_cut.mp4 = 剪后口播视频
subtitles.srt = AI 校对后的剪后字幕 SRT
assets/ = 可选素材
source_cut.mp4 和 subtitles.srt 可以是符号链接;给用户汇报时,要明确指出它们已经指向剪后视频和剪后字幕。
到审核页这一步必须停下来,把链接交给用户。
用户确认前,Agent 只能做三件事:
review.htmlreview_server.js除非用户明确说“你帮我点执行”或“直接剪”,否则 Agent 不允许:
/api/cut用户在审核页点击「执行剪辑」后,服务器只生成剪后视频。Agent 监听到剪后视频完成后,才能进入剪后重转写和 AI 校对字幕。
[
{"text": "大", "start": 0.12, "end": 0.2, "isGap": false},
{"text": "", "start": 6.78, "end": 7.48, "isGap": true}
]
[72, 85, 120] // Claude 分析生成的预选索引
⚠️ 匹配原片参数重编码,帧级精确切割。
cut_video.sh 的工作方式:
filter_complex trim+concat 帧级精确切割-profile:v high -b:v {原片码率} -pix_fmt yuv420p关键:重编码画质取决于是否匹配原片参数,不是 CRF 值。
-b:v {原片码率} -profile:v high -pix_fmt yuv420p → 肉眼无区别-crf N 不指定 profile/pix_fmt → 可能有偏差cd /Volumes/成峰/代码/剪辑Agent/.claude/skills
cp .env.example .env
# 编辑 .env 填入 VOLCENGINE_API_KEY=xxx
content-media
自进化 skills。记录用户反馈,更新方法论和规则。触发词:更新规则、记录反馈、改进skill
development
口播视频成片 Skill。把文章/口播稿/SRT、剪后视频和 HTML/图片素材串成分镜稿、时间线预览和最终 1080x1440 竖版 MP4。触发词:口播成片、做分镜稿、时间线预览、合成口播视频、导出竖屏MP4
content-media
视频高清导出。2-pass编码+锐化,匹配或超越原片画质。触发词:高清化、高清导出、导出高清、渲染高清
development
环境准备。安装依赖、配置 API Key、验证环境。触发词:安装、环境准备、初始化