本文へ移動
cccskills
無料GitHub で公開

wjs-segmenting-video

Use when the user has a long-form video (interview / lecture / podcast / conversation) and a transcript SRT, and wants to extract 3–6 stand-alone topical short clips from it. This skill ONLY cuts and crops — it produces raw clips + per-clip SRTs as a hand-off package for downstream post-production (`/wjs-overlaying-video`). Triggers — "切成几段", "分主题", "拆成短视频", "切片", "topic segments", "split into clips".

インストール方法を見る

含まれるファイル(9)

  • SKILL.md13.8 KB
  • references/example_segments.json1.2 KB
  • references/platform_sizes.md907 B
  • references/segments_schema.json1.8 KB
  • scripts/burn_subs.py9.3 KB
  • scripts/compose_cover.py7.5 KB
  • scripts/make_cover.py5.3 KB
  • scripts/prepend_intro.py11.6 KB
  • scripts/segment.py4.8 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

wjs-segmenting-video

Cut a long video + SRT into multiple stand-alone short clips, each oriented for the target platform. This skill stops after cutting + cropping — it hands off the raw clips to /wjs-overlaying-video for covers, captions, illustrations, CTA, and final render.

When to use

  • Long-form video (≥10 min) with an existing SRT transcript.
  • Goal is stand-alone short clips (each viewable without context).
  • The user will (or you will) drive post-production separately in /wjs-overlaying-video.

When NOT to use

  • Single-topic trimming → just use ffmpeg -ss A -to B.
  • No transcript yet → run /wjs-transcribing-audio first (then /wjs-translating-subtitles if the segments need a non-source language).
  • Multicam editing → use /wjs-editing-multicam.
  • Highlight reel with multiple cuts inside a single topic → that's editing, not segmentation.

What this skill IS — and IS NOT

IsIs not
You (the agent) read the full SRT and decide the topic boundariesA script that runs NLP topic modeling, silence detection, or "viral moment" scoring. Topic boundaries are semantic; competing tools (Descript, OpusClip, Riverside Magic Clips) all get this wrong by automating it.
segment.py cuts; /wjs-reframing-video reorientsAn end-to-end "magic" pipeline
Accurate-seek cuts by default (re-encode) — clip starts EXACTLY at requested timestampStream-copy cuts (those produce keyframe-snap drift up to GOP duration)
Hands off raw cropped clips + per-clip SRTsBurned subtitles, covers, intros, CTAs (those live in /wjs-overlaying-video)

The pipeline

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)

Step 1 — Read SRT, write segments.json

Don't outsource topic identification to a script. For each candidate segment, judge:

  • Self-contained? A cold viewer must understand it without prior context.
  • Single thread? One central question / insight; if the speaker pivots mid-clip, that's two segments.
  • Length fits platform? 60–180s for 视频号 / 30–60s for 抖音&Shorts. <30s feels truncated; >4min loses retention.
  • Hook + payoff? Open on a claim / question / vivid image; close on a takeaway. Never end mid-sentence.
  • Snap to SRT cue boundaries — never cut mid-word.

3–6 strong segments from a 10-minute source is normal. Drop boring middles. Quality > quantity.

Schema (full spec in references/segments_schema.json, example in 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 English (used in filenames). title uses \n for line break, 2 lines max, 8–12 Chinese chars per line. cover_prompt is consumed downstream by /wjs-overlaying-video's cover-generation step — keep it written here so the overlay skill can pick it up without re-asking.

Step 2 — Accurate-seek cut

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

--reencode is the default recommended mode. It cuts with ffmpeg -ss N -i src -c:v libx264 -c:a aac so the output starts EXACTLY at the requested timestamp. ~30s per clip on CPU. Also extracts a midpoint frame per segment to output/frame_NN_slug.jpg.

Why default to --reencode and not stream-copy:

Stream-copy via ffmpeg -ss N -c copy seeks to the nearest keyframe before N (it can't re-encode). The output's t=0 then maps to source t=keyframe, so the clip plays a fraction of a second of "lead-in" content before the requested speech. Captions sliced from the master SRT at boundary N appear AHEAD of the audio by exactly that GOP fraction — listeners feel "subtitles lead the voice."

In practice on H.264 source with GOP=2s: every clip is off by 0.6–1.5s. Looks like a synchronization bug downstream; it's actually a cut-time bug upstream.

Stream-copy variant (only if you control the source encode)

If the source has been re-encoded with -force_key_frames at every requested cut boundary, stream-copy IS accurate. Workflow:

# 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/

Use this only when iterating on segment boundaries (you'll re-cut the same source many times). For one-shot work, --reencode is simpler and just as correct.

Diagnosing keyframe-snap on already-cut clips

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_"

Output like 360.023,K__ 362.023,K__ → GOP=2s. A -c copy cut at 361.000 actually starts at 360.023, captions are 0.977s ahead of audio. The retroactive fix is a per-clip SRT offset shim (requested_start − nearest_preceding_keyframe) added to every cue's start/end, but the root fix is to re-cut with --reencode.

Step 3 — Orientation check (ask before continuing)

Compare source video aspect ratio to the target platform:

PlatformNative orientationAspect
视频号 (WeChat Channels)vertical9:16
抖音 / TikTok / Reelsvertical9:16
小红书 (Xiaohongshu video)vertical9:16
YouTube Shortsvertical9:16
YouTube (regular)horizontal16:9
B站 (Bilibili)horizontal16:9

Probe with ffprobe:

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

If source aspect already matches the platform → skip this step.

If mismatch → ASK THE USER before converting. Sample phrasing:

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

Never silently skip the check — finding out at upload time that your horizontal clip needs to be vertical is a frustrating failure mode the skill exists to prevent.

Calling /wjs-reframing-video

The crop script needs mediapipe + opencv + numpy in a Python 3.12 venv (mediapipe doesn't ship wheels for 3.14+). One-time setup:

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

Per-clip invocation:

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

After cropping, swap the cropped versions to canonical names so downstream pipelines find them:

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

Sanity check: face-on-screen detection rate in the crop log can read low (e.g. face#0: 9.6s on screen (9%)) when speakers sit further than ~2 m from the camera. A low number is OK — the active-speaker hysteresis + fallback-to-largest-face still produces well-centered crops. But 0 face observations / (no face / fallback): 100% is NOT OK: with zero landmarks the crop falls back to the frame center, which on a two-person interview set lands on the background between the speakers (fireplace / plant), not on anyone. When you see that, abandon the MediaPipe crop and do a deterministic fixed crop on the speaker's known screen position — see /wjs-reframing-video → "Zero-detection fallback". Always verify visually by extracting a midpoint frame and confirming the speaker is centered before committing.

Step 4 — Slice per-clip SRTs

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

The --no-burn flag emits per-clip SRTs (clip_NN_slug.zh-CN.burn.srt) with timestamps already shifted to start at 0 — exactly the input /wjs-overlaying-video captions expect (its compositions start the body at t=cover_duration, not the master clock).

Despite the legacy name burn_subs.py, this step does NOT burn pixels in --no-burn mode — it's just an SRT slicer. (The burn-pixels mode exists for the legacy "Path A" workflow but is deprecated in favor of /wjs-overlaying-video's HTML/CSS caption rendering.)

Hand-off package — what to deliver to /wjs-overlaying-video

After Steps 1–4, deliver EXACTLY these per-segment artifacts:

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

Then invoke /wjs-overlaying-video to add covers, captions, illustrations, CTA, and produce the upload-ready MP4 per clip. The overlay skill generates ONE final composition per clip and renders it in a single encode (no cascade of re-encodes).

Quick reference

TaskCommand
Cut clips (accurate, default)segment.py --segments S.json --out output/ --reencode
Probe source aspectffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 IN.mp4
Convert orientation (ask first)invoke /wjs-reframing-video per clip
Slice per-clip SRTsburn_subs.py --segments S.json --out output/ --no-burn
Diagnose keyframe positionsffprobe -v error -select_streams v:0 -read_intervals A%B -show_entries packet=pts_time,flags -of csv=p=0 src.mp4 | grep K_

Common mistakes

  • Cutting mid-sentence — always snap to SRT cue boundaries.
  • Trying to use 100% of the video — 3–6 strong clips from 10 min is normal. Boring middle = drop.
  • Letting the LLM write the title — the title is judgment, not summary. Review and rewrite before passing to make_cover.
  • Stream-copy without --force_key_frames preprocessing — produces clips with audio ahead of captions by up to 1 GOP. Use --reencode (default) unless the source was specifically prepared.
  • Skipping the orientation check — getting a horizontal podcast on 视频号 and finding out at upload time is preventable. Probe aspect and ask the user before cropping.
  • Burning subs / generating covers in THIS skill — those moved to /wjs-overlaying-video. This skill stops after Step 4.

Integration with other skills

  • /wjs-transcribing-audio — produce the source SRT first if missing. The word-level Whisper output (or Volcano/豆包 ASR output) is preferred for accurate cue timing. If the segments need translating, chain into /wjs-translating-subtitles.
  • /wjs-reframing-video — call in Step 3 when source orientation doesn't match target platform. Face-tracked active-speaker following keeps the talker in frame.
  • /wjs-editing-multicam — if the source is multi-cam, render the synced single MP4 first, then segment.
  • /wjs-overlaying-video — the default downstream for everything after Step 4. Covers, captions, illustrations, CTA, and final render all happen there. Don't add post-production in this skill.

Files & references

  • scripts/segment.py — accurate-seek + stream-copy cutting
  • scripts/burn_subs.py — SRT slicer (--no-burn mode); legacy libass burn-in mode is deprecated in favor of /wjs-overlaying-video
  • references/segments_schema.json — JSON Schema for segments.json
  • references/example_segments.json — worked example

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

王建硕的思维框架与表达方式。基于 7 份深度调研(约 100 万词英文博客 + 约 109 万字中文博客, 2002–2022,全部一手)提炼出 7 个核心心智模型、10 条决策启发式和完整的双语表达 DNA。 用途:以王建硕的身份和声音写作、回应、思考——他平实、诚恳、好奇,爱用家常比喻和自造词, 在具体和抽象之间反复架梯子,从不写自己没亲身验证过的东西。 当用户提到「用王建硕的视角」「王建硕会怎么看」「王建硕模式」「像王建硕一样写」 「Jian Shuo Wang perspective」「切换到王建硕」时使用。 即使用户只是说「帮我用王建硕的角度想想」「如果是王建硕会怎么写这篇」也应触发。 一旦激活,本次对话后续所有回应都保持王建硕身份,直到用户明确说「退出」—— 不需要每轮重新点名。 不适用:用户要的是关于王建硕本人的客观介绍 / 事实查询(「王建硕是谁」「他哪年创业的」), 那种问题正常回答即可,不要进入角色扮演。

日本語の概要は準備中です。原文の説明を表示しています。

jianshuo/claude-skills1312026年8月21日 更新

Use when the user asks to audit what's wrong with a project, "make it right", "看看项目出了什么问题", "为什么用户的需求还没上线", "为什么没提交App Store", "为什么没新build", or wants a holistic state-of-the-project check covering unmerged branches, stalled PRs, failed GitHub Actions, stale builds, plan drift (TODOS.md / ROADMAP), unreleased commits, and log errors. Runs read-only investigation, presents a grouped checklist, fixes only after explicit user confirmation. Aware of the Cathier iOS app workflow (Xcode + fastlane + auto-merge @claude PRs from in-app feedback).

日本語の概要は準備中です。原文の説明を表示しています。

jianshuo/claude-skills1312026年8月21日 更新

Use when the user has a video + an SRT and wants the subtitles either burned into the pixels (libass, always-visible) or soft-muxed as a togglable track. Also handles the final composite step for the localization pipeline — burn subs, mix a dub track, and keep the original audio as a low-volume bed, all in ONE ffmpeg encode (no cascade). Verifies libass availability and auto-downloads a static evermeet ffmpeg build when Homebrew's stripped binary lacks it. Triggers — "烧字幕", "硬字幕", "burn subtitles", "burn-in subs", "embed subtitle", "soft mux SRT", "把字幕烧进视频", "做最终合成".

日本語の概要は準備中です。原文の説明を表示しています。

jianshuo/claude-skills1312026年8月21日 更新

Use when the user complains about spam on his X/Twitter posts — 同城面付 / 寻固炮 / 线下上门 / 免费破处 这类引流号在他推文下刷的 emoji 垃圾回复 — and wants them removed. Covers the last 7 days (X recent-search window). Triggers — "把这些spam删掉", "清理X垃圾回复", "推文下面好多引流号", "clean spam replies", "/wjs-cleaning-spam".

日本語の概要は準備中です。原文の説明を表示しています。

jianshuo/claude-skills1312026年8月21日 更新

Use when the user wants a 王建硕-style WeChat article (article.md) turned into a narrated short MP4 video — TTS voiceover via 火山引擎 Volcano TTS, HyperFrames CSS/GSAP animation per scene, subtle SFX, abstract watercolor background, full pipeline rendering to 1080×1920 portrait MP4 (30-90s). Triggers — "把这篇文章做成视频", "做一个解说视频", "讲解视频", "/wjs-converting-text-to-video".

日本語の概要は準備中です。原文の説明を表示しています。

jianshuo/claude-skills1312026年8月21日 更新

Use when migrating a WordPress site to a Hugo static site on GitHub Pages from a WXR export (.xml) plus the wp-content/uploads folder — preserving /archives/<id>/ URLs, localizing images, and deploying via GitHub Actions. Triggers — "把 WordPress 迁成 Hugo", "wordpress 转静态站", "migrate WordPress to Hugo", "WXR to Hugo", "publish WordPress to GitHub Pages", "/wjs-converting-wp-to-hugo".

日本語の概要は準備中です。原文の説明を表示しています。

jianshuo/claude-skills1312026年8月21日 更新

jianshuo のスキルをすべて見る

このスキルの問題を報告する