SuperAPI AI Directory

video-clipper

Design & creation

View repository

Updated 10-16 09:44

About this skill

Video Clipper — Intelligent Long-Video Clipping

Dependencies

  • ffmpeg / ffprobe: Video clipping, silence detection, trim+concat (already installed on the system)
  • whisperx venv: scripts/.venv-whisperx/(for secondary quality check)
  • Transcription JSON: Requires video-to-text skill's timestamped JSON output (for precise positioning and stutter detection)
  • Script set(all in workspace/scripts/):
    • batch-clip-v4.sh — main batch clipping script (silence removal + stutter removal + crossfade)
    • smart-silence.py — intelligent silence processing (long-silence deletion / short-silence compression)
    • stutter-detect.py — stutter detection (based on the original transcription JSON)
    • clip-postcheck.py — WhisperX secondary scan + automatic repair of residual stutters
    • batch-postcheck.sh — batch secondary quality-check script
    • iterate-until-clean.py — repeatedly iterate a single clip (transcribe→detect→repair→re-transcribe) until zero issues remain

Complete four-phase workflow

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

Phase 1: Material preparation

  1. Confirm the input file, obtain the total duration
  2. CJK filename handling: if it contains Chinese, create an English symlink; background nohup commands must use English paths
    ln -sf "/path/to/直播回放.mp4" /tmp/livestream-input.mp4
    
  3. Confirm that the transcription JSON exists:video-to-text skill's output (including word_segments-level timestamps)

Phase 2: Clip point positioning

The most critical step — you cannot guess timestamps by intuition; you must locate precisely based on the transcribed text.

2.1 Candidate topic sources

  • Prioritize the golden quotes and timestamps in the insight-extractor output
  • Or manually provide a topic name, then use the transcription JSON to locate it

2.2 Use the transcription JSON to calibrate boundaries

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 Principles for precise boundaries

  1. Complete viewpoint: from introduction/setup → conclusion/reaction, without truncation
  2. No redundancy before: cut out chit-chat, transitions, and irrelevant content
  3. No trailing after: cut as soon as the viewpoint finishes, without including the start of the next topic
  4. Expansion confirmation: expand 2-3 minutes before and after to confirm the boundaries are correct

2.4 Timestamp format

  • < 60 minutes:MM:SS(e.g. 41:40)
  • ≥ 60 minutes:H:MM:SS(e.g. 1:50:08)

Phase 3: Batch clipping (main workflow)

Use scripts/batch-clip-v4.sh。

Three-layer cleanup pipeline

  1. Precise rough cut (ffmpeg -ss ... -to ...): cut the original segments from the source video by time range
  2. Silence + stutter jump cut (smart-silence.py + stutter-detect.py):
    • Long silence (≥0.5s): skip entirely
    • Short silence (0.25-0.5s): compress to 0.12s (preserve a sense of breathing)
    • Stutter (repeated words/phrases): detected based on the original transcription JSON, skip the first occurrence
  3. filter_complex trim+concat + crossfade:
    • Video: plain concat (avoid visual flicker)
    • Audio: chained acrossfade(20ms at each splice point, eliminating the "click" sensation)

⚠️ Key constraints

  • Cannot use select/aselect: long videos have different audio and video time bases, which causes drift
  • Must use nohup Run in the background: 14 items × about 2-3 minutes each, total time ~20-30 minutes
  • macOS zsh has no mapfile: use while IFS= read -r line instead
  • When timestamp >59:59: must use H:MM:SS format (109:30 → 1:49:30)

How to run

# 确认 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

Modify clip list

Edit batch-clip-v4.sh in the clips=() array, format is "start|end|name":

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

Phase 4: Secondary quality check (WhisperX post-check)

Why is a secondary quality check needed?

Stutter detection is based on the original transcription JSON, but the original transcription sometimes misses certain stutters (they were not transcribed).
The edited clip may still contain residual stutters, requiring WhisperX to re-transcribe the clip itself, then perform detection again.

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

Run post-check

# 激活 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 detection items

  1. Single-character repetition(AA type): 运运, 对对对 → keep the last one
  2. Phrase repetition(ngram 2-4 characters): 就是就是, 当时的当时 → keep the latter one
  3. Low-confidence clusters: 3 or more consecutive characters with score<0.05, and overlapping with the following text → marked as jump-cut residue

Valid reduplication whitelist (no false positives): Try, take a look, thank you, just now, often, grandpa, mom, etc. 100+ words

Repeated iteration per item (strictest mode)

For clips with extremely high quality requirements, use iterate-until-clean.py:

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

Each round: WhisperX transcription → detection → repair → replacement → re-transcription verification.
At most 5 rounds, usually converges in 1-2 rounds.

Time per item

WhisperX transcription of a 1-minute clip ≈ 60-90 seconds (CPU).
Post-check for all 14 items ≈ 15-20 minutes.Be sure to run in the background。


Phase 5: Final check

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

Output specifications

  • Location:workspace/clips/<编号>-<名称>.mp4
  • Naming:01-胆子够大.mp4、02-AI后背发凉.mp4
  • Encoding:H.264 CRF23 + AAC 128kbps + faststart
  • Size: 3-25MB/item (depending on duration)
  • Compression rate: on average 20-30% shorter than the original clips (silence + stutter + crossfade)

Integration with other Skills

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

Pitfall log

Problem Cause Solution
Audio-video desync select/aselect filter audio and video time bases differ Switch to trim/atrim + concat
Timestamp not recognized 109:30 format exceeds 59:59 Change to H:MM:SS(1:49:30)
macOS zsh error mapfile does not exist Use while IFS= read -r line
Chinese filename garbled nohup background + CJK path Create English symlink
exec session timeout 15-30 minute task killed by SIGTERM All batch tasks must nohup
Residual stutter Original transcription missed recognition, stutter-detect cannot catch it Phase 4 WhisperX post-check
Jump-cut "click" sound trim+concat splice point audio abrupt change Add 20ms at each splice point acrossfade
Stutter misjudged as reduplication 试试/看看/谢谢 treated as repetition VALID_REDUP whitelist filtering

Performance reference

Number of slices Total original duration Phase 3 duration Phase 4 duration
6 items ~15 min ~8 min ~10 min
14 items ~35 min ~20 min ~20 min
20 items ~50 min ~30 min ~28 min