mcpField
S
short-editing

short-editing

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

short-editing について

short-editing は、撮影素材から完成した短尺動画を作るクリエイター向けスキルです。単一の仕様ファイルでローカルレンダラーを動かし、文字起こし、カット、モーション、字幕、音声ミックス、品質確認を行います。

主な機能

  1. 011 本の素材から短尺動画を作成
  2. 02音声を文字起こししてカット時刻を対応
  3. 03シーン、字幕、モーションを設定
  4. 04動画をレンダリングして音声をミックス
  5. 05静止画を生成して品質を確認
  6. 06動画、ポスター、字幕ファイルを出力

活用シーン

  1. 01撮影素材を短尺動画に編集
  2. 02無音や言い直しを取り除く
  3. 03字幕、音楽、効果音を追加
  4. 04縦型または正方形の動画を確認

インストール

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

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

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/
looks.json
assets/icons/
build-icons.mjs
assets/music/
tracks.json
engine/
camera.mjs
audio.mjs
chrome.mjs
faces.swift
images.mjs
engine/bin/darwin/
faces.hash
matte.hash
faces
matte
engine/lib/
chill-synth.js
chill.js
cues.js
encode.js
player.js
chill-styles.js
dsp.js
runtime-yt.js
kit.js
runtime.js
scenes-crash.js
scenes-clean.js
scenes-faceless.js
scenes-master.js
scenes-story.js
scenes-life.js
scenes-meta.js
style.js

詳細

翻訳後に原文が更新されています。最新の内容は原文をご確認ください。

ショート動画の編集

素材のワンテイクから、完成動画を1本作成します:video-agent/edits/<slug>/final.mp4。縦型ショート動画、またはYouTube向けの横型フル動画のいずれかです。

ツールは無料で、制作者のマシン上で動作します。

  • Remotion(Reactで動画を作成)と、単語ごとのタイミング情報を含むローカル音声文字起こし機能(whisper.cpp)を搭載した公式TikTokテンプレート。
  • チェックと音声処理にはffmpegを使用します。

見た目は固定でシンプルです:スタイル.md。描画コードはRemotionショート動画.mdにあります。

実行方法

  • 実行前チェック:node -v && ffmpeg -version && git --version。何かが不足している場合は、そこで止めて /studio-setup tools に案内します。
  • 制作者の言語で話します。インストール、文字起こし、レンダリングなど、時間のかかる各ステップの前に、何をするかと所要時間を1行で伝えます。
  • 言語とアクセントカラーを確認するため、video-agent/voice.mdを読みます。音声ファイルがない場合は両方を尋ねるか、英語と#FFD400を使用します。
  • 名前付きスタイル(リクエスト内で style <name> と指定され、/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、1920x1080横型素材が90秒を超える場合、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)。次に実行するのは /publish <slug> です。

投稿文

動画の台本と voice.md をもとに、動画と同じ言語で post.md を作成します。

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

そのスキルがインストールされている場合は、studio-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 を使わない場合

自分の編集ソフトを好むクリエイターもいます。また、従業員が4人以上の企業が Remotion を使うには有料ライセンスが必要です。手順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)。

コメント