SuperAPI AI Directory

video-cut

Design & creation

View repository

Updated 10-16 01:48

About this skill

1. Positioning

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

  1. Validate and complete clip_plan.json, write out with clip_id, the original/output time and duration of clip_plan_validated.json。
  2. First avoid the risk of flash frames near hard cuts in the original video, then snap 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 duration to an integer number of output frames (for a move of less than one frame, prefer the side that the sentence-boundary gate still judges 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 video, to avoid a one-frame flash). The sentence-boundary gate checks the aligned boundaries.
  4. Splice the selected intervals and output at 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 real output timeline; this tool does not read narration, nor does it do original-to-output mapping.

The same input will produce the same output.edited_source.mp4.meta.json Record normalized clips, render settings, and each source file's size/mtime_ns; reuse only if all three match the current ones and edited_source.mp4 exists and is non-empty; re-render if any differs. If there is only a sidecar but no media file, do not reuse.

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 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 rough intervals, and the tool will try 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; keep only a 1-second guard on the side immediately adjacent to real dialogue. Windows containing only punctuation ("...") still count as speech.

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

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

duration may be omitted (when omitted, read with ffprobe);source_work_dir may 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 map each interval to recap_story_plan.json one beat of .

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

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

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

For Q&A, reactions, or action payoffs that cannot be deleted, first verify the source evidence, then register the exact 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 absolute path,start/end is in original seconds; for multiple sources, optionally fill in source_id to disambiguate. Only register specific moments that truly need to be kept; do not lock the entire beat by default.before Only record the precedence relationships 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 preserved instance, 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 let a fragment that lacks a prerequisite at the beginning pass. Write the result to clip_plan_validated.json.qc.required_evidence; missing segments, wrong order, or invalid declarations will be blocked during precheck, cache reuse, and before rendering, and the duration relaxation option will not skip them. This result verifies that the selected segments are preserved; the actual semantics and final mix are still checked according to the review steps.

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

4. Run the command

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: clip plan path, default <work_dir>/clip_plan.json。

  • --sources-manifest: source list for multi-source editing {"sources": [{"source_id", "source_path"[, "duration", "source_work_dir"]}]}; for fragments use source_id to specify the source, and snap sentence boundaries and frame cut points according to its 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 is blocked.

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

  • --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 and 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 is blocked, exit with a non-zero status and write the reason to clip_plan_validated.json 's qc.blocking, each item with code:

  • unsafe_clip_sentence_boundary: the fragment boundary is still inside the original spoken audio; see qc.boundary_status.sentence_checksfor per-boundary determination. A blocked boundary has nearest_safe: {"before", "after"}: the nearest safe boundary within 5 seconds before and after {time, reason, delta}(original clip seconds;delta is the number of seconds relative to the current boundary, or nullif none; it is already a landing 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 no longer be blocked by the sentence boundary), modify according to it clip_plan.json 's start/end and rerun. Moving the in-point earlier (before) keeps more, moving it later (after) trims it, and the out-point is the opposite; first confirm that the change will not cut must-preserve content or overlap adjacent fragments. Both sides being null means there is no pause nearby, so change the interval instead of fine-tuning.
  • target_duration_drift: duration deviation exceeds the blocking threshold; details in qc.target_duration。
  • REQUIRED_EVIDENCE_INVALID / REQUIRED_EVIDENCE_MISSING / REQUIRED_EVIDENCE_ORDER / REQUIRED_EVIDENCE_AUDIO_UNAVAILABLE: must-preserve evidence declaration is invalid, missing segments, wrong order, or source has no audio track; 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 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 time of each frame alignment is written in qc.boundary_status.frame_snaps。
  • edited_source.mp4: a constant-frame-rate short video after splicing according to the plan, with frame count equal to qc.frame_grid.frame_count。
  • shot_review.json: only the actual video short-shot/dense-cut candidates generated after --review-shots is enabled; it will not change the plan.

Downstream treats edited_source.mp4 Treated as video, write the Agent according to the output time narration.json as narration.

6. Boundary and Timeline Rules

  • clip_plan.json Use the original clip time;narration.json directly use the post-cut output time; there is no original clip → output narration mapping. The output time is based on clip_plan_validated.json as the standard: frame alignment will move boundaries by less than one frame (no more than 40 ms at 25fps); before writing narration, read the validated plan, and do not estimate using the original intervals you wrote yourself.
  • When the boundary is not on the frame grid, concat will drop 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 does not move, and the duration is still aligned to an integer number of output frames.
  • By default, overlapping or repeating original clip intervals is prohibited;--allow-overlap only allowed after it is enabled.
  • A segment start point can only be located at the source start, a reliable sentence end/silence window, or form a lossless same-source continuous connection with the previous segment; the same applies to segment end points. If ASR determines that speech is still ongoing and it cannot snap, write unsafe_clip_sentence_boundary and block.
  • SCENE_CUT_SNAP Enabled by default: first snap source start backward and source end forward to nearby hard cuts based on the picture, then perform final correction with sentence-end snapping, to avoid visual correction re-creating half-sentence original audio; when there is no nearby pause and sentence-end snapping cannot correct it, the avoidance that moved the boundary into speech will be withdrawn (if the original position can pass the gate). Default range is SCENE_CUT_SNAP_MARGIN=0.5 seconds, detection threshold is SCENE_CUT_DETECT_THRESHOLD=0.4。
  • The scene-change score only provides junction candidates; it does not prove that a junction is natural. First check whether dense candidates appear within a short time window, then distinguish the source: unrelated short shots from the original clip should be deleted entirely; related but short shots that look like flash frames should be preserved by expanding IN/OUT to keep the complete action, reaction, or dialogue, without using freeze frame/slow motion to fake duration; cut points created by this concatenation should preferably be handled by moving the boundary, restoring same-source continuous motion, merging adjacent segments, or switching to a more natural connection, to eliminate them as much as possible. After the final video is produced, still play around each junction for about 0.5–1 second 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 cover 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 just to lower the scene score; for the framing review and repair verification workflow, see references/shot-review.md。
  • For lossless connections of continuous same-source segments, do not apply double-sided audio fade-out within the sentence; for non-continuous segments, still apply anti-pop fade-in/fade-out within safe pauses.

When it is necessary to check frequent shot changes within a short time, 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 the starting threshold, not a quality criterion; large motion, white flashes, and dissolves may all cause false positives. After mapping candidates back to the original clip shot and this concatenation boundary, handle them according to the source classification above, and play at normal speed to decide whether to keep them.

When it is necessary to be precise to the actual frame, inspect residual shots inside long intervals, and 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 perform semantic understanding, does not write narration, and does not choose segments for the Agent; the scene filter only handles technical boundary candidate detection.
  • Only performs generation edited_source.mp4 the required cutting, concatenation, and one intermediate encoding, and does not handle subtitle packaging or final delivery compression.