mcpField
W
wjs-segmenting-video

wjs-segmenting-video

github.com/jianshuo创意与媒体137175 次安装今天更新
代码仓库

关于 wjs-segmenting-video

wjs-segmenting-video 是将长视频与现有 SRT 切分为适合目标平台的独立短片的技能,供需要人工判断主题边界的视频工作流使用。它采用精确时间重编码切片,并输出裁剪片段、逐段 SRT 和帧图,供后续叠加处理。

主要功能

  1. 01依据 SRT 人工判断主题边界
  2. 02按指定时间精确切片
  3. 03检查并按需调整画面方向
  4. 04生成逐段 SRT 与中点帧
  5. 05将原始片段交给后期技能

适用场景

  1. 01从长视频挑选可独立观看的片段
  2. 02为短视频平台准备不同方向的片段
  3. 03切片后交给 wjs-overlaying-video 添加字幕和封面

安装

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

$npx skills add https://github.com/jianshuo/claude-skills --skill wjs-segmenting-video

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

包含的文件

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

references/
example_segments.json
platform_sizes.md
segments_schema.json
scripts/
burn_subs.py
compose_cover.py
make_cover.py
prepend_intro.py
segment.py

详细介绍

wjs-segmenting-video

将长视频和 SRT 切分为多个独立短片,每个短片都适配目标平台。此技能在切分和裁切后即结束——它会将原始短片交给 /wjs-overlaying-video,用于制作封面、字幕、插图、CTA 和最终渲染。

适用场景

  • 有现成 SRT 转录文本的长视频(≥10 分钟)。
  • 目标是制作独立短片(每个短片无需上下文也能看懂)。
  • 用户会(或你会)在 /wjs-overlaying-video 中单独进行后期制作。

不适用场景

  • 单主题剪辑 → 直接使用 ffmpeg -ss A -to B。
  • 尚无转录文本 → 先运行 /wjs-transcribing-audio(如果片段需要使用非源语言,再运行 /wjs-translating-subtitles)。
  • 多机位剪辑 → 使用 /wjs-editing-multicam。
  • 在单个主题内进行多次剪切的集锦 → 这是剪辑,不是切分。

此技能是什么——不是什么

是不是
由你(Agent)阅读完整 SRT 并确定主题边界运行 NLP 主题建模、静音检测或“爆款时刻”评分的脚本。主题边界属于语义范畴;Descript、OpusClip、Riverside Magic Clips 等同类工具都会因自动化处理而判断错误。
segment.py 负责切分;/wjs-reframing-video 负责调整方向端到端的“魔法”流水线
默认采用精准定位切分(重新编码)——片段严格从指定时间戳开始流复制切分(会产生最多一个 GOP 时长的关键帧对齐偏移)
交付裁切后的原始短片 + 每个短片对应的 SRT烧录字幕、封面、片头、CTA(这些由 /wjs-overlaying-video 处理)

工作流

long video + SRT
   ↓     (agent reads SRT, decides topics — judgment, not parsing)
segments.json
   ↓     segment.py --reencode (accurate seek; clip starts exactly at requested t)
clip_NN.mp4 + frame_NN.jpg
   ↓     ASK: target platform orientation match source?
   ↓     /wjs-reframing-video on each clip (if 16:9 → 9:16, etc.)
   ↓     re-extract frames from cropped clips
clip_NN.mp4 (now in target orientation) + clip_NN.zh-CN.burn.srt
   ↓
HAND OFF → /wjs-overlaying-video
   (does covers + captions + illustrations + CTA + final render)

第 1 步——阅读 SRT,编写 segments.json

不要把主题识别交给脚本。 对每个候选片段,判断:

  • 是否完整自洽? 初次观看的观众无需先前上下文也必须能看懂。
  • 是否围绕单一主题? 一个核心问题或见解;如果说话者在片段中途转向新话题,那就是两个片段。
  • 时长是否适合平台? 视频号为 60–180 秒,抖音和 Shorts 为 30–60 秒。短于 30 秒会让人觉得内容被截断;长于 4 分钟则会降低留存率。
  • 是否有引入和收尾? 以主张、问题或生动画面开场;以要点总结收尾。绝不能在句子中间结束。
  • 对齐到 SRT 字幕条目边界——绝不能从单词中间切开。

10 分钟的视频源通常可以剪出 3–6 个高质量片段。删掉平淡的中间内容。质量胜过数量。

架构(完整规范见 references/segments_schema.json,示例见 references/example_segments.json):

{
  "source_video": "input.mp4",
  "source_srt": "input.zh-CN.srt",
  "platform": "wechat_channels",
  "segments": [{
    "id": 1, "slug": "intent-not-code",
    "title": "AI 时代不是写代码\n而是写意图",
    "summary": "Two-sentence pitch — what's the insight, what's at stake.",
    "start": "00:00:43.460", "end": "00:02:35.220",
    "cover_prompt": "Visual concept for gpt-image-2 (style anchor, not literal scene)"
  }]
}

slug = kebab-case 英文(用于文件名)。title 使用 \n 换行,最多 2 行,每行 8–12 个汉字。cover_prompt 由下游 /wjs-overlaying-video 的封面生成步骤使用——请在此处填写,以便叠加技能直接读取,无需再次询问。

第 2 步——精准定位切分

python3 ~/.claude/skills/wjs-segmenting-video/scripts/segment.py \
    --segments segments.json --out output/ --reencode

--reencode 是默认推荐模式。它通过 ffmpeg -ss N -i src -c:v libx264 -c:a aac 切分,使输出严格从指定时间戳开始。使用 CPU 时,每个片段约需 30 秒。它还会为每个片段提取一帧中点画面,保存到 output/frame_NN_slug.jpg。

为什么默认使用 --reencode 而不是流复制:

通过 ffmpeg -ss N -c copy 进行流复制时,会定位到 N 之前最近的关键帧(无法重新编码)。因此,输出的 t=0 会映射到源视频的 t=keyframe,片段会在所需语音前播放一小段“前导”内容。从主 SRT 中按边界 N 切出的字幕会恰好提前于音频一个 GOP 时长——听众会感觉“字幕比声音快”。

实际使用 GOP=2s 的 H.264 视频源时,每个片段都会偏移 0.6–1.5 秒。这看起来像是下游的同步问题;实际上是上游切分时机有误。

流复制变体(仅当你能控制源编码时)

如果源视频已使用 -force_key_frames 在每个 请求的剪辑边界重新编码,流复制是准确的。工作流:

# Build the comma-separated keyframe list from segments.json
KF=$(python3 -c "import json; s=json.load(open('segments.json'))
ts=[]
for seg in s['segments']:
    ts += [seg['start'], seg['end']]
print(','.join(ts))")

# Re-encode master once, forcing keyframes at all segment boundaries
ffmpeg -i master.mp4 \
  -c:v libx264 -preset medium -crf 18 \
  -force_key_frames "$KF" \
  -c:a copy master_kf.mp4

# Now stream-copy cuts land exactly:
python3 segment.py --segments segments.json --source master_kf.mp4 --out output/

仅在反复调整片段边界时使用此方法(你会多次重新剪切 同一个源视频)。对于一次性处理,--reencode 更简单, 而且同样准确。

诊断已剪切片段中的关键帧对齐问题
ffprobe -v error -select_streams v:0 -read_intervals "$((N-2))%$((N+5))" \
  -show_entries packet=pts_time,flags -of csv=p=0 master.mp4 | grep "K_"

类似 360.023,K__ 362.023,K__ 的输出 → GOP=2s。在 361.000 处使用 -c copy 剪切,实际会从 360.023 开始,字幕会比音频 提前 0.977s。补救方法是为每个片段添加 SRT 偏移调整 (请求的起始时间 − 最近的前置关键帧),并应用到每条字幕的 开始/结束时间;但根本的解决方法是使用 --reencode 重新剪切。

第 3 步 — 检查方向(继续前先询问)

将源视频的宽高比与目标平台进行比较:

平台原生方向宽高比
视频号 (WeChat Channels)竖屏9:16
抖音 / TikTok / Reels竖屏9:16
小红书 (Xiaohongshu video)竖屏9:16
YouTube Shorts竖屏9:16
YouTube(普通版)横屏16:9
B站 (Bilibili)横屏16:9

使用 ffprobe 检查:

ffprobe -v error -select_streams v:0 \
  -show_entries stream=width,height -of csv=p=0 clip_01_*.mp4

如果源视频的宽高比已符合平台要求 → 跳过此步骤。

如果不符合 → 转换前询问用户。示例措辞:

源视频是横屏 (1920×1080),平台 视频号 需要竖屏 (9:16)。是否对每段 调用 /wjs-reframing-video 转成竖屏?(crop 会用 MediaPipe 跟踪正在说话 的人的脸,保持说话人始终在画面中)

绝不要悄悄跳过检查——直到上传时才发现横屏片段需要转成竖屏, 会令人非常沮丧,而这正是此技能要避免的问题。

调用 /wjs-reframing-video

裁剪脚本需要在 Python 3.12 虚拟环境中安装 mediapipe + opencv + numpy(mediapipe 不提供适用于 3.14+ 的 wheel)。一次性设置:

uv venv --python 3.12 /tmp/_crop_venv
/tmp/_crop_venv/bin/python -m pip install mediapipe opencv-python numpy

逐片段调用:

for n in 01 02 03 04 05; do
  slug=$(ls clip_${n}_*.mp4 | grep -v -E "_intro|_burned|_vert" | head -1 | sed -E "s/clip_${n}_(.+)\.mp4/\1/")
  /tmp/_crop_venv/bin/python ~/.claude/skills/wjs-reframing-video/scripts/crop.py \
    "clip_${n}_${slug}.mp4" \
    --out "clip_${n}_${slug}_vert.mp4" \
    --target portrait \
    --bitrate 8M    # 视频号 caps at 10Mbps
done

裁剪后,将裁剪版本替换为规范名称,以便后续流水线能够找到它们:

mkdir -p _horizontal_archive
for n in 01 02 03 04 05; do
  base=$(ls clip_${n}_*_vert.mp4 | sed -E "s/_vert\.mp4$//")
  mv "${base}.mp4" "_horizontal_archive/"
  mv "${base}_vert.mp4" "${base}.mp4"
  # Re-extract midpoint frame:
  mid=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "${base}.mp4" | awk '{print $1/2}')
  slug=$(echo "$base" | sed -E "s/^clip_${n}_//")
  ffmpeg -hide_banner -loglevel error -ss "$mid" -i "${base}.mp4" \
    -frames:v 1 -q:v 3 "frame_${n}_${slug}.jpg" -y
done

合理性检查:裁剪日志中的人脸出现在画面内的检测率可能偏低(例如 face#0: 9.6s on screen (9%)),这是因为说话人坐在距离摄像机约 2 m 以外的位置。数值偏低没问题——活动说话人滞后机制加上回退到最大人脸,仍能生成居中效果良好的裁剪结果。但**0 face observations / (no face / fallback): 100% 不行**:没有任何关键点时,裁剪会回退到画面中心;在双人访谈布景中,中心落在说话人之间的背景上(壁炉 / 植物),而不是任何人身上。遇到这种情况时,放弃 MediaPipe 裁剪,改为根据说话人的已知画面位置进行确定性固定裁剪——参见 /wjs-reframing-video →“零检测回退”。提交前务必进行视觉验证:提取一帧中间帧,确认说话人位于画面中央。

第 4 步 — 按片段切分 SRT

python3 ~/.claude/skills/wjs-segmenting-video/scripts/burn_subs.py \
    --segments segments.json --out output/ --no-burn

--no-burn 标志会生成逐片段 SRT(clip_NN_slug.zh-CN.burn.srt), 时间戳已调整为从 0 开始——这正是 /wjs-overlaying-video 字幕所需的输入(其合成会从 t=cover_duration 开始正文, 而不是使用主时钟)。

尽管脚本名称 burn_subs.py 沿用了旧称,但在 --no-burn 模式下,此步骤不会 将像素烧录到画面中——它只是一个 SRT 切分器。(烧录像素的模式供旧版“路径 A”工作流使用,但现已弃用,推荐改用 /wjs-overlaying-video 的 HTML/CSS 字幕渲染。)

交接包 — 交付给 /wjs-overlaying-video 的内容

完成第 1–4 步后,准确交付以下各片段的产物:

output/
  clip_NN_slug.mp4                  # raw cropped clip (target orientation, no subs, no cover)
  clip_NN_slug.zh-CN.burn.srt       # per-clip SRT, timestamps shifted to start at 0
  frame_NN_slug.jpg                 # midpoint frame (cover reference)
  segments.json                     # for slug/title/summary/cover_prompt metadata

接着调用 /wjs-overlaying-video 添加封面、字幕、插图、 CTA,并为每个片段生成可直接上传的 MP4。叠加技能会为每个片段生成一个最终合成视频,并通过一次编码完成渲染(不会连续多次重新编码)。

快速参考

任务命令
剪辑片段(精确,默认)segment.py --segments S.json --out output/ --reencode
探测源视频宽高比ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 IN.mp4
转换方向(先询问)按每个片段调用 /wjs-reframing-video
切分每个片段的 SRTburn_subs.py --segments S.json --out output/ --no-burn
诊断关键帧位置ffprobe -v error -select_streams v:0 -read_intervals A%B -show_entries packet=pts_time,flags -of csv=p=0 src.mp4 | grep K_

常见错误

  • 在句子中间剪切 — 始终对齐到 SRT 字幕条目的边界。
  • 试图用完 100% 的视频内容 — 从 10 分钟视频中剪出 3–6 个优质片段很正常。无聊的中段直接删掉。
  • 让大模型撰写标题 — 标题需要判断,而非概括。传给 make_cover 之前,请先审核并重写。
  • 未使用 --force_key_frames 预处理就进行流复制 — 可能导致片段中的音频最多比字幕提前 1 GOP。除非源视频经过专门处理,否则请使用 --reencode(默认)。
  • 跳过方向检查 — 横屏播客发到视频号,到上传时才发现方向不对,这种情况本可避免。请先探测宽高比,并在裁剪前询问用户。
  • 在此技能中烧录字幕 / 生成封面 — 这些工作已移至 /wjs-overlaying-video。此技能在步骤 4 后结束。

与其他技能集成

  • /wjs-transcribing-audio — 如果缺少源 SRT,请先生成。为准确设置字幕条目时间,优先使用逐词 Whisper 输出(或 Volcano/豆包 ASR 输出)。如果需要翻译片段,请接着调用 /wjs-translating-subtitles。
  • /wjs-reframing-video — 当源视频方向与目标平台不匹配时,在步骤 3 调用。基于人脸跟踪的主动说话者跟随可让说话者始终处于画面内。
  • /wjs-editing-multicam — 如果源视频为多机位,请先渲染同步后的单个 MP4,再进行片段剪辑。
  • /wjs-overlaying-video — 步骤 4 之后所有工作的默认下游技能。封面、字幕、插图、CTA 和最终渲染都在此完成。请勿在此技能中添加后期制作工作。

文件与参考资料

  • scripts/segment.py — 精确定位 + 流复制剪辑
  • scripts/burn_subs.py — SRT 切分工具(--no-burn 模式);旧版 libass 烧录模式已弃用,改用 /wjs-overlaying-video
  • references/segments_schema.json — segments.json 的 JSON Schema
  • references/example_segments.json — 完整示例

评论

支持 Markdown