关于 clipify
clipify 是面向视频剪辑的 Skill,可从对话或动作视频中筛选片段并生成独立剪辑。它结合语音转写或冲击声检测,并支持竖屏重构、变速和字幕。
主要功能
- 01对话与动作模式选段
- 02检测冲击声定位动作
- 03抽帧核验候选画面
- 04生成竖屏平移或分屏
- 05变速压缩揭示镜头
- 06制作逐词字幕
适用场景
- 01从播客中挑选笑点片段
- 02按击打声制作动作蒙太奇
- 03将横屏访谈改为竖屏
- 04为对话剪辑添加逐词字幕
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ louisedesadeleer/ clipify --skill clipify安装后在 Claude Code 中输入/clipify确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEREADME.md.gitignorescripts/build_ass.pydetect_transients.pyaudio_align.pybuild_pan.pyanalyze.py详细介绍
Clipify
在视频中寻找精彩片段,将其剪成独立短片;可选择将 16:9 转为 9:16(人脸跟随或分屏)、调整镜头摇移速度以更紧凑地呈现内容,并逐词烧录 opus 风格字幕。
模式
在步骤 1 之前选择一种模式。所选模式会改变你寻找片段的方式,以及是否添加字幕。
- 对话模式(播客、访谈、对镜讲话视频的默认模式):使用 Whisper 转录,然后寻找包袱、反应或尴尬的停顿。→ 步骤 1A。在步骤 5 添加字幕。
- 动作模式(劈柴、体育、打击乐、打鼓,以及任何伴有重复撞击声的内容):完全跳过 Whisper,运行
detect_transients.py找到每次击打,并据此剪辑。→ 步骤 1B。跳过步骤 5;击打声本身就是节奏。 - 混合模式:对话部分使用对话模式,其余部分使用动作模式,并交替剪辑。使用 1A 和 1B。
如果用户说“我在这里没有说话”,则使用动作模式。如果用户说“每次 X 时都剪一下”(劈砍、击打、出拳、鼓点、挥动、脚步),则使用动作模式。其他情况默认使用对话模式。
输入
- 视频文件路径(用户会提供;否则请询问)
- 可选:所需格式(9:16、16:9、1:1)——如果用户未指定,在选定候选片段后询问
- 可选:字幕样式偏好——如果用户未指定,在添加字幕前询问
工具(仅使用最快的方式)
- Whisper:
whisper --model tiny.en --word_timestamps True --output_format json(速度约为small.en的 10 倍;英语质量足够)。非英语内容使用--model base(省略--language)。 - ffmpeg: 解码时添加
-hwaccel videotoolbox,渲染时添加-preset ultrafast。最终母版使用-c:v libx264 -crf 20。 - 音频对齐使用 Numpy(FFT 互相关)。无需 scipy/cv2。
- 脚本:
<skill-dir>/scripts/(其中<skill-dir>是包含此 SKILL.md 的目录,通常为~/.claude/skills/clipify/)analyze.py— 根据两个 ROI 运动文件生成说话人时间线build_pan.py— 使用硬切生成 ffmpeg 裁剪 x 表达式build_ass.py— 根据 Whisper JSON 生成 opus 风格 ASS 字幕audio_align.py— 查找子片段在较长源视频中的偏移detect_transients.py— 查找尖锐撞击声(动作模式);参见--help
工作目录:/tmp/clipify/(开始时创建,并保留产物以供调试)。
工作流
步骤 1A — 寻找对话片段(对话模式)
mkdir -p /tmp/clipify
ffmpeg -y -hwaccel videotoolbox -i "$VIDEO" -vn -ac 1 -ar 16000 /tmp/clipify/audio.wav
whisper /tmp/clipify/audio.wav --model tiny.en --word_timestamps True --output_format json --output_dir /tmp/clipify --language en
读取生成的 JSON(或 .txt),并选出 3–5 个候选片段。可留意以下有趣信号:
- 包袱和反应: “什么”“等等”“不可能”等词语、笑声、“哈哈”、脏话
- 反转时刻: 设问 → 意外回答
- 尴尬的停顿: Whisper 片段之间间隔较长,或出现填充词(“呃”“嗯”)
- 自嘲 / 值得引用的妙语: 简短、独立成句的陈述
- 音频峰值: 使用
ffmpeg -af volumedetect检测,或查找快速的一来一回(Whisper 片段交替且较短)
针对每个候选片段,提出:[开始时间, 结束时间, 有趣之处, 建议标题]。片段时长以 10–25 秒为宜。展示列表,并让用户确认或挑选。
步骤 1B — 找到每次击打(动作模式)
用音频瞬态检测替代 Whisper。检测器通过计算 1–6 kHz 频段的频谱通量,找到尖锐的撞击声(斧头劈木头、拳头击打垫子、鼓面、球棒击球、木头落在一堆木头上)。撞击会产生宽带脉冲,而环境噪声和风声不会。
ffmpeg -y -hwaccel videotoolbox -i "$VIDEO" -vn -ac 1 -ar 16000 /tmp/clipify/audio.wav
python3 <skill-dir>/scripts/detect_transients.py /tmp/clipify/audio.wav \
--min-flux 30 --min-gap 0.6 > /tmp/clipify/strikes.json
调参:
--min-flux 30是合理的默认值。真实撞击的数值为 50–300;环境噪声和风声为 5–15。误报太多 → 提高到 40–50。检测到的击打太少 → 降至 20。--min-gap 0.6(秒)表示两次击打之间的最短间隔。快速击鼓可能需要 0.2;劈砍用 0.6–1.0 即可。--band 1000:6000覆盖木头、金属和玻璃的撞击声。沉重的闷响(底鼓、身体击打)频率更低(--band 200:2000);哨声、叮当声和小军鼓爆裂声频率更高(--band 4000:10000)。- 对于多片段素材(一个装有手机视频的文件夹),在每个片段上运行检测器,并从不同角度挑选多样化的片段。制作时长 45–60 秒、每个镜头约 0.9 秒的蒙太奇,目标是选取 50–65 次击打。
每个“镜头”应从击打前约 0.4 秒开始(展示蓄力动作),并在击打后约 0.5 秒结束(包含击打和短暂的后续动作)。每个节拍约为 0.9 秒。
步骤 1.5 — 确认每个候选片段都在画面内
渲染前务必执行此步骤,无论使用哪种模式。音频检测找到的是事件的声音;摄像机可能并未拍到该事件。跳过此步骤是导致用户反馈“你在我都没入镜的地方切入了劈砍镜头”的头号原因。
# Sample one frame at each candidate strike time T:
for T in $STRIKE_TIMES; do
ffmpeg -y -ss "$T" -i "$VIDEO" -frames:v 1 -vf scale=320:-1 \
/tmp/clipify/verify_${T}.jpg
done
查看每张验证图像,如果出现以下情况,就移除该候选片段:
- 主体弯腰、出框或被障碍物挡住
- 拇指遮住了镜头(没错,这种情况确实会发生——而且只有通过视觉检查才能发现)
- 明显是其他声源(关门声、工具掉落声)触发了检测器
对于约 30 个候选片段,每个片段只需快速调用一次 ffmpeg,再批量查看一次图像。这点额外工作很值得。
步骤 2 — 修剪选中的片段
ffmpeg -y -ss "$START" -t "$DURATION" -i "$VIDEO" -c copy /tmp/clipify/clip_$N.mp4
(使用 -c copy 可即时修剪。只有在剪切必须精确到帧时才重新编码。)
步骤 3 — 确定输出格式
询问用户(如果用户已指定,则跳过):“9:16(TikTok / Reels)、16:9(YouTube),还是 1:1(Insta 动态)?”
步骤 4 — 如果要从 16:9 转为 9:16:在两张脸之间平移,还是分屏
使用 ffprobe 检测源视频宽高比。如果源视频是 16:9,目标是 9:16,则询问:
“有两种方案:(a) 硬切平移,跟随正在说话的人(一次只显示一张脸);或者 (b) 分屏,上下排列并同时显示两张脸。你想选哪种?”
如果画面中只有一张脸(单人说话片段),则跳过询问。对于单人说话片段,直接居中裁剪。
步骤 4a — 在两张脸之间平移(推荐用于快速剪切的对话式出镜视频)
-
定位两张脸的 ROI。 抽取一帧:
ffmpeg -ss <middle> -i <clip> -frames:v 1 /tmp/clipify/probe.jpg。查看图像。目测每张脸的嘴部和下巴区域,并以源视频像素空间中的x,y,w,h表示。(无需 cv2——片段内摄像机固定;一帧就足够。)通过绘制方框进行验证:ffmpeg -i probe.jpg -vf "drawbox=x=$LX:y=$LY:w=$LW:h=$LH:[email protected]:t=4,drawbox=x=$RX:y=$RY:w=$RW:h=$RH:[email protected]:t=4" verify.jpg最多迭代两次。方框应覆盖嘴部和下巴,并避开手和麦克风。不要过度调整——帧差分对误差有容忍度。
-
提取每帧在各 ROI 中的运动能量:
ffmpeg -y -i clip.mp4 -filter_complex " [0:v]split=2[a][b]; [a]crop=$LW:$LH:$LX:$LY,format=gray,tblend=all_mode=difference,signalstats,metadata=mode=print:key=lavfi.signalstats.YAVG:file=/tmp/clipify/L.txt[la]; [b]crop=$RW:$RH:$RX:$RY,format=gray,tblend=all_mode=difference,signalstats,metadata=mode=print:key=lavfi.signalstats.YAVG:file=/tmp/clipify/R.txt[ra] " -map "[la]" -f null - -map "[ra]" -f null - -
构建说话者时间线(最短停留时间为 1.0 秒——较短的插话会合并到前一位说话者的片段中):
python3 <skill-dir>/scripts/analyze.py /tmp/clipify/L.txt /tmp/clipify/R.txt 1.0 > /tmp/clipify/segments.json -
为源视频中的 9:16 竖向条带选择平移 x 坐标。 源视频宽度 W=1920,目标宽度 W=1080 时,裁剪条带宽度 = 608。
- LEFT_X =
face_left_center_x - 304(限制为 ≥ 0) - RIGHT_X =
face_right_center_x - 304(限制为 ≤ source_W - 608)
- LEFT_X =
-
生成硬切 x 表达式并渲染:
EXPR=$(python3 <skill-dir>/scripts/build_pan.py /tmp/clipify/segments.json $LEFT_X $RIGHT_X) ffmpeg -y -hwaccel videotoolbox -i clip.mp4 -filter_complex \ "[0:v]crop=608:1080:x='$EXPR':y=0,scale=1080:1920:flags=lanczos[v]" \ -map "[v]" -map 0:a -c:v libx264 -preset fast -crf 20 -pix_fmt yuv420p \ -c:a aac -b:a 192k /tmp/clipify/clip_panned.mp4假设源视频为 1920×1080;如果源视频为 4K,请先缩小尺寸,或将所有坐标加倍。
Step 4b — 分屏(始终显示双方)
两个上下堆叠的画面,各为 1080×960。当前发言者的画面置顶——发言者切换时,叠加层也随之翻转。
[0:v]split=2[a0][a1];
[a0]crop=Wcrop:Hcrop:LX_tile:LY_tile,scale=1080:960,split=2[lt0][lt1];
[a1]crop=Wcrop:Hcrop:RX_tile:RY_tile,scale=1080:960,split=2[rt0][rt1];
[lt0][rt0]vstack[layoutL];
[rt1][lt1]vstack[layoutR];
[layoutL][layoutR]overlay=0:0:enable='<RIGHT_SPEAKER_ENABLE>'[v]
根据 segments.json,将右侧发言者的片段组合成 between(t,a,b)+between(t,a,b)+...,以构建 <RIGHT_SPEAKER_ENABLE>。画面裁剪应以每张脸为中心,目标尺寸约为 720×640(1.125:1,与 1080×960 相匹配)。
Step 4c — 时间变速揭示(可选)
如果用户想缩短缓慢的风景/摇摄/揭示镜头(“把这段剪掉一半”),但又不想丢失叙事脉络,就使用变速坡道,而不是裁剪。裁剪会迫使你舍弃部分过程;变速坡道则能保留所有节拍,只是节奏更快。
# 2x speed: 12s arc → 6s output, audio pitch preserved via atempo
ffmpeg -y -ss "$START" -i "$CLIP" -t "$ORIG_DUR" \
-vf "setpts=PTS/2,scale=1080:1920:flags=lanczos,fps=30" \
-af "atempo=2.0" \
-c:v libx264 -preset fast -crf 21 -pix_fmt yuv420p \
-c:a aac -b:a 128k /tmp/clipify/clip_2x.mp4
经验法则:
- 1.5x — 让人物动作显得“稍微利落”但又不至于像加速播放
- 2x — 更有冲击力的揭示;看起来仍然自然
- 3x–4x — 延时摄影的感觉(4x 可串联
atempo=2.0,atempo=2.0;每个滤镜实例中的atempo只接受 0.5–2.0) - 如果音频只是环境声/风声,而且变调会让人分心,就移除
-af atempo并将该片段静音
当剪辑的其余部分富有节奏感(快速切换蒙太奇、对话来回交替),而揭示镜头原本会显得拖沓时,这种方式很有用。
Step 5 — 添加字幕
动作模式下跳过此步骤——打击声就是节奏,字幕只会让画面显得杂乱。
只询问一次(仅当用户尚未指定风格时):
“三种字幕风格:opus(大号粗体白字,当前词以黄色高亮)、karaoke(每组 4 个词,绿色高亮)、minimal(简洁的 Helvetica,不高亮)。或者粘贴你喜欢的示例。”
如果用户粘贴了参考图/示例:尽可能贴合其字体、字号、字重、颜色、位置和动画——手动编写自定义 ASS,或扩展 build_ass.py。
否则使用预设:
# Re-run whisper on the trimmed clip for accurate timestamps relative to clip start
whisper /tmp/clipify/clip_panned.mp4 --model tiny.en --word_timestamps True --output_format json --output_dir /tmp/clipify --language en
python3 <skill-dir>/scripts/build_ass.py /tmp/clipify/clip_panned.json /tmp/clipify/captions.ass opus
烧录字幕:
ffmpeg -y -i /tmp/clipify/clip_panned.mp4 -vf "subtitles=/tmp/clipify/captions.ass" \
-c:v libx264 -preset fast -crf 20 -c:a copy "$OUTPUT.mp4"
Step 6 — 交付
- 将每个输出保存到
<source_dir>/clipify_out/(如果目录不存在则创建) - 每个片段打印一行:名称、时长、趣味之处、输出路径
- 使用
open <path>打开第一个输出,让用户检查 - 提供继续调整的选项(更换风格、调整 ROI、切换为分屏、重新调整字幕时序)
注意事项(以往运行的教训——不要重蹈覆辙)
- 检测到音频 ≠ 画面中出现事件。 渲染前务必执行步骤 1.5(核验每个候选帧)。检测器找出的是砍击的声音,而不是砍击者是否在画面中。主体弯腰、在镜头外,或拇指挡住镜头时,音频检测器仍会触发。渲染前先筛掉这些情况。
- 频谱通量频段很重要。 默认的
--band 1000:6000覆盖木头、金属和玻璃的撞击声。沉重的低音闷响(底鼓、身体撞击)需要--band 200:2000。口哨声、碰杯声、军鼓爆裂声需要--band 4000:10000。如果--min-flux 30没有结果,先尝试降到 15;如果结果有数千条,先换个频段,再考虑提高阈值。 - 变速音频要成对处理。
atempo单个滤镜只接受 0.5–2.0;要实现 4 倍速,请串联atempo=2.0,atempo=2.0。如果不使用atempo,仅用setpts会让音频听起来像花栗鼠。 - 不要过度调整 ROI。 最多迭代两次。运动差分容错度较高——覆盖嘴部和下巴的较宽 ROI 即使没有精准对准嘴部也能正常工作。
- 注意片段内部的场景切换。 运行
ffmpeg -filter:v "select='gt(scene,0.3)',showinfo" -f null -统计切换次数。如果一个 16:9→9:16 的片段有许多次切换,固定的人脸 ROI 只对占主导的场景有效;请提醒用户,并提出可以选择单镜头片段,或接受切换期间画面偏离中心。 - 源分辨率很重要。 如果源视频是 4K,可以先缩小到 1920×1080(速度更快,适合输出 9:16),或者将所有 ROI/平移坐标乘以 2。
- 源视频中烧录了字幕。 有些“原始”片段仍带有字幕。如果是这样,请通过音频互相关(
audio_align.py)找到无字幕母版,并从那里开始剪辑。 - 如果短片段就够用,不要对完整的长片源运行 whisper。 步骤 2 之后再对剪辑后的片段运行 whisper;只有在步骤 1 中需要转录稿来寻找有趣时刻时,才对完整源视频运行 whisper。
- 用一句话说明计划,然后立即执行。 不要逐一描述每次迭代。