narrate-video について
動画にタイミングを合わせたナレーションを加えるスキルです。シーン分析から台本作成、音声生成、同期確認まで案内し、映像に沿ったナレーションが必要な制作者向けです。
主な機能
- 01シーンと時刻の分析
- 02時間指定のナレーション作成
- 03AzureまたはGeminiで音声生成
- 04ナレーションと動画の結合
- 05音量・無音区間・同期の確認
活用シーン
- 01製品デモ動画へのナレーション追加
- 02シーンに合わせた草稿台本の調整
- 03指定言語での音声生成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ feiskyer/ video-skills --skill narrate-videoインストール後、Claude Code で/narrate-videoを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/gemini-tts.mdvoices.mdscripts/check_env.pynarration_script_template.py詳細
動画ナレーション
動画にプロ品質のナレーションを追加します。動画を分析し、タイミングを調整した台本を作成または推敲し、Azure TTS または Gemini 3.1 Flash TTS で音声を生成して結合します。音声と映像が同期した、ナレーション付きの動画が完成します。
入力: $ARGUMENTS
追加リソース
- 音声一覧とタイミングの目安:references/voices.md
- Gemini TTS API と AI Studio のリクエスト形式:references/gemini-tts.md
- Python スクリプトのテンプレート:scripts/narration_script_template.py — 動画のディレクトリに
narration_script.pyとしてコピーし、プレースホルダーを埋めてください
フェーズ 0:セットアップ
プロバイダー
ユーザーが Gemini を明示的に希望する場合、または GEMINI_API_KEY がすでに設定されている場合を除き、デフォルトで azure を使用します。Gemini を使用する場合は、references/gemini-tts.md に記載されている公式の Gemini TTS リクエストパターンを使用してください。
言語
使用する言語をユーザーに尋ねます。デフォルトは英語です。references/voices.md で音声と読み上げ速度を確認してください。
環境
# 1. Check provider credentials exist (NEVER read or display their values)
scripts/check_env.py azure
# or
scripts/check_env.py gemini
# 2. Check tool dependencies
command -v ffmpeg && command -v ffprobe && command -v python3
# 3. Check Python dependencies
python3 -c "import dotenv" 2>&1
# 4. Azure only
python3 -c "import azure.cognitiveservices.speech" 2>&1
Azure が選択されており、AZURE_SPEECH_KEY または AZURE_SPEECH_REGION が設定されていない場合は、~/.narrate_video.env に追加するようユーザーに依頼してください。
AZURE_SPEECH_KEY=your-key-here
AZURE_SPEECH_REGION=your-region-here
Gemini が選択されており、GEMINI_API_KEY が設定されていない場合は、~/.narrate_video.env に追加するようユーザーに依頼してください。
GEMINI_API_KEY=your-key-here
# Optional override
GEMINI_TTS_MODEL=gemini-3.1-flash-tts-preview
その後、処理を停止してください。キーは機密情報です。存在するかどうかだけを確認し、値を読み取ったり表示したりしてはいけません。
フェーズ 1:動画の分析
1.1 メタデータ
ffprobe -v quiet -print_format json -show_format -show_streams <video>
動画の合計時間、解像度、フレームレート、音声トラックの有無を記録します。
1.2 シーンの抽出
シーンの切り替わりを特定するため、3~4 秒間隔でフレームを抽出します。
mkdir -p /tmp/narration-frames
for t in $(seq 0 3 <duration>); do
ffmpeg -y -ss $t -i <video> -frames:v 1 -q:v 2 /tmp/narration-frames/frame_${t}s.jpg 2>/dev/null
done
フレームを確認します(画像の表示には Read ツールを使用します)。シーンが切り替わる正確なタイムスタンプを記録してください。タイミングが曖昧な場合は、切り替わりの瞬間を特定できるよう、1~2 秒間隔で追加のフレームを抽出します。
1.3 切り替わりマップ
タイムスタンプと映像の内容を対応させたシーン切り替わりの表を作成します。
0s - Opening screen
3s - User starts typing
8s - System begins processing
34s - Response appears
画面に映っているものを説明するナレーションは、その内容が画面に表示された後に始めてください。映像より音声が先に流れると、視聴者は違和感を覚え、戸惑います。映像が表示されてから少し遅れてナレーションが始まると、プレゼンターが見えている内容を案内しているように感じられ、自然です。
フェーズ 2:台本の作成
形式
各ナレーションセグメントは (start_seconds, text) タプルです。
SEGMENTS = [
(0, "Opening narration here."),
(8, "Next segment narration..."),
]
作成ガイド
タイミング:セグメント間には少なくとも 1 秒の無音を設けてください。この余裕があると、ナレーションが急かされた印象ではなく、会話のように自然に聞こえます。テキストが時間枠に収まるかどうかは、references/voices.md のタイミングの目安を使って推定してください。英語の場合、時間枠(秒)に 2.5 語/秒を掛け、その 80% を安全な語数の上限とします。
流れ:各セグメントが次のセグメントへ論理的につながるようにしてください。「And」「Now」「So」などのつなぎ言葉は役立ちますが、変化をつけてください。「And now」が 3 回連続すると、ロボットのように聞こえます。
入力に応じた調整:ユーザーが草稿を提供した場合は、シーン分析に基づいてタイムスタンプを調整し、時間枠を超えるテキストを削り、表現を整えてください。ただし、ユーザーの意図と重要なポイントは維持してください。草稿がない場合は、画面に映っている内容をもとに各シーンのナレーションを作成します。
Gemini プロンプトの注意点:Gemini を使用する場合は、スタイルの指示と読み上げる台本を分けてください。Gemini 3.1 Flash TTS は、メタデータを読み上げたり、曖昧なプロンプトを拒否したりすることがあるため、スクリプトテンプレートではテキストを安全な TRANSCRIPT: の前置きで囲んでいます。
生成前の確認
音声を生成する前に、各セグメントが時間枠に収まることを確認してください。
window = next_segment_start - this_segment_start
max_words = window * words_per_second * 0.8
セグメントが長すぎる場合は、この段階でテキストを短くしてください。単語を削るほうが、音声を再生成するよりはるかに低コストです。
フェーズ 3:スクリプトを生成する
scripts/narration_script_template.py を動画のディレクトリに narration_script.py としてコピーします。以下を設定します。
TTS_PROVIDERをazureまたはgeminiに設定します- プロバイダー別の表から
VOICE_NAMEを選びます INPUT_VIDEOとOUTPUT_VIDEOを設定します(相対パスのみ)- フェーズ 2 の内容を
SEGMENTSに設定します
設計上の注意点
これらの選択は、実際の本番環境での問題をデバッグした経験に基づいています。
- amix での
normalize=0:ffmpeg のamixは、デフォルトで入力数に応じて音量を分割します。セグメントが 20 個ある場合、出力音量は 1/20 になり、ほぼ無音になります。 - 元の音声を破棄する:元の音声を音量 5%で混ぜても、二重音声の不自然なノイズが聞こえます。
- 重なりがある場合は中止する:セグメントの音声が次のセグメントの開始時刻を過ぎていると、スクリプトは停止し、問題を報告します。音声が重なると不自然に聞こえます。
- 既存の音声ファイルをスキップする:スクリプトは、キャッシュ済みファイルが存在しないセグメントの音声のみを生成します。Azure は
.mp3、Gemini は.wavを使用します。セグメントのテキストを変更した場合は、再実行する前に対応するseg_XXX.*ファイルを削除してください。 - Gemini の再試行ロジック:Gemini 3.1 Flash TTS では、一時的な
500エラーが発生することがあります。テンプレートは失敗する前に自動で数回再試行します。
フェーズ 4:実行と調整
python3 narration_script.py
タイミングレポートに重なり(間隔 < 0)が表示された場合は、テキストを短くするか、次のセグメントの開始時刻を遅らせるかを判断します。テキストを変更した場合は、まず narration_segments/ 内の対応するキャッシュ済み音声ファイルを削除してください。開始時刻だけを変更した場合は、そのまま再実行します。
すべての間隔が 0 以上になるまで調整を続けます。
フェーズ 5:検証
ビルドが成功するたびに、次の 3 つのチェックをすべて実行します。
音量
ffmpeg -i <output> -ss 0 -t 30 -af "volumedetect" -vn -f null - 2>&1 | grep -E "mean_volume|max_volume"
mean_volume が -25~-15 dB、max が -10~0 dB になることを確認します。mean が -40 dB 未満の場合は、normalize=0 の修正が適用されていません。フィルター文字列を確認してください。
無音区間
ffmpeg -i <output> -af "silencedetect=noise=-30dB:d=0.3" -vn -f null - 2>&1 | grep -E "silence_(start|end)" | head -20
セグメントの切り替わりで、きれいに無音になっていることを確認します。無音の境界は、想定されるセグメントの終了時刻と開始時刻に一致している必要があります。
音声と映像の同期
主要なセグメントの開始時刻から 5~8 箇所でフレームを抽出し、確認します。
for t in <timestamps>; do
ffmpeg -y -ss $t -i <output> -frames:v 1 -q:v 2 /tmp/verify_${t}s.jpg 2>/dev/null
done
そのシーンのナレーションが始まる時点で、画面上の内容がすでに表示されている必要があります。
トラブルシューティング
| 症状 | 原因 | 対処方法 |
|---|---|---|
| 2 つの声が再生される | 元の音声がミックスされている | [final] の音声トラックのみをマップし、0:a はマップしないでください |
| 音声がほぼ無音 | amix が入力数に応じて音量を分割している | amix のパラメーターに :normalize=0 を追加してください |
| ナレーションが同期していない | シーンのタイムスタンプが不正確 | 問題のある箇所の前後で、1~2 秒間隔でフレームを再抽出してください |
| セグメントの境界で音声が重なる | 前のセグメントが長すぎる | そのセグメントのテキストを短くするか、次のセグメントを遅らせてください |
| テキストを変更したのに音声が変わらない | 古いキャッシュ済み音声ファイルが残っている | narration_segments/seg_XXX.* を削除して再実行してください |
| 動画の最後で音声が途切れる | 最後のセグメントが動画の長さを超えている | 動画の終了 3~4 秒前に終わるよう短くしてください |
Gemini が 500 を返す | プレビューモデルが音声ではなくテキストトークンを出力した | 再実行してください。テンプレートは一時的な失敗をすでに再試行します |
| Gemini がプロンプトのラベルを読み上げる | プロンプト分類に失敗した、またはプロンプトが曖昧すぎた | トランスクリプトを明確に記述し、テンプレートの TRANSCRIPT: ラッパーを使用してください |