video-clipper

تایید شده

从长视频(直播回放、会议录像、播客)中批量生成短视频切片。基于转写文稿精确定位观点边界,自动去除静音卡顿和口吃,输出音画同步的短视频。适用于:直播切片、会议精华提取、短视频二创、播客精彩片段。

@yfge
MIT۱۴۰۵/۱/۱۴
(0)
۱
۲
۲

نصب مهارت

مهارت‌ها کدهای شخص ثالث از مخازن عمومی GitHub هستند. SkillHub الگوهای مخرب شناخته‌شده را اسکن می‌کند اما نمی‌تواند امنیت را تضمین کند. قبل از نصب، کد منبع را بررسی کنید.

نصب سراسری (سطح کاربر):

npx skillhub install yfge/video-skills-suite/video-clipper

نصب در پروژه فعلی:

npx skillhub install yfge/video-skills-suite/video-clipper --project

مسیر پیشنهادی: ~/.claude/skills/video-clipper/

محتوای SKILL.md

---
name: video-clipper
description: 从长视频(直播回放、会议录像、播客)中批量生成短视频切片。基于转写文稿精确定位观点边界,自动去除静音卡顿和口吃,输出音画同步的短视频。适用于:直播切片、会议精华提取、短视频二创、播客精彩片段。
---

# Video Clipper — 长视频智能切片

## 依赖

- **ffmpeg / ffprobe**: 视频切片、静音检测、trim+concat(系统已安装)
- **whisperx venv**: `scripts/.venv-whisperx/`(用于二次质检)
- **转写 JSON**: 需要 `video-to-text` skill 的带时间戳 JSON 输出(用于精确定位和口吃检测)
- **脚本集**(均在 `workspace/scripts/`):
  - `batch-clip-v4.sh` — 主批量切片脚本(去静音 + 去口吃 + crossfade)
  - `smart-silence.py` — 智能静音处理(长静音删除 / 短静音压缩)
  - `stutter-detect.py` — 口吃检测(基于原始转写 JSON)
  - `clip-postcheck.py` — WhisperX 二次扫描 + 自动修复残留口吃
  - `batch-postcheck.sh` — 批量二次质检脚本
  - `iterate-until-clean.py` — 单条 clip 反复迭代(转写→检测→修复→再转写)直到零问题

## 完整四阶段流程

```
Phase 1: 素材准备
Phase 2: 切片点定位(观点边界)
Phase 3: 批量切片(去静音 + 去口吃 + crossfade)← batch-clip-v4.sh
Phase 4: 二次质检(WhisperX 重转写 → 残留口吃检测修复)← batch-postcheck.sh
```

---

### Phase 1: 素材准备

1. **确认输入文件**,获取总时长
2. **CJK 文件名处理**:含中文则建英文 symlink,后台 nohup 命令需用英文路径
   ```bash
   ln -sf "/path/to/直播回放.mp4" /tmp/livestream-input.mp4
   ```
3. **确认转写 JSON 存在**:`video-to-text` skill 的输出(含 word_segments 级时间戳)

---

### Phase 2: 切片点定位

**最关键的一步——不能凭直觉猜时间戳,必须基于转写文字精确定位。**

#### 2.1 候选话题来源
- 优先用 `insight-extractor` 输出中的金句和时间戳
- 或人工给出话题名,再用转写 JSON 定位

#### 2.2 用转写 JSON 校准边界

```python
import json

with open("transcript.json") as f:
    data = json.load(f)

for seg in data["segments"]:
    if START <= seg["start"] <= END:
        m, s = divmod(int(seg["start"]), 60)
        h, m = divmod(m, 60)
        ts = f"{h}:{m:02d}:{s:02d}" if h else f"{m}:{s:02d}"
        print(f"  [{ts}] {seg['text'].strip()}")
```

#### 2.3 精确边界原则

1. **观点完整**:从引入/铺垫 → 结论/反应,不截断
2. **前不带冗余**:切掉闲聊、过渡、无关内容
3. **后不拖尾**:观点讲完即切,不带下一话题开头
4. **扩展确认**:向前后各扩 2-3 分钟,确认边界无误

#### 2.4 时间戳格式

- < 60 分钟:`MM:SS`(如 `41:40`)
- ≥ 60 分钟:`H:MM:SS`(如 `1:50:08`)

---

### Phase 3: 批量切片(主流程)

使用 `scripts/batch-clip-v4.sh`。

#### 三层清理管道

1. **精确粗切** (`ffmpeg -ss ... -to ...`):从原视频按时间段切出原始片段
2. **静音 + 口吃跳切** (`smart-silence.py` + `stutter-detect.py`):
   - 长静音(≥0.5s):完全跳过
   - 短静音(0.25-0.5s):压缩到 0.12s(保留呼吸感)
   - 口吃(重复词/短语):基于原始转写 JSON 检测,跳过第一次出现
3. **filter_complex trim+concat + crossfade**:
   - 视频:plain concat(避免视觉闪烁)
   - 音频:链式 `acrossfade`(每拼接点 20ms,消除"咔哒"感)

#### ⚠️ 关键约束

- **不能用 `select/aselect`**:长视频音视频时间基不同,会产生漂移
- **必须用 `nohup` 后台运行**:14 条 × 每条约 2-3 分钟,总耗时 ~20-30 分钟
- **macOS zsh 无 `mapfile`**:用 `while IFS= read -r line` 替代
- **时间戳 >59:59 时**:必须用 `H:MM:SS` 格式(`109:30` → `1:49:30`)

#### 运行方式

```bash
# 确认 symlink 存在
ln -sf "/path/to/原始视频.mp4" /tmp/livestream-input.mp4

# 后台运行
nohup bash workspace/scripts/batch-clip-v4.sh > /tmp/batch-clip-v4.log 2>&1 &

# 监控
tail -f /tmp/batch-clip-v4.log
```

#### 修改切片列表

编辑 `batch-clip-v4.sh` 中的 `clips=()` 数组,格式为 `"start|end|name"`:

```bash
clips=(
  "30:16|31:58|01-胆子够大"
  "1:50:08|1:52:20|02-AI后背发凉"
  ...
)
```

---

### Phase 4: 二次质检(WhisperX post-check)

**为什么需要二次质检?**

口吃检测基于原始转写 JSON,但原始转写有时会漏掉某些口吃(没有转写出来)。  
编辑后的 clip 可能仍含残留口吃,需要用 WhisperX **重新转写 clip 本身**,再做检测。

```
原始转写 JSON → stutter-detect.py → 跳切 → clip
                                              ↓
                                   clip-postcheck.py(WhisperX 重转写)
                                              ↓
                                   发现残留 → 自动二次修复
```

#### 运行 post-check

```bash
# 激活 whisperx venv
source scripts/.venv-whisperx/bin/activate

# 单条检测(不修复,只报告)
python3 scripts/clip-postcheck.py clips/02-AI后背发凉.mp4

# 单条检测 + 自动修复(-fixed.mp4 会替换原文件)
python3 scripts/clip-postcheck.py clips/02-AI后背发凉.mp4 --fix

# 批量检测 + 自动修复全部 clips/
bash scripts/batch-postcheck.sh
```

#### clip-postcheck.py 检测项

1. **单字重复**(AA 型):运运、对对对 → 保留最后一个
2. **短语重复**(ngram 2-4 字):就是就是、当时的当时 → 保留后一个
3. **低置信度簇**:3 个以上 score<0.05 的连续字,且与后文重叠 → 标记为跳切残留

**有效叠词白名单(不误判)**:试试、看看、谢谢、刚刚、常常、爷爷、妈妈 等 100+ 词

#### 单条反复迭代(最严格模式)

对质量要求极高的 clip,用 `iterate-until-clean.py`:

```bash
source scripts/.venv-whisperx/bin/activate
python3 scripts/iterate-until-clean.py clips/02-AI后背发凉.mp4
```

每轮:WhisperX 转写 → 检测 → 修复 → 替换 → 再转写验证。  
最多 5 轮,通常 1-2 轮收敛。

#### 每条耗时

WhisperX 转写 1 分钟 clip ≈ 60-90 秒(CPU)。  
14 条全部 post-check ≈ 15-20 分钟。**务必后台运行**。

---

### Phase 5: 最终检查

```bash
for f in clips/*.mp4; do
  [ -f "$f" ] || continue
  sz=$(du -h "$f" | cut -f1)
  dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$f" 2>/dev/null | cut -d. -f1)
  min=$((dur/60)); sec=$((dur%60))
  printf "%-35s %5s  %d:%02d\n" "$(basename "$f")" "$sz" "$min" "$sec"
done
```

---

## 输出规格

- **位置**:`workspace/clips/<编号>-<名称>.mp4`
- **命名**:`01-胆子够大.mp4`、`02-AI后背发凉.mp4`
- **编码**:H.264 CRF23 + AAC 128kbps + faststart
- **大小**:3-25MB/条(取决于时长)
- **压缩率**:比原始切片平均短 20-30%(静音 + 口吃 + crossfade)

---

## 与其他 Skill 的衔接

```
video-to-text       →  转写 JSON(时间戳 + 口吃检测来源)
    ↓
insight-extractor   →  观点摘要(切片候选来源)
    ↓
video-clipper       →  短视频切片(本 skill)
    ├── Phase 3: batch-clip-v4.sh(去静音 + 去口吃 + crossfade)
    └── Phase 4: batch-postcheck.sh(WhisperX 二次质检)
    ↓
(人工 / 剪映加字幕)→  发布短视频平台
```

---

## 踩坑记录

| 问题 | 原因 | 解决 |
|------|------|------|
| 音画不同步 | `select/aselect` 滤镜音视频时间基不同 | 改用 `trim/atrim + concat` |
| 时间戳不认 | `109:30` 格式超过 59:59 | 改为 `H:MM:SS`(`1:49:30`)|
| macOS zsh 报错 | `mapfile` 不存在 | 用 `while IFS= read -r line` |
| 中文文件名乱码 | nohup 后台 + CJK 路径 | 建英文 symlink |
| exec session 超时 | 15-30 分钟任务被 SIGTERM | 所有批量任务必须 `nohup` |
| 残留口吃 | 原始转写漏识别,stutter-detect 抓不到 | Phase 4 WhisperX post-check |
| 跳切"咔哒"声 | trim+concat 拼接点音频突变 | 每拼接点加 20ms `acrossfade` |
| 口吃误判叠词 | 试试/看看/谢谢 被当作重复 | `VALID_REDUP` 白名单过滤 |

---

## 性能参考

| 切片数 | 总原始时长 | Phase 3 耗时 | Phase 4 耗时 |
|--------|-----------|-------------|-------------|
| 6 条 | ~15 min | ~8 min | ~10 min |
| 14 条 | ~35 min | ~20 min | ~20 min |
| 20 条 | ~50 min | ~30 min | ~28 min |