mcpField
C
cut-video

cut-video

github.com/louisedesadeleer创意与媒体11590 次安装16 周前更新
代码仓库

关于 cut-video

Cut Video 是用于激进精简长视频录制的技能,可输出供 CapCut 使用的清理后 MP4。它结合 Whisper 转录文本与 Montreal Forced Aligner 的词级时间,并要求对照音频核实边界,尤其注意重复录制片段。

主要功能

  1. 01裁剪静音、填充词和重复内容
  2. 02使用 MFA 将文本与音频对齐
  3. 03通过重新转录核验剪辑边界
  4. 04支持选择剪辑语气
  5. 05输出清理后的 MP4

适用场景

  1. 01精简长时间录制内容
  2. 02删除面对镜头视频中的错误开头
  3. 03准备 CapCut 剪辑素材

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/louisedesadeleer/cut-video --skill cut-video

安装后在 Claude Code 中输入/cut-video确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
LICENSE
make_review.py

详细介绍

剪辑视频

激进地精简长篇录制内容:去掉静音、填充词、模棱两可的表达、薄弱的过渡、不完整的开头和重复内容。保留笑声和喜剧停顿。输出清理后的 MP4,可直接导入 CapCut 添加布局、缩放和表情包。

**这项技能比单靠 Whisper 剪辑更准确。**剪辑点由 **Montreal Forced Aligner(MFA)**的词边界驱动(精度约为 10–20ms,并将实际词间静音作为明确区间处理),而不是使用 Whisper 时间戳(误差为 ±100–300ms,且停顿会包含在词语时长内)。Whisper 仅用于生成转录文本,再由 MFA 将文本与音频对齐。结果是:剪辑更精准,不会切掉词语的起始或尾音,并且能可靠地检测静音。

⚠️ 但 MFA 时间只是草稿,并非真实基准(2026-07-02 烧录,AI is bad at jokes 的 DJI 运行结果)。MFA 只能对齐 Whisper 提供的转录文本。对于有大量重录的素材,Whisper 会将重复台词合并,导致 MFA 将一个转录实例摊到多个口述版本上——该次运行后半段测得的漂移为 1–6 秒(“the average of all working code”:MFA 标记为 217.2s,实际起始时间为 225.4s;最后一次 “what this means”:MFA 标记为 114.7s,实际时间为 130.4s)。每个保留边界在渲染前都必须通过独立窗口重新转录(第 2.5 步)来核实真实基准。如果在那段视频上跳过此步骤,后半部分的剪辑就会全部变成垃圾。

风格目标(根据 AI mogging my dad.mp4 校准):

  • 平均每 1.3–1.5 秒剪辑一次(剪辑时长中位数约为 1.1s)
  • 总时长比原始素材缩短约 40–60%
  • 在句子中间剪辑,而不仅仅是在句子之间
  • 逐词精简——删掉填充词、模棱两可的表达和薄弱的开场白

输入

  • 视频文件路径(由用户提供;否则询问)
  • 可选:语气偏好(aggressive / balanced / sentimental / documentary)——默认:aggressive

工作目录

/tmp/cut-video/<basename>/——开始时创建目录,并保留文件以便调试。


步骤 0 — 创建代理文件(最重要的提速方法)

如果源文件采用 HEVC 编码、大于 500MB,或分辨率为 4K 或更高,先转码为 1080p H.264 工作副本。后续每一步都针对代理文件运行,而非源文件。

先检查方向(ffprobe … width,height)——DJI/手机拍摄的素材通常为竖屏 9:16,硬编码 scale=1920:1080 会导致画面变形。仅横屏使用 scale=1920:1080;竖屏使用 scale=1080:1920(或不受方向影响的 scale=-2:1080 / scale=1080:-2)。

ffmpeg -y -hwaccel videotoolbox -i "$SRC" \
  -vf scale=1920:1080 \        # portrait sources: scale=1080:1920
  -c:v libx264 -preset fast -crf 20 -pix_fmt yuv420p \
  -c:a aac -b:a 192k \
  /tmp/cut-video/$NAME/proxy.mp4

关键标志:

  • 在输入端使用 -hwaccel videotoolbox——以硬件解码 HEVC,在 Apple Silicon 上速度快约 5–10 倍。不使用它,就得等几分钟而不是几秒。
  • -preset fast——libx264 默认值为 medium,在工作副本上速度慢约 3 倍,却没有实际的画质收益。

硬件编码替代方案(在 M 系列芯片上更快,但文件稍大):

-c:v h264_videotoolbox -b:v 8M

步骤 1 — 获取转录文本(whisper,作为 MFA 的输入)

MFA 是强制对齐器,不是转录器——它需要转录文本来进行对齐。Whisper 在这里唯一的任务就是生成这些文本;切分时不会使用 whisper 的词级时间戳(误差为 ±100–300ms,而且 turbo 会把停顿算进词语时长)。所有时间信息都由 MFA(步骤 1.5)提供。

ffmpeg -y -hwaccel videotoolbox -i /tmp/cut-video/$NAME/proxy.mp4 \
  -vn -ac 1 -ar 16000 /tmp/cut-video/$NAME/audio.wav
whisper /tmp/cut-video/$NAME/audio.wav \
  --model tiny.en --word_timestamps True --output_format json \
  --output_dir /tmp/cut-video/$NAME --language en

模型选择(2026-07-02 修订——“我们只需要词语”这一说法是错的):转录文本就是对齐输入,因此转录错误会变成时间错误。在 AI is bad at jokes 这次运行中,tiny.en 漏掉的词正是旧警告所预测的(“bland”、“slop”),把“Compare this to”听成了“comparative is”,还合并了整句重录内容——而 MFA 对齐这些错误文本后,后半部分的时间偏移了 1–6s。tiny.en 只用于快速初筛,以了解录制内容的结构;如果转录文本显示存在重录/重复,或者素材有噪声/在户外拍摄,则第 2.5 步的分段真实内容校验会使用 small.en,并提供实际切分时间。(使用完整的 small.en 转录来作为 MFA 输入也是不错的升级方案——处理 5 分钟视频约需 1–2 min——但它也会合并重录内容,因此仍不能省去第 2.5 步。)对于非英语内容,使用 --model base 并去掉 --language。我们保留 --word_timestamps True,只是为了留存 whisper 的时间戳以供交叉检查/备用(参见步骤 1.5 的注意事项、2a-legacy)——绝不将其作为主要切分依据。

步骤 1.5 — 使用 MFA(Montreal Forced Aligner)将转录文本与音频对齐 — 计时引擎

这是此技能的核心。**MFA 会将 whisper 转录文本强制对齐到音频,并以约 10–20 毫秒的精度返回单词边界,同时将真正的词间静音表示为明确的空区间。**所有剪辑决策(间隙、填充词、说错后重说、重录)都使用 MFA 单词时间。

**自动安装 MFA(此技能会自行完成——不要让用户手动安装)。**每次运行开始时都执行此幂等引导;一切就绪后,该步骤不会执行任何操作(只需几秒),而在全新机器上首次安装则需要约 2–3 分钟。只有在确实安装了内容时,才告诉用户“正在安装 MFA(仅需一次)……”。

# 1. Ensure conda exists (MFA's only supported install path). Install miniforge via brew if missing.
if ! command -v conda >/dev/null 2>&1; then
  echo "conda not found — installing miniforge (one-time)…"
  brew install --cask miniforge || brew install miniforge
  # make conda available in this shell
  eval "$("$(brew --prefix)/bin/conda" shell.bash hook 2>/dev/null || conda shell.bash hook)"
fi
source "$(conda info --base)/etc/profile.d/conda.sh"

# 2. Ensure the mfa env exists with MFA installed
if ! conda env list | grep -q '^mfa\b'; then
  echo "creating mfa conda env (one-time)…"
  conda create -n mfa -c conda-forge montreal-forced-aligner -y
fi

# 3. Ensure the English acoustic model + dictionary are downloaded (idempotent; skips if present)
conda run -n mfa mfa model download acoustic english_mfa  2>/dev/null || true
conda run -n mfa mfa model download dictionary english_mfa 2>/dev/null || true

如果缺少 brew,就无法自动安装 MFA——请改用 whisper+silencedetect 路径(见下方的回退说明),并告诉用户,要启用 MFA 的高精度功能,需要运行这一行命令:brew install miniforge。

每次运行:

# corpus = a dir with audio.wav + audio.txt (the whisper transcript as PLAIN TEXT, no timestamps)
mkdir -p /tmp/cut-video/$NAME/corpus
cp /tmp/cut-video/$NAME/audio.wav /tmp/cut-video/$NAME/corpus/
python3 -c "import json;d=json.load(open('/tmp/cut-video/$NAME/audio.json'));open('/tmp/cut-video/$NAME/corpus/audio.txt','w').write(d['text'])"
conda run -n mfa mfa align --clean /tmp/cut-video/$NAME/corpus english_mfa english_mfa /tmp/cut-video/$NAME/aligned
# → aligned/audio.TextGrid — "words" tier: (start, end, word); EMPTY-label intervals = true silences

解析 TextGrid 的 words 层(使用 pip praatio,或编写一个 20 行的解析器——区间层是纯文本)。MFA 的优势:

  • 真正的单词边界 → 剪辑留白可缩小到约 0.02 秒,不会切掉单词的起始或结尾。
  • 真正的静音会表示为明确的空区间 → 2a 间隙表可直接基于 TextGrid 生成;whisper 将停顿嵌入文本的问题也随之消失。
  • **注意事项:**MFA 对它收到的转录文本进行对齐。如果 whisper 将口语中的结巴重复合并了(“growth… growth marketer” → “growth marketer”),对齐会在其附近局部偏移——仍需保留 silencedetect 检查(2a)作为交叉验证,并将偏差超过 0.3 秒的部分视为可疑区域,重新检查。对于重录较多的自拍素材,偏移并非局部问题——它会累积,可能达到数秒(参见顶部警告和步骤 2.5)。如果噪声底也导致 silencedetect 失效(动态范围过低保护),你就没有任何自动交叉验证——此时步骤 2.5 是必做项,而非可选项。
  • **偏移预警——拉长的单词:**任何 MFA 或 whisper 时长约 ≥1 秒的短单词(在 2026-07-02 这次运行中,“like” 为 1.16 秒、“there's” 为 1.5 秒、“of” 为 2.4 秒、“the” 为 4.9 秒),都可能隐藏着停顿、口误或被完全合并的重录内容。预先收集所有拉长的单词;每个单词都标记一个真实内容未知的区域,必须重新选取窗口检查(步骤 2.5)。有一个拉长的 “the” 最终被发现包含整句完整、清晰的一次录制——并且最终保留的正是这段录制。
  • **回退方案:**如果 MFA/conda 不可用,且用户不想安装,就改用 whisper 单词时间戳 + 音频 silencedetect(2a)——并在计划摘要中说明这一点。只有此路径会使用 whisper 时间戳来决定剪辑位置。

步骤 2 — 制定剪辑清单(默认采用激进剪辑)

根据MFA 单词区间计算要保留的(起始,结束)时间区间列表(仅在回退时使用 whisper JSON)。激进剪辑会删除以下四类内容:

2a. 静音间隙 — 优先使用 MFA 空白区间,并以音频静音检测交叉核验(更新于 2026-06-10)

使用 MFA(步骤 1.5)时,真正的静音是 TextGrid 中标签为空的区间——直接对这些区间应用下方的间隙表。使用音频静音检测进行交叉核验——但要根据录音本身的噪声底校准阈值;切勿将 -30dB 硬编码(2026-06-16 新增:在一次使用 DJI 麦克风录制的对镜拍摄中吃了亏,其噪声底约为 -24dB:固定的 -30dB 将整段录音都判为非静音,因此每个“静音”间隙都被测为“高于阈值 = 有内容,保留”,约 8 秒的空白停顿也留在了剪辑中):

# 1. Measure the noise floor F and speech level S from per-0.5s-window RMS
#    (slice the wav into 0.5s windows, run volumedetect on each, collect mean_volume;
#     F ≈ 10th-percentile window RMS  (room tone),  S ≈ 90th-percentile  (speech))
# 2. Set the silence threshold RELATIVE to the floor, not absolute:
THRESH=$(python3 -c "print(f'{F + 6:.0f}')")        # 6 dB above the measured floor
ffmpeg -i proxy.mp4 -af silencedetect=noise=${THRESH}dB:d=0.14 -f null - 2>&1 | grep silence_

⚠️ 低动态范围保护措施(2026-06-16 的真正教训):如果 S − F < ~8dB(麦克风噪声大——户外/DJI/领夹麦克风存在空调嗡嗡声,且每秒 RMS 在说话和静音期间读数相同),则任何能量阈值都无法区分说话和静音。此时完全不要相信 silencedetect/volumedetect——所有剪辑都要依据 MFA 词语边界;如果没有 MFA,则在备用方案中依据 small.en 词语起始点 + 拉长词语停顿检测(持续时间远长于正常词语的轻声词其实藏着一个停顿:保留其起始后的约 0.10 + 0.105·len(word) 秒,然后剪到下一个词的起始处)。在计划摘要中说明使用了哪条路径。

没有 MFA 时,只有在通过动态范围检查后,silencedetect 流程才是可靠的间隙来源:mlx_whisper/whisper-turbo(以及 tiny.en)会把停顿嵌入词语时长中——一个 3 秒的“词语”实际上是“词语 [长停顿]”,词间间隙读数约为 0.00,基于间隙的剪辑毫无作用。备用方案应使用 small.en(而非 tiny.en)——tiny 会更严重地将词语时长拉伸至静音期间,还会悄悄漏掉词语(“bland”“slop”)和整段重录内容。解析 silence_start/silence_end,移除这些区间(保留约 0.04 秒的边距;MFA 精确边界可使用约 0.02 秒)。对于“几乎没有停顿”的情况 → d=0.12,边距为 0.03。结合去重(2c/2d),通过移除 silences ∪ dropped-word-ranges 进行剪辑。这就是将中位数控制在约 1.1–1.3 秒的方法。

  • **Whisper 会隐藏的结巴重复:**turbo 会将口头重复合并(“growth… growth marketer” → “growth marketer”),而且每次运行的结果都可能不同(漏掉/重新加回词语)。基于转录文本的剪辑无法发现这些问题——MFA 对齐偏移与 silencedetect 检测结果不一致时,说明该区域存在问题;请重新检查,或向用户询问时间码并剪掉精确片段。
2a-legacy. 词间静音间隙(严格阈值)
间隙aggressive(默认)balancedsentimentaldocumentary
< 0.25s保留保留保留保留
0.25–1s剪至 0.1s剪至 0.3s剪至 0.5s保留
1–3s剪至 0.2s剪至 0.8s剪至 1.5s剪至 2s
> 3s剪至 0.2s剪至 0.5s剪至 1s剪至 3s

aggressive 模式的阈值与参考视频的节奏相符(剪辑中位数为 1.13 秒)。

2b. 填充词 — 前后各留 50 毫秒

始终剪掉:um、uh、umm、uhh、er、erm、ah、mhm、hmm(单独出现时)

2c. 犹豫词与弱过渡语 — 默认模式下积极剪掉

这些词语用作填充语(而非表达实质性内容)时剪掉。标记为待剪,然后在审核步骤中结合上下文确认:

  • 犹豫词:like(用作填充语,而非表示比较)、you know、I mean、I guess、I think(表示犹豫,而非陈述观点时)、kind of、sort of、basically、literally、actually(用作填充语时)
  • 弱过渡语:so(句首)、and so、and then、but um、but uh、okay so、right so、well
  • 冗余限定语:kind of like、sort of like、or whatever、or something
  • 重新调整话题的短语:anyway、so anyway、the thing is、what I'm saying is、to be honest、honestly

在 balanced / sentimental / documentary 模式下,只剪掉出现在重复表述之前的犹豫词(错误起句,见 2d)。在对话性较强的片段中予以保留。

2d. 错误起头和重复——大幅删减

通过以下方式检测:

  • **自我纠正:**说话者在约 3 秒内重新说出相同的开头。模式示例:"I want — I wanted to..."、"It's a — it's an interesting..."。删掉较早的尝试及中间的停顿。
  • 重启式铺垫:"so basically what I'm saying is..."、"the point is..."、"let me start over..."。删掉铺垫,保留实际要点。
  • **逐字重复:**说话者连续两次说出相同的 3 个或更多单词。保留较流畅的一遍。

实现提示:对连续词语 n 元组建立滑动窗口模糊匹配。在 3 秒窗口内,若 3 元组相似度 > 0.8,则标记较早的一处以便删除。

2d-bis. 整句重录(自拍对镜录制,新增于 2026-06-10)

自拍式开场白/宣传片中会出现整句重录,间隔 5–60 秒(“The next person is Carmen who works. … The next person, the next guest I invited is Carmen who's social and content lead at Slate”)——远超 2d 的 3 秒窗口。检测并处理这些重录:

  • 检测:在60 秒窗口内进行模糊 n 元组匹配(4 元组,相似度 > 0.7);也要匹配句子开头(前 3–5 个词),因为重录通常会重新起句。
  • 默认保留最后一遍——说话者会反复录制,直到满意为止。例外:如果最后一遍不完整或说错,而较早的一遍完整无误,则保留完整无误的一遍,并在计划摘要中说明这一调整。
  • 重录边界前通常会出现长停顿、吸气声、点击声或提示语(“okay”“again”“take two”)——停顿与开头匹配的组合是最强信号。
  • 在第 3 步计划中列出每组重录(line → takes at [t1, t2, t3] → keeping t3),以便用户指定保留哪一遍。
2e. 绝不可删减的内容——明确保护
  • **笑声。**Whisper 会将笑声转录为静音。在应用任何静音删减之前,先检查音频振幅:
    ffmpeg -i audio.wav -af "volumedetect" -f null - 2>&1 | grep mean_volume
    
    将窗口的 RMS 与测得的噪声底 F(2a)比较,而不是与固定 dB 值比较:如果窗口 RMS > F + ~10dB,则其中有可听见的内容(笑声、呼吸声、反应声)——应予以保留。对噪声较大的麦克风使用硬编码的 -30dB/-25dB 阈值会出错,因为其噪声底本身可能已高于该值——这会把纯室内底噪“保护”成笑声,而这正是无声片段得以保留的原因。使用 ffmpeg -i audio.wav -af "astats=metadata=1:reset=1,ametadata=print:key=lavfi.astats.Overall.RMS_level" 获取每个窗口的 RMS。(还要记住 2a 中关于低动态范围的注意事项:当 S − F < ~8dB 时,RMS 同样无法区分笑声和室内底噪——应改用 MFA/词语起始时间,并在计划中标出所有模糊之处,供用户判断。)
  • **喜剧停顿。**包袱之后有意留出的停顿。在 aggressive 模式下,也应将其缩短至 0.4–0.6 秒(不要删掉,只需压紧)。
  • **反应声。**叹气、“ooh”、“wow”、倒吸气声——都属于内容。
  • **单词式包袱。**如果一句话在停顿后以一个词的妙语收尾,应保留这段停顿。
2f. 严格检查——绝不容忍重复(Louise,2026-06-10:“清除所有重复内容”)

生成保留片段列表后,运行机器扫描——不要只靠眼睛检查转录:

  1. 扫描保留范围内的 MFA 单词,查找:(a) 连续重复的单词;(b) 跨片段边界重复的双词组合(口误重说可能跨越剪切点——曾有一次,“……meet her. And we're [口误] / And we're going to……”就这样漏了过去);(c) 相邻句子重复的句首(“So… So…” → 剪掉第二个)。
  2. 剪掉填充性口误,即使出现在统计数据中也要剪:“I believe”、句首的“Like”、“I guess”。保留修辞性重复(平行结构)——这是有意为之。
  3. 删除单词时,剪到 MFA 单词的完整结束位置(再加 0.01 秒)——残留 15 毫秒的元音听起来仍像这个单词。
  4. MFA OOV 陷阱(2026-06-10 的惨痛教训):MFA 词典中缺失的首字母缩略词/名称(“AI”、品牌名)会在对齐中悄然消失——该单词会被并入相邻单词;随后若将保留片段的结束位置对齐到“最后一个 MFA 单词”,就会截掉实际说出的单词。防护措施:如果 whisper 分段的结束时间比最后一个 MFA 单词的结束时间晚 0.2 秒以上,则对该边界采用较晚的时间(whisper 结束时间或下一处 silencedetect 起始时间);并使用单独片段转录核验该衔接处。
  5. 核验陷阱:对输出内容进行 whisper 转录会两次误导你。它会:(a) 合并保留下来的结巴重复(真实的“and we're… and we're”只转录一次——看起来干净,其实不然);(b) 在剪切点衔接处幻觉出上下文单词(“girls”+“Enjoy”的衔接会被转录成“So enjoy”——看起来有问题,其实没有)。因此要用两种方式核验:对保留范围进行 MFA 重复扫描(捕获真实的结巴重复),并对任何可疑的衔接处进行逐段单独转录(排除幻觉)。只有重复扫描结果为无重复时才能交付。
  6. **⭐ 跨 ASR 核验——最强的结巴重复检测器(2026-06-10 发现)。**MFA 重复扫描只能看到 WHISPER 转录出的单词——如果 whisper 合并了某段重说内容(“with the most recent video… with the most recent video” → 只转录一次),MFA 就无从得知,扫描也会让有问题的剪辑通过。解决办法:**使用第二个独立 ASR 转录剪辑并进行对比。**Tella 上传后的转录很适合此用途(采用不同模型 → 盲点不同):它一次就抓出了 5 处重复,而 whisper+MFA 流程却判定内容干净(一个重复短语、一个残留的“or, or”、两个词中片段“pers-”/“per-”、一个重复的“into”)。工作流程:将剪辑上传到 Tella(或任何第二个 ASR)→ 扫描其转录,查找重复单词/双词组合/短语和词中片段(“xyz-”)→ 将命中项映射回源时间点→剪切→重新核验。两个 ASR 结果不一致的部分,恰好标出需要修复或提醒用户注意的区域。

第 2.5 步 — 用独立窗口核实每个保留边界(新增于 2026-07-02 — 这一步挽救了整个流程)

MFA 能提供片段结构(说了什么、大致位置),但对于重录较多或有噪声的素材,它无法可靠地给出剪辑时间点。渲染前,使用 small.en 在独立的 ±几秒窗口中重新转录计划保留的每个片段——在较短的窗口内,whisper 的词语时间点准确,而且能听到完整转录遗漏的词(“bland”“slop”),也能识别完整转录合并的重录内容(“It turns out— it turns out…”)。

# windows: name:start:duration — start ≥0.5s BEFORE the expected onset
for n in "r1:5.5:8.4" "r2:17.0:6.0" "r3:30.5:6.0"; do
  f=${n%%:*}; rest=${n#*:}; o=${rest%%:*}; t=${rest##*:}
  ffmpeg -y -hide_banner -loglevel error -ss $o -t $t -i audio.wav gt/$f.wav
  whisper gt/$f.wav --model small.en --word_timestamps True --output_format json --output_dir gt --language en >/dev/null 2>&1
  python3 -c "
import json
d=json.load(open('gt/$f.json'))
print('=== $f (+$o) ===')
for s in d['segments']:
    for w in s.get('words',[]):
        print(f\"{$o+w['start']:7.2f}-{$o+w['end']:7.2f} {w['word']}\")"
done

(此循环兼容 zsh——不要使用 set -- $var,zsh 不会对未加引号的变量进行分词。)

读取这些窗口时的规则:

  • 窗口边缘处的词会被拉长,不可信。 如果第一个词恰好从窗口起点开始,或最后一个词在窗口终点结束,说明它被截断了——真实起止点在窗口之外。使用该时间前,先将边界向外移动约 1 秒,重新截取窗口。
  • 窗口内部被拉长的词也可能藏着未听到的音频——small.en 和完整转录一样,会合并窗口内的重复内容(跨越 93.1–97.9 的 “to” 中,包含了一整段被放弃的重录)。任何持续时间 ≳1 秒的词,都要通过更窄的窗口重新核查。有时其中其实有一段完整、干净的重录,完整转录的 ASR 都没有识别出来——用零散片段拼接前先检查。
  • 两个窗口对相同的词可能有不同结果(一个窗口捕捉到了口误,另一个则把它截断了)。请调整第三个窗口的位置,让有争议的部分位于窗口中间,远离两端,以此解决分歧。
  • 最终保留时间:起点提前 −0.03 秒,终点延后 +0.04–0.08 秒(要精确——20 个保留片段各自累加 +0.10 秒的余量,会增加 2 秒的杂音)。
  • 也要剪掉保留片段内部的停顿(新增于 2026-07-02 — “更少静音”)。 仅移除重录和片段间隙还不够:流畅的录制也会在句中停顿(“……谋生,[0.3s] 我想知道……”),累积起来也会很长。扫描每个保留片段中经核实的词语,找出词间间隔 > 0.2 秒的地方,并将每处缩短至约 0.1 秒(剪掉 [gap_start+0.05, gap_end−0.05])。刻意设计的修辞停顿(“中位数……还是平均数”)不要缩短到约 0.15 秒以下。
  • 保留片段中的拉长词陷阱(2026-07-02 踩过的坑): 在 ASR 中,拉长的口语词看起来与“词语 + 后续停顿”完全相同(“slooop” 被转录为 slop 285.08–286.32)。剪掉看似停顿的后半部分,可能会把词剪断在元音中间。若要在保留边界处剪入任何拉长的词,先用更窄的窗口重新检查,找出声音实际停止的位置——如果渲染后的核验转录中缺少原先存在的词,就说明剪辑把它删掉了:将边界往外移。

一段 5 分钟视频的成本:约 10–15 个窗口,每个窗口运行 small.en 几秒,合计约 2 分钟。这就是干净剪辑与返工整段后半部分之间的区别。

步骤 3 — 展示计划,立即渲染

不要等待批准——在同一轮中打印摘要并开始渲染(2026-07-09 更改:Louise 没注意到问题,审批关卡让她白白等待;渲染不会造成破坏,重新渲染成本也低,因此先渲染显然更好)。打印此摘要,然后直接进入步骤 4:

Original duration: 7m 25s
Cleaned duration: 3m 58s
Cut: 3m 27s (47%)
Total keep intervals: 312
Median cut duration: 1.2s (target: 1.1–1.5s)
Hard cuts (dead air > 3s removed): 23
Filler hits: 47
Hedge/transition cuts: 89
False-start drops: 12
Notable preserved long pauses: list timestamps + context (laughs, punchlines)

对照基准进行自检:

  • 如果中位剪辑时长 > 2s → 不够激进。收紧间隔阈值或扩充犹豫词列表,然后重试。
  • 如果中位剪辑时长 < 0.7s → 剪辑过度。可能破坏了呼吸或节奏。将 0.25–1s 阈值放宽到 0.3s。
  • 压缩目标:激进模式下为 40–60%。
  • **例外 — 重拍较多的素材(2026-07-02):**如果大部分素材时长都来自废弃的重拍片段,压缩主要通过删除整句实现,而非逐词切分——中位保留时长会达到 3–5s,压缩率为 65–75%,这是正确的。应改为根据压缩率和最终脚本的整洁度进行判断;不要为了达到 1.1–1.5s 的中位时长目标,就强行在流畅且保留的片段中间插入剪辑(该目标适用于填充词密集、没有重录的独白)。

自行运行自检,并在渲染前修正违规项——现在的质量关卡由你把关,而不是用户。然后立即渲染。用户会审核结果;如果某处剪掉了笑声或刻意停顿,就调整保留列表并重新渲染(很快——只需剪辑并拼接,无须重新转录)。

步骤 3.5 — 手动时间线编辑器(可选——仅在 Louise 要求时使用)

(自 2026-07-02 起不再默认启用:Louise 发现实际使用时编辑器操作繁琐,更希望流程自动剪得更紧凑——步骤 2.5 中的片段内停顿修剪正是因此而来。仅在她要求手动审查或调整时才生成编辑器。)

提供一个时间线编辑器,让她可以在渲染前(或渲染后)修剪静音并删除不需要的内容:

# keeps.json must be [start, end, "transcript text"] triplets in the working dir;
# needs proxy.mp4 (and ideally audio.wav) in the same dir. Generates wave.png once.
python3 ~/.claude/skills/cut-video/make_review.py /tmp/cut-video/$NAME
open /tmp/cut-video/$NAME/review.html

此页面提供的功能(独立的 HTML 文件,放在 proxy.mp4 旁边,无需服务器——波形峰值和静音建议都嵌入文件中,因此可通过 file:// 使用):

  • 显示完整源素材的画布波形时间线,在任何缩放级别都清晰锐利。双指捏合或按住 ctrl/⌘ 滚动滚轮,可围绕光标缩放(连续缩放,范围为适应视图至 400px/s);鼠标滚轮用于平移;也可使用 +/−/适应视图按钮。点击可拖动播放位置;播放头与视频同步。
  • 在波形上拖动以删除该范围——这是主要的静音移除操作:看到平坦区段后,划过它即可删除。跨区块边界也有效(修剪、拆分或删除覆盖的内容)。
  • 琥珀色斜线带 = 保留区块内自动建议的静音(基于峰值,采用自适应阈值——由生成器计算并嵌入)。点击色带即可剪除(两侧各保留 0.1s 停顿),也可点击**“✂ 全部剪除”**,用最短时长滑块一次处理所有建议。在嘈杂的麦克风录音中,建议效果会变差——这些只是可视化候选项,由她自行判断。
  • 拖动区块边缘进行修剪;拖动时视频会实时跳转,方便她听取剪切点。选中边缘后,可按 ±0.05s 微调(使用按钮或 ←/→,按住 shift 则为 0.25s)。
  • 在播放头位置拆分(S),丢弃/恢复区块(D/⌫,或点击对应卡片),双击间隙可恢复已剪掉的片段,撤销(Z,最多 60 步)。
  • 下方的卡片列表包含每个片段的 ▶ 播放按钮和转录文本;点击**“预览最终剪辑”**即可连续播放保留区块并跳过已剪内容——无需渲染即可实时预览最终效果。
  • **“为 Claude 复制编辑决定”**会复制 {"keeps": [[a,b], ...]}——以源代理素材秒数表示的完整编辑后保留列表。

应用她的编辑决定:粘贴的 keeps 数组会替换旧保留列表——按时间重叠情况,将转录文本从先前的 keeps.json 转移过来(她从间隙恢复的区块没有文本;将其标记为“(已恢复)”),然后重建 filter.txt 并重新渲染。代理素材已就绪,因此修改只需几秒钟。

边界处理:她手动拖动的边缘是有意设置的——不要再将其吸附到 MFA/ASR 词边界。只有当某个边缘落在实际词语中间时才发出警告,并指出具体词语及最近的干净边界。

步骤 4 — 使用 trim + concat 渲染,不要使用 select

**重要:**使用 trim+concat 滤镜图模式,不要使用 select/aselect。select 滤镜会压缩视频帧,但不会正确压缩音频 PTS,导致音画不同步。

为每个保留区间构建一组 trim+atrim,然后将它们 concat:

[0:v]trim=A1:B1,setpts=PTS-STARTPTS[v0];
[0:a]atrim=A1:B1,asetpts=PTS-STARTPTS[a0];
[0:v]trim=A2:B2,setpts=PTS-STARTPTS[v1];
[0:a]atrim=A2:B2,asetpts=PTS-STARTPTS[a1];
...
[v0][a0][v1][a1]...concat=n=N:v=1:a=1[outv][outa]

将滤镜图写入文件(文件会很长——积极剪辑时,7 分钟的源素材可能有 200–400 个区间),并使用 -filter_complex_script。渲染命令:

ffmpeg -y -hwaccel videotoolbox -i /tmp/cut-video/$NAME/proxy.mp4 \
  -filter_complex_script /tmp/cut-video/$NAME/filter.txt \
  -map "[outv]" -map "[outa]" \
  -c:v libx264 -preset fast -crf 20 -pix_fmt yuv420p \
  -c:a aac -b:a 192k \
  /tmp/cut-video/$NAME/cleaned.mp4

**大量剪切时的更快替代方案:**如果滤镜图超过约 500 个 trim,则改为逐段提取并无损拼接:

  1. 将每个保留区间提取为单独的流复制片段
  2. 构建 concat demuxer 文件
  3. ffmpeg -f concat -safe 0 -i concat.txt -c copy cleaned.mp4

这样在修剪阶段就完全无需重新编码。

第 5 步 — 交付

  • 将最终文件保存到 <source_dir>/cut_out/<source_name>_cut.mp4(如目录不存在则创建)
  • 打印一行:原始时长 → 清理后时长、剪掉的百分比、剪辑中位时长、输出路径
  • open 该文件,让用户立即查看
  • 修改成本很低:代理文件和经过实测校准的时间点都保留在工作目录中,因此“删掉第二段” / “收紧 X”只需几秒即可重新渲染。(如果她要求使用 Step 3.5 编辑器,提醒她该编辑器仍可使用,方便将决策粘贴回去。)
  • 提供继续调整:重新调整间隔阈值、切换语气预设、标记要保留或剪掉的特定时刻

常见问题 — 切勿重犯(根据以往运行经验总结)

  • 代理处理步骤不要跳过 -hwaccel videotoolbox。 对 7 分钟的 4K 源素材进行 HEVC 软件解码可能需要 5 分钟以上。使用该标记后约需 30 秒。
  • 不要使用 select 滤镜剪辑。 它会导致音频错位。请使用 trim+concat。
  • 不要使用 -preset medium(libx264 的默认值)。 在工作副本上,它比 -preset fast 慢约 3 倍,画质却没有提升。
  • 不要等用户批准后再渲染(2026-07-09 已修改——这一关卡浪费的时间比节省的还多)。打印计划后立即渲染。之前担心的情况(“无声”间隔里藏着笑声,或“填充语”其实是有意强调)由下方的振幅检查和摘要中的保留停顿列表处理——剪错了也只需快速重新渲染。
  • 不检查振幅就不要剪掉较长的“静音”。 那通常是笑声、思考时的停顿,或铺垫与包袱之间的节拍。
  • 有代理文件时,不要对整个原始源素材运行 whisper。 使用从代理文件中提取的 audio.wav 运行 whisper。
  • 不要对音频重新编码两次。 如果只改了视频,请使用 -c:a copy,跳过不必要的 AAC 编码步骤。
  • 不要畏手畏脚。 默认使用 aggressive。参考剪辑风格节奏很快——如果输出感觉“保守”,就不符合 Louise 的 CapCut 节奏。
  • 对于重拍较多或噪声较大的素材,不要只依据 MFA 时间点渲染。 2026-07-02 的 DJI 运行实测出现了 1–6 秒的偏差。先用独立窗口(Step 2.5)校准每个保留片段的边界。
  • 不要将纵向源素材缩放到 1920:1080。 先检查方向;DJI 和手机素材通常是 9:16。
  • 不要信任独立转录中靠近窗口边缘的词语时间点。 碰到窗口边界的词会被截断或拉伸;用作剪辑点之前应重新划分窗口。
  • h264_videotoolbox -b:v 8M 也适用于最终渲染,不只是代理文件——在 M 系列设备上,整个 4K-HEVC→代理文件→18 段渲染总共约 1 分钟即可完成。

基准参考

AI mogging my dad.mp4(2026-05-03,CapCut 项目 0501):

  • 460 秒内剪切 335 次 = 每 1.37 秒剪切 1 次
  • 中位数 1.13 秒,平均值 1.37 秒
  • 41% 的剪辑时长 < 1 秒,12% < 0.5 秒
  • 只有 3 次剪辑时长 > 5 秒
  • 原始素材压缩约 60%

以此作为调校“aggressive”模式的基准真值。

本技能明确不做的事

  • 添加缩放、布局或动态图形(属于独立任务——在 CapCut 中处理,或通过 [[tella-edit]] 处理)
  • 添加梗图或 B-roll(由用户挑选;短视频剪辑请参见 [[clipify]])
  • 烧录字幕(清理后单独处理)
  • 上传到任何地方

让本技能专注于一件事:快速、激进地将长篇录制内容剪辑成更紧凑的 MP4。

评论

支持 Markdown