with one click
videocut-talkcut
口播视频转录和口误识别。生成审查稿和删除任务清单。触发词:talkcut、处理视频、识别口误
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
口播视频转录和口误识别。生成审查稿和删除任务清单。触发词:talkcut、处理视频、识别口误
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
视频剪辑 Agent,专为口播视频设计 功能: - 语义理解:AI 逐句分析,识别重说/纠正/卡顿 - 静音检测:自动标记过长静音段 - 重复句检测:删除重复内容,保留最佳表达 - 文案生成:基于保留内容自动生成视频介绍草稿 - subtitles生成:Whisper 转录 + 词典纠错 - 自更新:记住用户偏好,越用越准 触发场景: - 用户说"/videocut:install" - 用户说"/videocut:cut [视频文件]" - 用户说"/videocut:subtitle [视频文件]" - 用户说"/videocut:update"
evolution skills。记录用户反馈,更新方法论和规则。触发词:更新规则、记录反馈、改进skill
环境准备。install依赖、配置 API Key、验证环境。触发词:install、环境准备、初始化
subtitles生成与烧录。火山引擎转录→词典纠错→审核→烧录。触发词:加subtitles、生成subtitles、subtitles
| name | videocut:talkcut |
| description | 口播视频转录和口误识别。生成审查稿和删除任务清单。触发词:talkcut、处理视频、识别口误 |
语音转录 + AI 口误识别 + 网页审核
补充资料:
用户: 帮我剪这个口播视频
用户: 处理一下这个视频
{DEFAULT_OUTPUT_DIR}/
└── YYYY-MM-DD_视频名/
├── talkcut/
│ ├── 1_transcribe/
│ │ ├── audio.wav
│ │ ├── audio_timeline.json
│ │ ├── volcengine_result.json (仅火山引擎方案)
│ │ └── subtitles_words.json
│ ├── 2_analysis/
│ │ ├── readable.txt
│ │ ├── sentences.txt
│ │ ├── auto_selected.json
│ │ └── 口误分析.md
│ └── 3_review/
│ ├── audio.wav
│ ├── audio_timeline.json
│ ├── review.html
│ └── 视频介绍草稿.md
└── subtitles/
└── ...
规则:已有文件夹则复用,否则新建。
0. 创建输出目录
↓
1. 提取和视频时间轴对齐的审核音频
↓
2. 选择转录方案(读取 .env 的 ASR_ENGINE)
├─ volcengine: 上传 → 火山引擎 API → generate_subtitles.js
└─ whisper: 本地 whisper_transcribe.py
↓
3. 得到 subtitles_words.json(两条路径汇合)
↓
4. AI 分析口误/静音,生成预选列表 (auto_selected.json)
↓
4.2 规范化预选列表(补短停顿桥接)
↓
4.5 生成视频介绍草稿 (视频介绍草稿.md)
↓
5. 生成审核网页 (review.html)
↓
6. 启动审核服务器,用户网页确认
↓
【等待用户确认】→ 网页点击「执行剪辑」或手动 /剪辑
# 读取 .env 中的 DEFAULT_OUTPUT_DIR
SKILL_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
ENV_FILE="$SKILL_ROOT/.env"
OUTPUT_ROOT=$(grep DEFAULT_OUTPUT_DIR "$ENV_FILE" | cut -d'=' -f2)
如果 DEFAULT_OUTPUT_DIR 为空(首次使用):
.env 的 DEFAULT_OUTPUT_DIR= 行output/ 子目录# 变量设置(根据实际视频调整)
VIDEO_PATH="/path/to/视频.mp4"
VIDEO_NAME=$(basename "$VIDEO_PATH" .mp4)
DATE=$(date +%Y-%m-%d)
BASE_DIR="${OUTPUT_ROOT}/${DATE}_${VIDEO_NAME}/talkcut"
# 创建子目录
mkdir -p "$BASE_DIR/1_transcribe" "$BASE_DIR/2_analysis" "$BASE_DIR/3_review"
cd "$BASE_DIR"
cd 1_transcribe
# 提取和视频时间轴对齐的审核音频,并写出时间轴元数据
node "$SKILL_DIR/scripts/extract_review_audio.js" "$VIDEO_PATH" audio.wav audio_timeline.json
说明:
audio.wavaudio_timeline.json 用来把审核页时间轴和源视频时间轴对齐,避免不同视频的音频起点差异导致切点偏移读取 .env 中的 ASR_ENGINE:
volcengine → 方案 Awhisper → 方案 B# 1. 转成便于上传的 mp3
ffmpeg -y -i audio.wav -c:a libmp3lame audio.mp3
# 2. 上传获取公网 URL
curl -s -F "files[]=@audio.mp3" https://uguu.se/upload
# 返回: {"success":true,"files":[{"url":"https://h.uguu.se/xxx.mp3"}]}
# 3. 调用火山引擎 API
node "$SKILL_DIR/scripts/volcengine_transcribe.js" "https://h.uguu.se/xxx.mp3"
# 输出: volcengine_result.json
# 4. 生成字级别subtitles
node "$SKILL_DIR/scripts/generate_subtitles.js" volcengine_result.json
# 输出: subtitles_words.json
先检查 mlx-whisper 是否已install,未install则自动install:
python3 -c "import mlx_whisper" 2>/dev/null || pip3 install mlx-whisper
执行转录(首次运行会自动下载模型,约 1.5GB):
python3 "$SKILL_DIR/scripts/whisper_transcribe.py" audio.wav
# 直接输出: subtitles_words.json(已包含 gap 检测,无需再调 generate_subtitles.js)
cd ..
→ 两条路径都输出 subtitles_words.json,后续步骤完全一致。
cd 2_analysis
node -e "
const data = require('../1_transcribe/subtitles_words.json');
let output = [];
data.forEach((w, i) => {
if (w.isGap) {
const dur = (w.end - w.start).toFixed(2);
if (dur >= 0.5) 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'));
"
先读 user_rules/ 目录下所有规则文件。
必须先分句,再分析。按静音切分成句子列表:
node -e "
const data = require('../1_transcribe/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_transcribe/subtitles_words.json');
const selected = [];
words.forEach((w, i) => {
if (w.isGap && (w.end - w.start) >= 0.5) selected.push(i);
});
require('fs').writeFileSync('auto_selected.json', JSON.stringify(selected, null, 2));
console.log('≥0.5s静音数量:', selected.length);
"
→ 输出 auto_selected.json(只含静音 idx)
检测规则(按优先级):
| # | 类型 | 判断方法 | 删除范围 |
|---|---|---|---|
| 1 | 重复句 | 相邻句子开头≥5字相同 | 较短的整句 |
| 2 | 隔一句重复 | 中间是残句时,比对前后句 | 前句+残句 |
| 3 | 残句 | 话说一半+静音 | 整个残句 |
| 4 | 句内重复 | A+中间+A 模式 | 前面部分 |
| 5 | 卡顿词 | 那个那个、就是就是 | 前面部分 |
| 6 | 重说纠正 | 部分重复/否定纠正 | 前面部分 |
| 7 | 语气词 | 嗯、啊、那个 | 标记但不自动删 |
核心原则:
分段分析(循环执行):
1. Read readable.txt offset=N limit=300
2. 结合 sentences.txt 分析这300行
3. 追加口误 idx 到 auto_selected.json
4. 记录到 口误分析.md
5. N += 300,回到步骤1
🚨 关键警告:行号 ≠ idx
readable.txt 格式: idx|内容|时间
↑ 用这个值
行号1500 → "1568|[静1.02s]|..." ← idx是1568,不是1500!
口误分析.md 格式:
## 第N段 (行号范围)
| idx | 时间 | 类型 | 内容 | 处理 |
|-----|------|------|------|------|
| 65-75 | 15.80-17.66 | 重复句 | "这是我剪出来的一个案例" | 删 |
被删句子之间夹着的短 gap 也应自动标记删除,避免审核页留下碎片:
node -e "
const words = require('../1_transcribe/subtitles_words.json');
const selected = new Set(require('./auto_selected.json'));
let added = 0;
words.forEach((w, i) => {
if (!w.isGap || selected.has(i)) return;
let prev = i - 1;
while (prev >= 0 && words[prev].isGap) prev--;
let next = i + 1;
while (next < words.length && words[next].isGap) next++;
if (prev >= 0 && next < words.length && selected.has(prev) && selected.has(next)) {
selected.add(i);
added++;
}
});
const sorted = Array.from(selected).sort((a, b) => a - b);
require('fs').writeFileSync('auto_selected.json', JSON.stringify(sorted, null, 2) + '\\n');
if (added) console.log('🧹 自动清理孤立小间隙:', added, '个');
"
AI 补完索引后,再执行一次规范化,补上夹在两个待删片段之间的短停顿:
node "$SKILL_DIR/scripts/refine_auto_selected.js" \
"../1_transcribe/subtitles_words.json" \
"auto_selected.json"
补充规则:
<0.5s 的停顿,前后最近的口播词都已经在 auto_selected.json 里,这个停顿也默认删掉subtitles_words.json 里会按整段保留,不再拆成很多个 1.0s这一步必须由 Claude 直接完成,不要用本地模板脚本代写。
按 show-notes.md 的要求,基于当前准备保留的内容生成:
../3_review/视频介绍草稿.md
要求:
../3_review/视频介绍草稿.md,不能只在对话里临时输出review.htmlcd ../3_review
# 先确认视频介绍草稿已经落盘
test -s "视频介绍草稿.md"
# 6. 生成审核网页
node "$SKILL_DIR/scripts/generate_review.js" ../1_transcribe/subtitles_words.json ../2_analysis/auto_selected.json ../1_transcribe/audio.wav
# 输出: review.html
# 7. 启动审核服务器
node "$SKILL_DIR/scripts/review_server.js" 8899 "$VIDEO_PATH"
# 打开 http://localhost:8899
用户在网页中:
如果 test -s "视频介绍草稿.md" 失败:
注意:
audio_source.wav 试听;如果源路径失效,则继续复用当前目录已有的源音轨文件audio_timeline.json 会一并复制到审核目录,导出时按每个视频动态校准时间轴,不写死固定偏移[
{"text": "大", "start": 0.12, "end": 0.2, "isGap": false},
{"text": "", "start": 6.78, "end": 7.48, "isGap": true}
]
[72, 85, 120] // Claude 分析生成的预选索引
VOLCENGINE_API_KEY=xxx # 火山引擎 API Key(方案 A 需要)
DEFAULT_OUTPUT_DIR=xxx # 默认输出目录
ASR_ENGINE= # 转录方案: volcengine / whisper,留空每次询问
CUT_KEEP_PADDING_MS=300 # 保留片段语音边界前后缓冲
CUT_MIN_DELETE_MS=120 # 小于该时长的删除段默认忽略
CROSSFADE_MS=30 # 片段音频接缝淡化
获取指南:https://my.feishu.cn/wiki/Gh0MwxHePidsYfkIx7zcvJQynqc?from=from_copylink
# 编辑 .env 填入 VOLCENGINE_API_KEY=xxx