SuperAPI AI Directory

video-cut

Design & creation

View repository

Updated 10-16 09:30

About this skill

1. Positioning

This skill only executes the editing decisions the Agent has already made:

  1. Validate and complete clip_plan.json, write out the clip_idwith original/output times and durations, clip_plan_validated.json。
  2. First avoid the risk of flash frames near hard cuts in the original footage, then snap the boundaries to reliable sentence ends/natural pauses; audio integrity has final priority.
  3. Align each in-point to the source video frame grid and each segment's duration to an integer number of output frames (for moves of less than one frame, prefer the side that the sentence-boundary gate still judges as safe and that is still within the pause; when both sides are equally safe, choose the side that does not cross a hard cut in the original footage, to avoid flashing a frame); the sentence-boundary gate checks the aligned boundaries.
  4. Splice the selected intervals and output a constant-frame-rate edited_source.mp4, with the frame count matching what is recorded in clip_plan_validated.json .
  5. Stop here; the Agent writes narration.jsonaccording to the actual output timeline; this tool does not read narration, nor does it do original→output mapping.

The same input yields the same output.edited_source.mp4.meta.json records the normalized clips, render settings, and each source file's size/mtime_ns; reuse only when all three match the current state and edited_source.mp4 exists and is non-empty; if any differs, re-render. A sidecar without a media file is not reused.

2. Input contract

work_dir/clip_plan.json can be an array, or {"clips": [...]}:

{"start": 12.0, "end": 28.5, "reason": "b02 | turn | power: A→B | POV=女主 | 保留反应 | 入点=问题落下 | 出点=沉默结束"}
  • start / end is in original-footage seconds; also accepts source_start / source_end or in / out。
  • top-level optional target_duration, e.g. "10m"。
  • Each clip in a multi-video project must also fill in source_id(does not accept id as a substitute), and pass in the source manifest via --sources-manifest (shape see below).
  • speech_boundary_anchors.json and ASR time ranges are provided by the understanding stage; the Agent first writes approximate intervals, and the tool will attempt to snap them and return in/out points still within speech intervals as blockers. Windows containing only interjections or ASR noise ("Ah!", "Hi.") do not count as speech intervals, and only keep a 1-second guard on the side immediately adjacent to real dialogue. Windows with only punctuation ("……") still count as speech.

The multi-video source manifest accepts only one shape; other shapes error out directly and state the expected shape:

{"sources": [{"source_id": "ep1", "source_path": "/media/ep1.mp4", "duration": 1520.0, "source_work_dir": "sources/ep1"}]}

duration can be omitted (when omitted, read with ffprobe);source_work_dir can be omitted; when filled in, it is relative to --work-dir, used to read that source's silence, sentence-end anchors, and ASR. Other keys are ignored.

3. Editing intent contract

The tool will not make creative choices for the Agent. Before writing clips, complete the editing intent check in this section, and make each interval map to one beat of recap_story_plan.json .

Use the existing free-text reason to save concise decisions:

beat_id | function | change | POV | preferred moment | 入点 reason | 出点 reason

Do not keep an entire segment just because "the event is important"; keep the specific performance, reaction, action, or reveal that most makes the change hold. When understanding and emotion allow, enter late and exit early, while ensuring dialogue, action, and technical boundaries are complete.

For question-answer, reaction, or action payoffs that cannot be deleted, first verify the source evidence, then register the precise interval in the same clip_plan.json :

{
  "clips": [{"start": 12, "end": 18}],
  "required_evidence": {
    "nodes": [
      {"id": "refusal", "source": "/media/episode.mp4", "start": 12.25, "end": 14.5, "track": "audio", "content": "对方拒绝请求"},
      {"id": "response", "source": "/media/episode.mp4", "start": 15, "end": 17.5, "track": "video", "content": "听到拒绝后的反应与决定"}
    ],
    "before": [["refusal", "response"]]
  }
}

source uses the actual source file's absolute path,start/end is in original-footage seconds; for multiple sources, source_id may also be filled in for disambiguation. Register only the specific moments that truly need to be kept, and do not lock down the entire beat by default.before Record only the precedence relations required by this clip; when no order constraint is needed, write before: []。

After snapping all frames/sentence boundaries, the tool checks that every must-preserve moment has at least one fully continuous retained occurrence, source, and precedence; for audio nodes it also checks whether the source audio track exists. Every result occurrence (including partial fragments) must satisfy its declared premise; a later complete segment cannot be used to pass a fragment that lacks a premise at the beginning. The result is written to clip_plan_validated.json.qc.required_evidence; missing segments, wrong order, or invalid declarations will block at precheck, cache reuse, and before rendering, and the duration relaxation option will not skip this. This result verifies that the selected segments are retained; actual semantics and the final mix must still be checked according to the review step.

The following scripts/... are all relative to this skill directory. If the executor is started from the repository root, prepend this skill's absolute directory to the script path.

4. Run commands

python3 scripts/cut.py <video> --work-dir <work_dir> [--clip-plan <clip_plan.json>] \
  [--sources-manifest <sources.json>] [--target-duration 10m] [--allow-overlap] \
  [--allow-duration-drift] [--normalize-only] \
  [--review-shots [--shot-scene-threshold 0.35] [--shot-roi X Y W H]]
  • --clip-plan: path to the edit plan, default <work_dir>/clip_plan.json。

  • --sources-manifest: source list for multi-source editing {"sources": [{"source_id", "source_path"[, "duration", "source_work_dir"]}]}; fragments use source_id to indicate the source, and snap sentence boundaries and picture cut points according to their own source.

  • --target-duration: target duration. If the ratio of actual duration to target is outside 0.85–1.15, a warning is recorded; outside 0.60–1.40, it blocks.

  • --allow-duration-drift: allow only duration-deviation blocks (recorded as allowed: true warning), do not allow sentence-boundary or must-preserve evidence blocks.

  • --normalize-only: only normalize, snap, and check the plan, write out clip_plan_validated.json and then exit without rendering.

  • --review-shots: on the rendered or reused edited_source.mp4 recall short-shot and dense-cut candidates, report only, do not fix;--shot-scene-threshold is the recall threshold (default 0.35, not an acceptance criterion),--shot-roi scans only that pixel rectangle (use when there are black bars or packaging), and does not crop the picture.

When cut blocking occurs, exit with a nonzero status and write the reason into clip_plan_validated.json 's qc.blocking, each item with code:

  • unsafe_clip_sentence_boundary: the clip boundary is still within the original speech; for per-boundary determination see qc.boundary_status.sentence_checks. A blocked boundary includes nearest_safe: {"before", "after"}: the nearest safe boundary within 5 seconds before/after {time, reason, delta}(source seconds;delta is the number of seconds relative to the current boundary, or nullif none; it is already a point on the original frame grid and has been rechecked; when rerunning, if shot-change avoidance pulls it back into speech, this avoidance will be reverted,qc.boundary_status.shot_snaps records reverted_unsafe, so writing it back as-is will not be blocked again due to sentence boundaries), modify according to it clip_plan.json 's start/end and rerun. Moving the in-point earlier (before) retains more, moving it later (after) cuts it off; the out-point is the opposite; first confirm that the change will not cut off must-preserve content or overlap adjacent fragments. If both sides are null , it means there is no pause nearby, so change the interval rather than fine-tuning.
  • target_duration_drift: duration deviation exceeds the blocking threshold; see details in qc.target_duration。
  • REQUIRED_EVIDENCE_INVALID / REQUIRED_EVIDENCE_MISSING / REQUIRED_EVIDENCE_ORDER / REQUIRED_EVIDENCE_AUDIO_UNAVAILABLE: must-preserve evidence declaration is invalid, segments are missing, order is wrong, or the source has no audio track; see details in qc.required_evidence。

5. Output contract

  • clip_plan_validated.json: normalized fragments, including clip_id、source_start/end、output_start/end、duration and frame_count(the number of frames rendered for that fragment).qc.frame_grid records the output frame rate output_frame_rate(single-source uses the source frame rate, multi-source uses the canvas frame rate, NTSC is written as 30000/1001), total frame count, and each source frame rate; the before/after times of each frame alignment are written in qc.boundary_status.frame_snaps。
  • edited_source.mp4: a constant-frame-rate short video assembled according to the plan, with frame count equal to qc.frame_grid.frame_count。
  • shot_review.json: only --review-shots actual-video short-shot/dense-cut candidates generated after enabling; it will not change the plan.

Downstream treats edited_source.mp4 As video, take what the Agent writes by output time narration.json as narration.

6. Boundary and timeline rules

  • clip_plan.json Use source time;narration.json directly use the post-cut output time; there is no source → output narration mapping. Output time is based on clip_plan_validated.json : frame alignment moves the boundary by less than one frame (no more than 40 ms at 25fps). Before writing narration, read the validated plan; do not infer from the raw intervals you wrote yourself.
  • When the boundary is not on the frame grid, concat drops one frame slot at each junction (at 25fps the picture pauses for 80 ms, and the final video becomes variable frame rate), so frame alignment cannot be turned off. When the source frame rate is unknown (r_frame_rate is 0/0 or greater than 120), the in-point stays fixed, and the duration is still aligned to an integer number of output frames.
  • Overlapping or repeating source intervals is prohibited by default;--allow-overlap it is allowed only after being enabled.
  • A clip start point may only be at the source start, a reliable sentence end/silence window, or form a lossless same-source continuous connection with the previous clip; the same applies to clip end points. When ASR determines that speech is still ongoing and it cannot be snapped, write unsafe_clip_sentence_boundary and block.
  • SCENE_CUT_SNAP Enabled by default: first snap source start backward and source end forward to a nearby hard cut based on the picture, then sentence-end snapping makes the final correction, to avoid visual correction re-creating a half-sentence of original audio; when there is no nearby pause and sentence-end snapping cannot fix it, that avoidance of moving the boundary into speech is withdrawn (if the original position can pass the gate). The default range is SCENE_CUT_SNAP_MARGIN=0.5 seconds, and the detection threshold is SCENE_CUT_DETECT_THRESHOLD=0.4。
  • The scene-change score only provides junction candidates; it does not prove a junction is natural. First check whether dense candidates appear within a short time window, then distinguish the source: unrelated short shots native to the source footage are deleted entirely; shots that are relevant but so short they look like flash frames are preserved by expanding IN/OUT to keep the complete action, reaction, or dialogue, without faking duration with freeze frames/slow motion; cuts created by this concatenation should first be handled by moving the boundary, restoring same-source continuous motion, merging adjacent clips, or switching to a more natural connection, and eliminated as much as possible. After the final video is produced, still play about 0.5–1 second before and after each junction one by one; for white flashes or exposure dissolves, combine with frame-by-frame brightness localization. Do not cut off complete dialogue just to pass visual detection, and do not use transitions to hide bad junctions.
  • When trimming short residual shots, do not apply extreme zoom or displacement near the junction that is discontinuous with the shot it belongs to merely to lower the scene score; for the framing review and repair verification workflow, see references/shot-review.md。
  • Lossless connections of continuous same-source clips do not use mid-sentence two-sided audio fade-out; non-continuous clips still use pop-preventing fade-in/fade-out within safe pauses.

When you need to check frequent shot changes over a short period, first use the ffmpeg scene filter to recall candidate times:

ffmpeg -i input.mp4 -vf "select='gt(scene,0.35)',showinfo" -an -f null -

0.35 is a starting threshold, not a quality criterion; large motion, white flashes, and dissolves can all cause false positives. After mapping candidates back to the source shots and this concatenation's boundaries, handle them according to the source classification above, and play at normal speed to decide whether to keep them.

When you need precision down to actual frames, to check residual shots inside long intervals, and to record the plan path used, use scripts/shot_review.py or cut.py --review-shots(add --roi / --shot-roiwhen there are black bars or packaging); see references/shot-review.md。

7. Capability boundaries

  • Does not do semantic understanding, does not write narration, does not choose clips for the Agent; the scene filter only handles technical boundary candidate detection.
  • Only performs the cutting, concatenation, and one intermediate encoding required to generate edited_source.mp4 , and does not handle subtitle packaging or final delivery compression.