Communitygithub.com

eternityspring/reelbench-skills

把拉片数据和原片合成一条**能直接看的视频**:一边是画面,一边是这一镜的分镜信息 (镜号、起止、时长、景别、类别、运镜、画面描述、台词),**镜头切了信息跟着切**, 镜头表自动滚动并高亮当前这一镜。 版式只看原片的宽高比:**横版 / 方版 → 画面在上、信息在下;竖版 → 画面在左、信息在右**, 画面永远原样缩放,不裁不拉。 镜头表**随播放滚动**:切点处滚一小段把当前镜头带到锚点、随后停住,高亮条同步滑过去。 实现上只截三张图(底板 + 铺开的长图 ×2),滚动与高亮由 ffmpeg 按时间裁窗, **布局全在 scripts/panel.css 里,改它就能改版式**,不用碰脚本。 吃 video-shots 产出的 shots.json(有 frames/ 就把关键帧当缩略图用)。 零 npm 依赖,用 ffmpeg 合成、无头浏览器渲面板。 Use when asked to 导出视频、合成视频、分镜视频、带分镜信息的视频、解说版视频、 video with shot info、annotated shot video。

reelbench-skills 是什么?

reelbench-skills is a Claude Code agent skill that 把拉片数据和原片合成一条**能直接看的视频**:一边是画面,一边是这一镜的分镜信息 (镜号、起止、时长、景别、类别、运镜、画面描述、台词),**镜头切了信息跟着切**, 镜头表自动滚动并高亮当前这一镜。 版式只看原片的宽高比:**横版 / 方版 → 画面在上、信息在下;竖版 → 画面在左、信息在右**, 画面永远原样缩放,不裁不拉。 镜头表**随播放滚动**:切点处滚一小段把当前镜头带到锚点、随后停住,高亮条同步滑过去。 实现上只截三张图(底板 + 铺开的长图 ×2),滚动与高亮由 ffmpeg 按时间裁窗, **布局全在 scripts/panel.css 里,改它就能改版式**,不用碰脚本。 吃 video-shots 产出的 shots.json(有 frames/ 就把关键帧当缩略图用)。 零 npm 依赖,用 ffmpeg 合成、无头浏览器渲面板。 Use when asked to 导出视频、合成视频、分镜视频、带分镜信息的视频、解说版视频、 video with shot info、annotated shot video。.

兼容平台✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/eternityspring/reelbench-skills/tree/HEAD/skills/video-sync

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

video-sync

Combine shots.json + original video into a single video: the video on one side, the current shot's analysis info on the other. The information updates with every cut, and the shot list auto-scrolls and highlights the current shot.

The layout depends only on the aspect ratio:

Original VideoLayoutComposition Method
Landscape (width > height), SquareVideo on top, info belowvstack
Portrait (height > width)Video on the left, info on the righthstack

Both orientations ensure the video is scaled as-is, without cropping or stretching; the panel fills the remaining space.

Only three screenshots are taken; everything dynamic is handled by ffmpeg. The only things that change on the panel are: scroll position and which row is highlighted. So we measure row positions once, take one static base screenshot, and two long tiled images of the full shot list (one dimmed, one fully lit). Scrolling = cropping a viewport from the dim long image. Highlighting = cropping a single row from the lit long image and overlaying it. Positions are driven by sendcmd expressions at each cut point. For a 53-shot video, screenshots are reduced from 53 to 3 (takes 12 seconds).

The current shot is pinned to the second row: shots 1 and 2 do not scroll (the highlight moves down one row); starting from shot 3, the list scrolls up one row per cut. Motion happens at cut points, pauses within shots — at a cut, it scrolls with --ease (default 0.45 seconds) and stops. For a 16-second long shot, the list stays still (no eye fatigue).

Row height is determined by content — longer descriptions make taller rows. The highlight bar is layered per row height (crop height cannot be changed at runtime).

{baseDir} = the directory containing this file. The script is {baseDir}/scripts/video-sync.mjs, zero dependencies, run directly with node.

Boundaries (what this skill does NOT do): It does not perform shot detection (that's video-shots's job; this skill consumes its shots.json), does not edit, transition, or add music, does not burn subtitles onto the video, and does not do frame-by-frame rendering (motion effects are achieved via ffmpeg time-based cropping).


Step 0 — Obtain shot analysis data first

You need a shots.json (output of video-shots) and the corresponding original video. Having a frames/ directory (with one keyframe per shot) is better — it will be used as thumbnails in the shot table. If absent, the thumbnail slot will be empty.

shots.json must match this video exactly: if meta.durationSeconds does not match the original video's duration, do not composite; go back and correct the shot analysis.

Step 1 — Check geometry first, then process the video

node {baseDir}/scripts/video-sync.mjs plan shots.json --video <video>

This only calculates geometry without compositing — takes seconds. It outputs: layout type, video area, panel area, output dimensions. Confirm these three numbers are reasonable before proceeding — compositing a 3-minute video takes 1–2 minutes; it's not worth redoing if the numbers are wrong.

Adjust if needed: --panel <ratio> (for landscape: panel height ÷ video height, default 0.8; for portrait: panel width ÷ video width, default 1.6), --width / --height (maximum bounds for the video area, default 1920 / 1080).

If the original video is too small, scale up the video area: the panel is calculated relative to the video area. A 640×360 video gives a panel of only 640×288 — four columns crammed together, unreadable. Use --scale 2 to scale the video area to 1280×720 and the panel to 1280×576, preserving aspect ratio. Default is 1 (only downscale, never upscale). plan / panels / compose / export all accept the same --scale.

Step 2 — Render the panel

node {baseDir}/scripts/video-sync.mjs panels shots.json --video <video> \
  --frames frames --lang zh|en [--out panels] [--chrome <path>]

Produces: static.png (base panel), list-dim.png / list-lit.png (full-length tiled images of the shot list — one dimmed, one fully lit), layout.json (row positions, used during composition for scrolling), and a preview file panels/panel.html — open it in a browser to preview the panel. Change shots via #S07 to see effects (the scrolling animation in the preview matches the export).

To change the layout, edit {baseDir}/scripts/panel.css and rerun panels. No need to modify the script or rethink the screenshot logic. Style conventions are documented in {baseDir}/references/panel-style.md.

Step 3 — Composite

node {baseDir}/scripts/video-sync.mjs compose shots.json --video <video> \
  [--panels panels] [-o out.mp4] [--crf 20] [--ease 0.45]

The audio track is copied directly from the original video (silent videos automatically get no audio track). After compositing, the output dimensions, duration, and layout type are printed to stderr — double-check them.

You can also run both steps in one command:

node {baseDir}/scripts/video-sync.mjs export shots.json --video <video> -o out.mp4 --frames frames

Step 4 — Sample frame verification ⛔ Do not skip

Do not deliver just because compositing succeeded. Extract at least three frames to verify the information matches the video:

for t in 5 30 60; do ffmpeg -v error -y -ss $t -i out.mp4 -frames:v 1 -q:v 3 /tmp/check-$t.jpg; done

Check three things: Which shot does this frame belong to? Is the shot number in the panel the same? Does the highlight row follow correctly? If they don't match, usually the shots.json does not correspond to the same video, or the --video parameter points to the wrong file.

Report in one sentence: output path, dimensions, duration, layout type, number of shots, and which panel images were not rendered (if any).


Boundaries

  • Scrolling only occurs at cut points: scrolls with --ease seconds, then stops. The panel is static between cuts. To make it scroll continuously (e.g., for a constant crawl), modify the motionPlan segments — but long shots will cause eye strain.
  • Requires a headless browser (Chrome / Chromium / Edge). This is the price for "layout can be freely customized": using ffmpeg's drawtext for the panel would mean implementing Chinese line breaks, thumbnails, and highlighting from scratch — changing the layout would require rewriting filter graphs.
  • The vocabulary (shot scale / type / camera movement / transition) is bundled within this skill — it does not import across directories, so the skill can be fully copied. If video-shots adds new terms, they must be added here too; otherwise they will appear as enumeration keys.
  • Panel rendering always uses exactly three screenshots, independent of shot count: ~12 seconds for 53 shots. Compositing time scales with video duration: ~75 seconds for a 203-second video.

Self-test

node {baseDir}/scripts/selftest.mjs

122 assertions — no ffmpeg, no browser: geometry (landscape/square/portrait, even-side dimensions, upper/lower bounds, sliders), panel page data contracts (vocabulary delivery, rhythm roles, thumbnail presence/absence, escaping), animation commands (each must be short — if an expression is too long, ffmpeg fails configuration), ffmpeg parameters. Run this after modifying any script.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

eternityspring/reelbench-skills

把一条视频重建成「只有画面和声音」的干净文件——**源片的元数据一概不搬**: GPS、设备型号、账号 ID、创建时间、章节、GoPro 的遥测轨,全部留在原地。 走的是白名单而不是黑名单:不列要删什么,只说带什么过去(画面一条流、声音一条流), 隐私保证来自结构,不来自枚举。 难点不在容器 tag,在**看不见的那几层**:x264 把完整编码参数写成 SEI 塞在码流里、 AAC 把版本号写进 DSE、avc1 的 compressorname 里还有一份——`ffprobe` 一个都看不见。 五个藏身处逐一堵死,每一处都有实测。 默认 `--mode copy`:**画面逐字节照搬**(cmp 验证过),只重编音频,53 秒的片子 1.3 秒跑完; `--mode encode` 完整重编码,多杀掉码流域的东西。 验收不靠声称:把源片所有元数据字符串当「针」,在输出文件里做**字节级扫描**, 扎到一根就红。12 道门全部由脚本确定性检查。 零依赖、零 API key,只要 node 和 ffmpeg。 Use when asked to 清元数据、去元数据、抹掉视频信息、视频隐私、去水印信息、 strip video metadata、remove exif from video、scrub video。

eternityspring/reelbench-skills

拉片:把一条成片拆成逐镜头的分析表——每个镜头的时长、景别、类别、运镜、画面。 分工刻在骨子里:**能量的都由代码量**(切点来自 ffmpeg 场景检测,时长是切点相减, 运动量是逐帧差分的中位数),模型只判断它真正该判断的那几件事(景别 / 类别 / 运镜 / 画面 / 节奏), 然后每一条判断都被代码当场对账——**声称推拉摇移却实测几乎不动,门直接拦**。 看片走联系表(每镜起手帧 + 收尾帧各拼一张大图,一屏二十几个镜头,a/b 对照就是运镜), 不是一张张翻。检测漏刀多刀用 recut 补刀并刀,自动重编号重算时长,手改边界过不了门。 产出 shots.json + Markdown 镜头表 + **单页交互式拉片报告**:内嵌播放器(播放时同步高亮镜头、 点镜头跳转)、镜头节奏带、可搜索可筛选可排序的镜头表(列表 / 卡片两种视图、首尾关键帧并排、 点图开大图)、景别类别运镜分布、出场人物、质量门、导出 JSON。单文件零依赖,离线双击能开。 15 道质量门全部由脚本确定性检查。 零依赖、零 API key,只要 node 和 ffmpeg。 Use when asked to 拉片、拆镜头、分析视频镜头、镜头时长、景别、运镜、镜头表、 video shot breakdown、shot list from video。

相关技能