mcpField
V
video-edit

video-edit

github.com/melvynxクリエイティブ・メディア7051 回インストール1 週間前に更新
リポジトリ

video-edit について

video-editは、録画素材を縦型ショート動画または横型のYouTube動画に仕上げるスキルです。文字起こし、カット選定、字幕や画面要素の追加、品質確認、レンダリング、納品までを案内します。

主な機能

  1. 01元動画の確認と準備
  2. 02単語ごとの時刻付き文字起こし
  3. 03無音や撮り直しに基づくカット作成
  4. 04字幕、フックカード、コールアウトの追加
  5. 05レンダリング前の静止画確認
  6. 06動画、字幕、投稿文の書き出し

活用シーン

  1. 011回の録画から縦型ショートを作成
  2. 02横型のYouTube動画を制作
  3. 03別の編集ソフト向けにSRT字幕を作成
  4. 04保存済みの編集データを使って動画を修正

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/melvynx/video-agent-skills --skill video-edit

インストール後、Claude Code で/video-editを入力して読み込まれたか確認します。

同梱ファイルreferences/remotion-short.mdreferences/style.md

詳細

動画編集

未編集のテイクを1つ用意すると、完成動画が1本書き出されます:video-agent/edits/<slug>/final.mp4。縦型ショート、またはYouTube向けの横型フル動画のどちらかです。

ツールは無料で、クリエイターのマシン上で実行されます。

  • Remotion(Reactで動画を作成)と、単語ごとのタイミング情報付きのローカル音声テキスト変換機能(whisper.cpp)を備えた公式TikTokテンプレート。
  • チェックと音声には ffmpeg を使用します。

ルックは固定でシンプルです:references/style.md。描画するコードはreferences/remotion-short.mdにあります。

実行方法

  • 事前確認:node -v && ffmpeg -version && git --version。何かが見つからない場合は停止し、/video-setup tools に案内します。
  • クリエイターの言語で話します。長い各工程(インストール、文字起こし、レンダリング)の前に、何をするかと所要時間を1行で伝えます。
  • 言語とアクセントカラーは video-agent/voice.md で確認します。音声ファイルがない場合は両方を尋ねるか、英語と #FFD400 を使います。
  • 名前付きスタイル(依頼に style <name> とあり、/video-steal で作成されたもの)がある場合は、video-agent/styles/<name>.md を読み、style.mdに重ねて適用します。そちらの値を優先し、記載されたとおりに効果音、ステッカー、カラーグレーディングを編集に反映します。名前のない項目はすべてstyle.mdの設定のままにします。
  • <slug>:小文字、ハイフン区切りで、ドットやスペースは使いません(文字起こしツールは最初のドットでファイル名を切り詰めます)。
  • 手順1と2はプロジェクトのルートから実行します。手順3以降のコマンドはすべて video-agent/studio/ で実行し、パスはそのディレクトリからの相対パスです。

1. テイクと形式

指定されたファイルを使います。指定がなければ、video-agent/takes/ 内の最新ファイルを使います。確認します:

ffprobe -v error -show_entries format=duration:stream=codec_type,codec_name,width,height,r_frame_rate,avg_frame_rate,color_transfer -of compact "<take>"
  • 音声ストリームがない場合は停止して、その旨を伝えます。
  • color_transfer が smpte2084 または arib-std-b67 の場合はHDRテイクです(iPhoneのデフォルト設定)。変換しないでください。レンダリング時にRemotionが通常の色にマッピングします。次回の撮影に備え、カメラ設定でHDRビデオをオフにするよう伝えます。
  • それ以外で、ファイルが .webm の場合、または可変フレームレートの場合(r_frame_rate と avg_frame_rate が異なる)は変換します。Remotionとffmpegのカット処理はいずれも、これらのファイルではずれが生じます。以降は新しいファイルをテイクとして使います:
    ffmpeg -y -i "<take>" -vf fps=30 -c:v libx264 -crf 18 -preset veryfast -pix_fmt yuv420p -c:a aac -b:a 192k -ar 48000 "video-agent/takes/<slug>-30fps.mp4"
    

次に形式を決めます。形式が指定されている場合(short、long、「YouTube向け」、「リール」など)は、その指定に従います。指定がなければテイクを見て選び、どちらにしたかを1行で伝えます:

形式構成条件デフォルト
ショートShort、1080x1920テイクが90秒未満、またはショート、リール、TikTokを希望している場合字幕あり、フックカードあり、8〜10秒ごとにコールアウト
ロングWide、1920x108090秒を超える横型テイク、YouTube向けフル動画、チュートリアル、Vlogの場合字幕あり(確認する:YouTube視聴者は.srtのみを好むことが多いため、showCaptions: false)、フックカードは希望がない限りなし、コールアウトは30〜60秒ごとを上限とし、チャプターあり
  • ショートで横型テイクを使う場合は、中央をクロップします。フレームの中央に映っていない場合は注意を伝えます。
  • ロングで縦型テイクを使うと、細い帯状にクロップされます。これは避け、ショートを提案します。
  • ロングの場合は、まず所要時間を伝えます。ノートPCで medium モデルを使うと、文字起こしには動画の長さとほぼ同じ時間がかかります(20分のテイクなら約20分、small は2〜3倍速)。レンダリングには動画1秒あたり3〜5秒かかります(15分の動画なら約1時間)。どちらもバックグラウンドで実行されます。

以下の <comp> は Short または Wide です。

元になったスクリプトを探します:video-agent/scripts/ 内で一致するスクリプト(slugが同じか、1行目が同じもの)を使用します。スクリプトがなくても問題ありません。その場合、カットは文字起こしのみに基づいて行います。

2. スタジオ(初回のみ)

まず、パッケージに約1 GB、初回の文字起こし時に音声エンジンとそのモデルに0.5〜1.5 GB、さらにセットアップに数分かかることを伝えてください。

git clone --depth 1 https://github.com/remotion-dev/template-tiktok video-agent/studio
cd video-agent/studio
npm i
  • video-agent/studio/.git を削除し、プロジェクト内の通常のフォルダーにします。
  • whisper-config.mjs で言語を設定します。
    • 英語:WHISPER_MODEL = "medium.en" と WHISPER_LANG = "en" のままにします。
    • それ以外の言語:WHISPER_MODEL = "medium"(低速なマシンでは "small"(466 MB))にし、WHISPER_LANG には言語コード("fr"、"es"、"de"、"pt" など)を設定します。
  • Short と Wide のコンポジション、およびフォントパッケージを追加します。references/remotion-short.md のセクション1〜3に従い、その後 npx tsc --noEmit を実行します。Wide がまだ存在していなかった頃に作成したスタジオの場合は、現在のセクション2と3の内容で src/Short/index.tsx と src/Root.tsx を置き換えます。
  • 任意:Claude に Remotion コードを改善してもらうには、npx skills add remotion-dev/skills を実行します。

3. 文字起こし

cp "../takes/<take file>" public/<slug>.mp4
node sub.mjs public/<slug>.mp4

public/<slug>.json に、単語ごとのエントリ({ "text": " word", "startMs", "endMs", "confidence" })を書き込みます。JSON がすでに存在するファイルはスキップされます。再度文字起こしするには、JSON を削除してください。

聞き間違えた単語(名前、ブランド名、数字)はスクリプトを参照して修正します。変更するのは text のみで、時刻は変更しないでください。

4. カットリスト

実際の無音部分を検出してから、単語間の間隔を一覧表示します。

ffmpeg -nostats -i public/<slug>.mp4 -af silencedetect=noise=-35dB:d=0.3 -f null - 2>&1 | grep silence_
node -e 'const w=require("./public/<slug>.json"),G=350,B=100,A=150,s=(ms)=>(ms/1000).toFixed(2);let r=[{fromMs:Math.max(0,w[0].startMs-B)}];for(let i=1;i<w.length;i++){const g=w[i].startMs-w[i-1].endMs;if(g>G){console.error(`gap ${s(w[i-1].endMs)} -> ${s(w[i].startMs)} (${g} ms) after "${w[i-1].text.trim()}"`);r[r.length-1].toMs=w[i-1].endMs+A;r.push({fromMs:w[i].startMs-B})}}r[r.length-1].toMs=w[w.length-1].endMs+300;console.log(JSON.stringify(r))'

2つ目のコマンドは、0.35秒を超えるすべての間隔と、その直前の単語を出力し、続けて style.md のマージンを適用した維持対象の ranges を出力します。その後、ranges を修正します。

  1. silencedetect で検出されない間隔(息継ぎ、声の小さい単語、音楽)は、該当する2つの range を再び結合します。
  2. 撮り直し:スクリプトを1行ずつ確認します。行(または最初の3単語)が複数回登場する場合は、最後の完全なテイクを残し、最初のテイクの先頭の単語から、残すテイクの開始位置までの ranges を削除します。
  3. 言い直しの前にある言いよどみ(「えー」や途中までの単語)、フックより前の部分、台本にない会話を削除します。

長尺動画:ルールは同じですが、0.35秒ではなく0.5秒を超える間隔を使います(G=500)。長尺動画には適度な間が必要なので、考え込む間は残してもかまいません。内容のある補足は残し、話がそれるものはカットします。台本がない場合、撮り直しとは同じ文が連続して2回話されていることです。後の方を残します。

レンダリングの前に、結果を数行で表示します。

Take 1:42 → short 0:47 (-55 s)
- 38 pauses cut (41 s)
- 3 retakes dropped: line 2 (x2), line 7
- dropped "ok let me do that again" at 0:58

5. モーション

スクリプトのビジュアルメモを参考にします(メモがない場合は、自分で選びます)。

  • フックカード:画面上に表示するフックは6語以内とし、*accent* の単語を1つ含め、表示時間は2〜3秒にします。
  • コールアウト:話者が口にする数字、結果、ツール名を表示します。8〜10秒ごとに1つを目安にし、4秒以内に2つ表示しないでください(長尺の場合は、重要な数字に限り、最大30〜60秒ごとに1つ)。atMs は、JSON 内にある該当単語の startMs です。

public/<slug>.edit.json を作成します(形式は remotion-short.md のセクション4を参照)。

6. QC を行ってからレンダリング

静止画を3〜4枚レンダリングします。フック(フレーム30)、コールアウト、動画中盤の字幕、最後の1秒を選びます(長尺の場合は、動画全体にわたって6〜8枚を選びます)。Short のみ:各画像にプラットフォームのセーフゾーンを重ねます。

npx remotion still <comp> out/<slug>-qc-30.png --frame=30 --props=public/<slug>.edit.json
ffmpeg -y -v error -i out/<slug>-qc-30.png -vf "drawbox=x=0:y=0:w=iw:h=230:[email protected]:t=fill,drawbox=x=0:y=1440:w=iw:h=480:[email protected]:t=fill,drawbox=x=850:y=900:w=230:h=540:[email protected]:t=fill" out/<slug>-qc-30-safe.png

各 -safe PNG を確認します(長尺の場合は各静止画)。赤いゾーン(アプリのボタン、名前、説明文で覆われる部分)にテキストがないこと、字幕が顔より下にあること、フックカードが読みやすいこと、コールアウトが目を覆っていないこと、アクセントカラーが適切であることを確認します。編集ファイルを修正してから、次を実行します。

npx remotion render <comp> out/<slug>.mp4 --props=public/<slug>.edit.json

ノートパソコンでは、動画1秒あたり約3〜5秒のレンダリング時間がかかります。バックグラウンドで実行し、所要時間を伝えてください。

7. 音声

音楽(依頼された場合のみ。YouTube Audio Library などのロイヤリティフリーのもの)は最初に追加し、音声より22 dB低くします。

ffmpeg -y -i out/<slug>.mp4 -stream_loop -1 -i "<music file>" -filter_complex "[1:a]volume=-22dB[m];[0:a][m]amix=inputs=2:duration=first:normalize=0[a]" -map 0:v -map "[a]" -c:v copy -c:a aac -b:a 192k out/<slug>-music.mp4

次に、レンダリング結果を音量調整します(音楽がある場合は out/<slug>-music.mp4)。Remotion は音声の音量を調整しません。また、単純な loudnorm の処理では、これほど短いクリップは目標より2〜3 LU小さくなります。まず測定します。

ffmpeg -nostats -i out/<slug>.mp4 -af ebur128=peak=true -f null - 2>&1 | grep -E "^\s+(I|Peak):"

ゲインは、-14 から I: の値を引いた値です(例:I = -17.0 → 3.0dB)。リミッターも適用します。

mkdir -p ../edits/<slug>
ffmpeg -y -i out/<slug>.mp4 -c:v copy -af "volume=<gain>dB,alimiter=limit=0.8:level=false" -c:a aac -b:a 192k -ar 48000 ../edits/<slug>/final.mp4

同じ方法で final.mp4 を測定します。I が -15〜-13、Peak が -1 dBFS 未満であれば適切です。

8. 納品

video-agent/edits/<slug>/ に次のファイルを用意します。

  • 投稿できる状態の final.mp4。
  • captions.srt:カット後のタイムラインに合わせた字幕です。1つのキューにつき最大7語とし、各文の後に新しいキューを作成します。プラットフォームではクローズドキャプションとして読み込まれます。
    node -e 'const e=require("./public/<slug>.edit.json"),w=require("./public/"+e.captions),F=30,fr=(ms)=>Math.round(ms/1000*F);let at=0;const p=e.ranges.map(r=>{const f=fr(r.fromMs),n=Math.max(1,fr(r.toMs)-f),q={...r,s:(at-f)/F*1000};at+=n;return q});const o=w.flatMap(x=>{const q=p.find(q=>x.startMs>=q.fromMs&&x.startMs<q.toMs);return q?[{t:x.text.trim(),a:x.startMs+q.s,b:Math.min(x.endMs,q.toMs)+q.s}]:[]});const ts=(ms)=>new Date(Math.max(0,Math.round(ms))).toISOString().slice(11,23).replace(".",",");let c=[],n=0,out="";const flush=()=>{if(c.length)out+=`${++n}\n${ts(c[0].a)} --> ${ts(c[c.length-1].b)}\n${c.map(x=>x.t).join(" ")}\n\n`;c=[]};for(const x of o){c.push(x);if(c.length===7||/[.?!]$/.test(x.t))flush()}flush();process.stdout.write(out)' > ../edits/<slug>/captions.srt
    
  • edit.json:後から動画を作り直せるように、public/<slug>.edit.json のコピーを保存します。
  • post.md:投稿時に貼り付けるテキストです(下記参照)。
  • 長尺動画のみ、post.md にYouTubeの説明欄用のチャプターを記載します。話題が変わる箇所に1〜3分ごとに設定し、時刻はカット後のタイムライン(captions.srt を参照)に合わせます。最初のチャプターは 0:00 とし、少なくとも3つ、それぞれ10秒以上にします。

次に、動画の長さ、カットした内容、ファイルの保存場所を伝え、変更案を1つ提示します(「フックカードを短くしますか? コールアウトを減らしますか? 別のアクセントカラーにしますか?」)。変更する場合は、JSONを編集して再レンダリングします(手順6と7)。次は /video-publish <slug> を実行します。

投稿文

動画のスクリプトと voice.md をもとに、動画と同じ言語で post.md を作成します。

  • タイトル(YouTube Shorts):60文字未満にし、最初の3語にトピックを含めます。長尺動画の場合は70文字未満にし、動画の内容に忠実な、動画が約束する内容を表します。
  • キャプション:最初の125文字だけでも意味が通るようにします(アプリでは「もっと見る」の前で省略されます)。続けて短い行を1〜2行と、voice.md にある依頼を1つ記載します。
  • 検索語:視聴者がこの動画を見つけるために入力する2〜3語を、タイトルか最初の行に自然に含めます。
  • ハッシュタグ:末尾に、トピックに関するものを3〜5個記載します。#fyp や #viral は使いません。
  • 動画で述べていない内容は加えません。新たな数値、主張、約束は含めません。

このスキルがインストールされている場合は、video-script の humanize パス(references/humanize.md)を実行します。

Title: Your hook is too long
Caption: Nobody hears word 12 of your hook. Cut it to 12 words and put the result first.
Try it on your next three videos.
Follow for one script fix a day.
#shortformvideo #videohooks #contentcreator

CapCut、Premiere、またはRemotionを使わない場合

自分のエディターを使いたいクリエイターもいます。また、Remotionは従業員が3人を超える企業では有料ライセンスが必要です。手順1〜4を実行し、範囲を指定した public/<slug>.edit.json("hook": null、"callouts": [])を作成します。手順5〜7は省略し、手順8の captions.srt と、次のいずれかを渡します。

  • 手作業でカットするための範囲を記載した cuts.md:
    mkdir -p ../edits/<slug>
    node -e 'const e=require("./public/<slug>.edit.json"),t=(ms)=>new Date(ms).toISOString().slice(14,23);for(const r of e.ranges)console.log(t(r.fromMs)+" → "+t(r.toMs))' > ../edits/<slug>/cuts.md
    
  • または、ffmpegだけを使ったシンプルなカット:
    SEL=$(node -e 'const e=require("./public/<slug>.edit.json");console.log(e.ranges.map(r=>`between(t,${r.fromMs/1000},${r.toMs/1000})`).join("+"))')
    ffmpeg -y -i public/<slug>.mp4 -vf "select='$SEL',setpts=N/FRAME_RATE/TB" -af "aselect='$SEL',asetpts=N/SR/TB" -c:v libx264 -crf 18 -c:a aac ../edits/<slug>/cut.mp4
    

制約事項

  • whisper.cpp は名前や専門用語を聞き間違えます。スクリプトで修正し、数値は目視で確認してください。
  • カットの精度は高いものの、完璧ではありません。息継ぎや語尾の小さな音が途切れることがあります。投稿前に一度、結果を確認してください。
  • Claudeは収録音声を聞けず、タイミングと silencedetect を信頼します。音質の悪い音声(音楽、エコー、複数人の声)では、どちらの精度も下がります。
  • セーフゾーンはTikTok、Reels、Shortsの中間に合わせたものです。各アプリではボタンの位置が少しずつ異なります。
  • Remotionのライセンス:個人、従業員3人以下の企業、非営利団体は無料です。それを超える企業には企業向けライセンスが必要です(remotion.pro/license)。

コメント