動画カット は何をしますか?
長い未編集動画を、コンパクトで視聴可能な ファーストカット に変換します — 決定的で、内容を考慮し、自己検証済みのものです。人間(あなた、モデル)が「何を残すか」という編集上の判断を下し;バンドルされたスクリプトが機械的な精密作業(境界調整、無音除去、メモリ安全なレンダリング、自己チェック)を行います。
使用するタイミング
- 生の未編集動画(YouTube URLまたはローカルファイル)を、よりタイトなカットにする必要がある場合。
- 「半分にする」「退屈な部分をカットする」「ファースト/ラフカットを作る」というリクエストがある場合。
- 以下の用途には 使用しません:単語単位のフィラー微細手術、マルチカムBロール編集、色/音声の仕上げ — これらは後続のP1/P2の仕上げ工程です。このスキルはP0(優先度最優先)です:診断の検証と、確実なカットの生成を行います。
依存関係
/video-understand が前提条件です。最初にそれを実行し、その検証済みのメディア、文字起こし、分析、タイムラインをこのスキル内で再作成する代わりに使用してください。開始する前に、それがインストールされていることを確認してください。インストールされていない場合は、この前提条件が欠けていることをユーザーに警告し、メディアを処理する前に停止してください。
以下がPATH上にある環境(特定のconda環境名を想定しないでください)を有効にしてください:
yt-dlp(ダウンロード)、ffmpeg/ffprobe(カット/レンダリング/プローブ)faster-whisperがインストールされたPython(CPUでも動作:device=cpu, compute_type=int8) まず確認:yt-dlp --version、ffmpeg -version、python -c "import faster_whisper"
プロジェクトプロトコルワークフロー
以下の永続的なプロジェクトファイルを使用します:
work/cut/edit-plan.json # 手書きの保持/削除判断のみ
work/timeline.json # 生成された精密範囲とソース/プログラムのマッピング
review/01-cut/cut-summary.md
review/01-cut/timeline-map.png
review/01-cut/boundary-review.mp4
review/01-cut/full-proxy.mp4 # オプションの全体プログラムペーシングレビュー用
各正規の判断には、安定した id、action(keep または drop)、start_s、end_s、reason、および意味/文字起こしの evidence_refs があります。保持判断は時系列順に並べます。V1は並べ替えや重複したソースクリップをサポートしません。
再び文字起こしや診断を行う代わりに、共有のエビデンスレイヤーを再利用します:
python skills/video-understand/scripts/transcribe.py work/cache/audio16k.wav work/understand/transcript
python skills/video-understand/scripts/analyze.py work/understand/transcript.json work/understand/analysis.json
既存の精密スクリプトは正規のプランを受け入れます。単語単位で安全な境界を拡張し、オプションで線形可変速を割り当て、共有タイムラインを生成します:
python skills/video-cut/scripts/build_edit.py work/cut/edit-plan.json work/understand/transcript.json work/cache/edit-final.json
python skills/video-cut/scripts/assign_speed.py work/cache/edit-final.json work/understand/transcript.json
python skills/video-understand/scripts/build_timeline.py work/cache/edit-final.json work/timeline.json --fps-num SOURCE_FPS_NUM --fps-den SOURCE_FPS_DEN
work/understand/media.json から正確な有理数FPSを使用し、30000/1001 を 30/1 に丸めないでください。timeline-map.png と短い boundary-review.mp4 をレビューします。full-proxy.mp4 は、プログラム全体のペーシングをレビューする必要がある場合にのみ生成します。結果を cut-summary.md に書き込み、操作チェックレポートをそのファイルに向けます。
タイムラインの貢献を、必要なソース入力とともに記録し、操作整数 revision を更新し、based_on を消費した理解リビジョンに正確に設定します:
"render": {
"kind": "timeline-transform",
"input": "../input/original-video.mp4",
"plan": "timeline.json"
}
共有デリバリレンダラーは、すべてのリビジョンチェックが通過した後にアクティブなシーケンスをエンコードします。
スタンドアロン互換性ワークフロー
以下のレガシーファイルとコマンドは、移行中もサポートされています。build_edit.py は edit_coarse.json と正規の decisions[] の両方を受け入れます; cut_render.py は edit_final.json と timeline.json の両方を受け入れます; このスキルの transcribe.py は ../video-understand/scripts/transcribe.py の互換性ラッパーです。
すべての中間ファイルは work/ の下に保持し、プロジェクトルートに納品します:
work/source.mp4 # ダウンロードされたオリジナル
work/audio16k.wav # whisper用の16kモノラル音声
work/transcript.json/.srt # 単語レベルのタイムスタンプ
work/analysis.json # 診断(無音/フィラー/連続発話)
work/edit_coarse.json # *** あなたの編集判断(手書き) ***
work/edit_final.json # 展開されたタイトなレンダリングセグメント(生成; セグメントごとの速度を含む)
work/selfcheck/ # カット再文字起こし + 結合フレーム
first_cut.mp4 # 納品物
first_cut_report.md # 編集 + 自己チェックレポート
パイプライン(6ステップ)
1. ダウンロード
最高アクセス可能な品質 + 音声。デフォルトのプレイヤークライアントはしばしば失敗します(利用不可 / DRM / サインイン)。信頼性のあるフォールバックは android クライアントです:
yt-dlp --extractor-args "youtube:player_client=android" \
-o work/source.mp4 "<URL>"
ログインなしで到達可能なのがマージされた360p(フォーマット18)のみの場合、それを受け入れ、制約を文書化してください — HD DASHはしばしばDRM/ログインで制限されています。その後、音声を抽出します:
ffmpeg -y -i work/source.mp4 -ac 1 -ar 16000 work/audio16k.wav
2. 文字起こし(単語レベル)
python scripts/transcribe.py work/audio16k.wav work/transcript [base.en]
work/transcript.json(セグメント + 単語ごとの開始/終了)と .srt を生成します。
faster-whisper、CPU/int8、VADオン、word_timestamps=True。
非英語: 多言語モデルと --lang を渡します。例: python scripts/transcribe.py work/audio16k.wav work/transcript medium --lang zh
(.en モデルは英語専用です)。最初に短いサンプルで言語を確認してください。
注意: Whisperはほとんどの発声された「えーと/あのー」を除去します — それらは文字起こしでは測定できません。そのため、フィラー検出は、文字起こしテキストのみに依存するのではなく、以下の語彙的な単語検索と無音ギャップ分析を組み合わせる必要があります。
3. 診断(「見る」+「読む」)
python scripts/analyze.py work/transcript.json work/analysis.json
以下の情報を提供します: 先頭/末尾の無音、単語間の無音 ≥0.8秒(その後の単語を含む)、フィラーカウント、WPM、発話比率、ほぼ同一の繰り返し、>40秒のモノローグラン(駄目候補)。
映像を 見る ために、約2秒ごとにフレームをコンタクトシートにサンプリングし、自分で確認してください; シーン構造を確認 / 黒やジャンクフレームを探します:
ffmpeg -y -i work/source.mp4 -vf "fps=1/2,scale=240:-1,tile=10x10" -frames:v 1 work/contact.png
ffmpeg -i work/source.mp4 -vf "blackdetect=d=0.1:pic_th=0.98" -an -f null -
単一の固定トーキングヘッドショットの場合、シーンカットはありません — これを想定し、後続のジャンプカットは正常であり、グリッチではないことに注意してください。
4. カットを決定 — edit_coarse.json を手書きで作成
これが編集の核心です。文字起こしと分析を読み、タイムライン全体を隙間や重複なくタイル状に配置する 保持ブロックと削除スパン の大まかな計画を、それぞれ in、out、および一行の reason とともに記述します。ゆっくりした前置きをいくつかの価値の高い断片に圧縮し; 内容を大きなブロックとして保持し; 明確な脱線/駄目を削除します。時系列順を維持します; 正規のV1タイムラインは並べ替えを拒否します。実行時間をおおよそ半分にすることを目指します。
- フォーマット仕様 + 実際の動作例:
reference/edit_coarse.schema.md - すべてのブロック境界を 文/節の終わり に合わせます — 文の途中やどもりで開始/停止しないでください。以下を使用:
これはレガシーのpython scripts/inspect_bounds.py work/edit_coarse.json work/transcript.jsonkeep[]と正規のdecisions[]プランの両方を受け入れ、各境界にまたがる単語を表示するので、それらをきれいな端に移動できます。
5. 展開 → 自動速度 → レンダリング
粗いブロックをタイトで無音のないレンダリングセグメントに変換し、話すペースを正規化してからレンダリングします:
python scripts/build_edit.py work/edit_coarse.json work/transcript.json work/edit_final.json 1.5
python scripts/assign_speed.py work/edit_final.json work/transcript.json # セグメントごとの速度を追加
python scripts/cut_render.py work/edit_final.json work/source.mp4 first_cut.mp4
build_edit.pyは各ブロックを内部のポーズ > SPLIT_GAP(第4引数、例1.5秒)で分割し、すべての境界を無音部分の内部までトリミングします(LEAD 0.10秒 / TRAIL 0.30秒)。これにより、単語がクリップされず、無音が除去されます。SPLIT_GAP を小さくすると、よりタイトで断片的になります; ゆっくりと意図的な話者の場合、0.6ではなく約1.5を推奨します(神経質なマイクロカットを避けるため)。assign_speed.py(自動可変速) — レンダリングセグメントをdecision_refでグループ化し、各手書き保持判断の発話ペースを測定し、共有のspeed係数と各セグメントの結果のout_durをedit_final.jsonに書き戻します。ペースが快適な帯域内にある場合は1.0のまま; 帯域外のペースは最も近い不感帯の端にのみ移動します。隣接する判断ブロック間の差は最大0.08倍で、「ポンピング」結合を避けます。ペースのみを変更します — 保持するコンテンツは決して変更しません。レンダリング前に印刷されたテーブルを読んでください:- 指標: 単語/分(英語) — CJK(zh/ja/ko)の場合は自動的に 文字/分 に切り替わります。
- デフォルトは 初回実行の開始点であり、検証された定数ではありません: 不感帯145–185 WPM、速度は 1.0–1.3 にクランプされます。これはスピードアップのみです: 遅い判断ブロックはタイトにできますが、自然に速い配信は1.0のままです。短い判断ブロック(<3.5秒 / <6単語)は測定ノイズが多すぎるため、グローバル係数を継承します。
- レートは無音除去後に測定されるため、総WPMよりも高く読み取られ、発話レートに近くなります。印刷されたテーブルのレビューは重要です: 目視で確認し、必要に応じて話者に合わせて
deadbandを調整してください。デフォルトを盲信しないでください。 - 調整つまみ(k=v):
mode=segment|global|off(デフォルトsegment;global=動画全体に1つの係数、off=無効)、deadband=lo,hi、min_speed=/max_speed=、min_seg=/min_words=、lang=auto|en|cjk。この機能を完全にスキップするには、このステップを実行しないだけです —cut_render.pyはspeedがない場合を1.0として扱います。
cut_render.pyは N個の独立したseek入力 + concatフィルタ、単一の再エンコード を使用します。これは必須です: 1つのデコードされた入力からN回トリミングすると、ffmpegはデコードされたストリーム全体(長い動画では数十GB)をバッファリングします。reference/pitfalls.mdを参照してください。セグメントがspeedを持つ場合、各入力を再タイミングし(setptsビデオ /atempoオーディオ)、concatの前に入力ごとにfps/timebase/サンプルレートを正規化してA/Vがロックされたままになります; 速度がない場合は、元の正確なグラフにフォールバックします。- 最終比較:
video-edit-compareは正規のタイムラインと実際の最終納品物を読み取ります。最終ピクセルをソース時間に投影し、可変速クリップをソース期間に引き伸ばし、削除された範囲を黒で表示します。
6. 自己チェック — これが通過するまで完了と宣言しないでください
# プロトコル精度リグレッションチェック(リポジトリルートから実行)
python skills/video-cut/scripts/check_project_protocol.py
# 完成したカットを再文字起こし
ffmpeg -y -i first_cut.mp4 -ac 1 -ar 16000 work/selfcheck/cut_audio16k.wav
python scripts/transcribe.py work/selfcheck/cut_audio16k.wav work/selfcheck/cut_transcript
# 内容 + フィラー + 境界チェック
python scripts/verify_cut.py work/edit_final.json work/transcript.json work/selfcheck/cut_transcript.json
# フレーム / 黒 / 結合チェック(結合時間は速度後のout_durを使用するため、正確なまま)
python scripts/selfcheck_frames.py work/edit_final.json first_cut.mp4 work/selfcheck
# A/V同期 + 残留無音なし
ffprobe -v error -select_streams v:0 -show_entries stream=duration -of csv=p=0 first_cut.mp4
ffprobe -v error -select_streams a:0 -show_entries stream=duration -of csv=p=0 first_cut.mp4
ffmpeg -i first_cut.mp4 -af silencedetect=noise=-32dB:d=1.5 -f null -
合格基準:
| チェック | 合格条件 |
|---|---|
| 黒フレーム | なし |
| 結合フレーム | クリーン — 自然な頭ジャンプのみ、黒/フリーズ/破損なし |
| A/V同期 | ビデオとオーディオの長さの差が約1フレーム以内(≈30ms); 可変速オンの場合、合計 ≈ final_duration_s_after_speed |
| 無音 ≥1.5秒 | 0(発話比率は顕著に上昇するはず) |
| 欠落/重複セグメント | なし — すべての主要コンテンツが存在(difflib ~0.90は許容; より低い比率は通常、不明瞭な発話の2パス目の文字起こしノイズであり、欠落ではない — キーワードの存在で検証してからカットのせいにしない) |
| 途中で切れた半文 | なし — 境界は無音部分/文の終わりに位置する |
正直なフィラーの注意点: 段落レベルのカットでは「ほぼフィラーなし」は達成できません。保持された内容は会話調であるため、1分あたりの密度はむしろ上昇することさえあります。これを正直に報告してください; 単語レベルのフィラー除去はP1/P2に属します(そして、単一ショットのトーキングヘッドは目に見えてぎくしゃくします)。
最後に
first_cut_report.md を書いてください: ソースの概要、何をカットしたかとその理由、自己チェックテーブル、そして正直な注意点(360pソースによる品質の上限、フィラー密度など)。可変速が適用された場合は、それを開示してください: 再タイミングされたセグメントの数と速度の範囲(assign_speed.py のテーブル / speed_params から)。これにより、読者はカットが1:1のリアルタイムではないことを理解します。また、verify_cut.py の1分あたりのフィラー数は、スピードアップ後(同じ単語、より短い分)にわずかに高く読み取られることにも注意してください — 報告上のアーティファクトであり、フィラーが追加されたわけではありません。
重要な原則(苦労して得たもの)
- あなたが内容を決め; スクリプトがフレームを決めます。
edit_coarse.jsonを手書きで;build_edit.pyに精密さを任せ、assign_speed.pyにペースを任せます。 - 速度はペースにのみ影響し、内容には影響しません。 自動可変速は、1つの
decision_refからのすべてのレンダリングセグメントに同じ速度を与え、帯域外のペースを最も近い不感帯の端にのみ移動させ、隣接する判断ブロック間の差を0.08倍に制限し、出力を1.0–1.3にクランプします。atempoはピッチを保持します。テーブルをレビュー; 無効にするにはmode=offまたはステップをスキップします。 - 単語を決してクリップしないでください。 境界は常に無音部分内にあります。
- メモリ安全なレンダリングのみ(N個のseek入力 + concat)。1つの入力から複数回トリミングしないでください。
- 断固として行動してください。 合理的な判断を下して先に進み; 過剰に質問しないでください。
- 自己チェックは譲れません。 完了を主張する前に、再文字起こし + フレームチェックを行ってください。
- トラップと修正の完全なリストについては
reference/pitfalls.mdを参照してください。