Communitygithub.com

heavenchenggong/demo-video-subtitler

製品デモやUIウォークスルーの画面録画に、スタイル付きで適切に配置された字幕を焼き込み、それに対応するキャプション原稿を生成します。

demo-video-subtitler とは?

このスキルは、製品デモ動画に字幕を追加するための完全なパイプラインを提供します。画面録画のフレームごとの分析、UIテキストの抽出、読み速度予算に基づいたキャプション原稿の作成、白文字に紫の輪郭とソフトなハローを施したスタイル設定、フォーカス追従型の配置、ffmpeg/libassを使った焼き込み、フレームレベルの検証までを行います。日本語、中国語、韓国語を含むあらゆる言語に対応し、特にCJKフォントファミリーの選択や行間調整に注意を払います。このスキルはClaude Code、Cursor、CodexなどのAIエージェント上で動作し、単一の`bootstrap.sh`スクリプトでffmpeg(libass対応)やフォントなどの依存関係を自動インストールします。また、`-ss`の位置によるフィルター無効化やダウンスケールフレームの座標誤差、シーン遷移を跨ぐキャプションといった、開発者にとって厄介なトラップを回避するための実践的な対策も盛り込まれています。

対応✓Claude Code~Codex CLI~Cursor
npx skills add heavenchenggong/demo-video-subtitler

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Demo Video Subtitler

問題

スクリーンレコーディングによる製品デモは、製品自体とは無関係な3つの理由で視聴者に伝わりません。UIテキストが間違った言語である、文字が小さすぎて読めない、そして動画にナレーションがない(無音録画)ことです。視聴者はクリックが発生しているのを見ても、その理由を理解できません。字幕はそれを解決します——ただし、それらが複雑なUI上でも読みやすいスタイルで、デモ中の部分を隠さない場所に配置され、画面切り替えで途切れないタイミングで表示されている場合に限ります。

このスキルは、困難な方法で発見すると何時間も費やすことになる、明白でない障害モードを含む完全なパイプラインです。

使用するタイミング

  • 「このデモ動画に字幕を追加して」/「この録画に韓国チーム向けのキャプションを付けて」
  • 無音のスクリーンレコーディング(.mp4、音声ストリームなし、またはBGMのみ)で、ストーリーをキャプションテキスト自体で伝える必要がある場合
  • 英語のみの製品デモを非英語圏の視聴者に見せる場合
  • 既存の字幕があるが、中央下部に固定されていてデモパネルを覆っている場合——フォーカス追従型の配置が必要
  • 動画に合わせてプレゼンターが読むための読み上げ台本を生成する場合(「関連ユースケース」参照)

このスキルを使用しないでください:合成音声による吹き替え(それはTTSパイプライン)、またはプラットフォーム上に既に存在するキャプションの抽出。

インストール(1コマンド)

scripts/bootstrap.sh --dry-run          # 確認のみ:不足しているものを正確に表示
scripts/bootstrap.sh                    # 不足しているものをインストール(1回確認あり)
scripts/bootstrap.sh --yes --lang ja    # 無人実行、日本語フォントも準備

bootstrap.sh は冪等です——既に準備が整っているマシンでは何もしません。プラットフォームを検出し、適切なインストーラー(Homebrew / apt / dnf / pacman / winget / scoop)を選択し、パッケージマネージャーが利用できない場合は静的ffmpegを ~/.local/bin に配置します。キャプションを焼き付けるセッションの最初のアクションとして実行してください。スキルにはインストール時のフックがないため、このスクリプトがインストーラーとなります。

前提条件

最初に組み込みチェックを実行します——マシンを検査し、不足しているものを正確に表示します。

scripts/doctor.sh "Hiragino Sans GB"     # 使用予定のCJKフォントファミリーを指定

チェック内容と、不足している場合のインストールコマンド:

要件理由インストール方法
libass 付きでビルドされた ffmpeg.ass キャプションを焼き付ける(-vf ass=...)macOS brew install ffmpeg; Windows: gyan.dev full ビルド; Debian apt install ffmpeg
ffmpeg ビルドの --enable-fontconfigフォントファミリー名で解決する。これがないと無言でフォールバックフォントになる上記と同じビルドに含まれている
システムが解決できるCJKフォントファミリーキャプションテキストそのものmacOS: Hiragino Sans GB(プリインストール)· Windows: Microsoft YaHei(プリインストール)· Linux: apt install fonts-noto-cjk
python3(任意)export_cues.py のみ。標準ライブラリのみ、pipインストール不要macOS: xcode-select --install; Linux: apt install python3
bash, zip(任意)スクリプト、および配布用のパッケージングほぼどこでもプリインストール済み

Node、ImageMagick、pipパッケージ、APIキーは不要です。視覚認識可能なモデル(エージェント自身)がフレームを読み取ります——それはインストールするものではありません。

厄介な2つの環境上の注意点:

  • ffmpeg を PATH に通す、またはスクリプト用にその場所をエクスポートする:FFMPEG=/path/to/ffmpeg scripts/burn.sh ...。
  • Windows:libassはフォント解像にfontconfig/DirectWriteを使用します(ビルドによります)。キャプションが間違った書体でレンダリングされる場合は、fontconfigをインストールし(scoop install fontconfig またはMSYS2)、fc-match "Microsoft YaHei:bold" で再確認してください。

パイプライン

上から下へ作業します。フェーズ2をスキップしないでください——以降のすべてのフェーズはUIジオメトリを知っていることに依存します。

フェーズ1——インベントリ(2分)

scripts/probe.sh INPUT.mp4

記録:再生時間、解像度、fps、ビットレート、音声ストリームの有無。スクリプト内で音声チェックを実行します(平均音量+無音マップ)。

音声チェックからの判断:

音声の状態字幕の種類
音声ストリームなしナレーション字幕——あなたがストーリーを執筆する(「何が起きているか/なぜそれが重要か」)
音声はあるが発話なし(音楽のみ)同上。10秒間のサンプルを文字起こしして確認:大きな音声で空またはほぼ空を返すASRツールは、音楽であって音声ではないことを意味する
発話あり会話字幕——まずタイムスタンプ付きで文字起こしし、その後クリーンアップする。コピーを創作しない

フェーズ2——視覚分析とジオメトリ(人々がスキップするフェーズ)

2 fpsでフレームを抽出します(60秒のクリップ→約120フレーム。まず4フレームごとに全体の流れを確認し、その後テキストにズームイン):

scripts/split_frames.sh INPUT.mp4 frames/ 2

キャプションを1つ書く前に、3つの成果物を作成します。

  1. ビートシート —— | 時間 | 画面上の変化 | 画面上のテキスト(原文まま) |。「ビート」とは任意の変化:通知の表示、パネルのスライドイン、ボタンのクリック、スクロール、ページ切り替え、トランジション。トランジションの種類(白フラッシュ、カット、黒)をメモする——キャプションはそれらをまたいではならない。

  2. UIテキスト、原文まま、ソース解像度から読み取る。 ダウンスケールされたプレビューフレームは小さなUIテキストをぼかし、誤って書き写すことになる。元のファイルからクロップする:ffmpeg -i IN.mp4 -ss 12.5 -frames:v 1 -vf "crop=W:H:X:Y,scale=2*iw:-1" out.jpg(フレーム正確な抽出のために -ss は -i の後。トラップ参照)。

  3. ジオメトリマップ ——「ライブ」パネル(デモ対象)のピクセル境界をソースピクセルで測定する。目視ではなく、実際に測定する。

    scripts/find_edge.sh INPUT.mp4 30 2880 300     # t=30s、x=2880から300px幅のストリップ
    

    次に返されたストリップ画像を読み取り、パネルの左端がストリップ内のどこに収まるかを確認する。そのオフセットをストリップのxに加算して絶対的な端を得る。レイアウトが変わる場合は、他のシーンでも繰り返す(例:後でチャットパネルが開くなど)。

ジオメトリマップに従うルール: キャプションはデモパネルに隣接する自由領域に配置され、決してその上には置かれない。フェーズ5参照。

フェーズ3——キャプション原稿の作成

2つのルールがすべてを支配します。

  • 1つのキューにつき1つのアイデア。 最大2~3行の短い行。キューは段落ではなく文です。
  • 読む速度の予算 CJK:毎秒3.5~5.5文字が快適。6以上は慌ただしく、3未満は間延びします。ラテン文字:毎秒12~18文字。計算し、推測しないでください。scripts/export_cues.py は .ass ファイルができればキューごとの密度を表示します。その数値に照らして繰り返し調整します。

キューのタイミングルール:

  • キューは視覚的なビートが着地するのと同時に開始します。通知が1.0秒にスライドインする場合、キューは1.0秒に開始します。
  • キューはトランジション(白フラッシュ/カット)の前に終了し、少なくとも0.8秒の無音時間を挟みます。トランジションをまたぐキャプションはグリッチのように読み取られます。
  • 適切な間隔を空けます。1分あたり3~5秒の空白があると、動画に呼吸が生まれます。壁一面のキャプションは視聴者を疲弊させます。
  • ナレーション字幕の場合、キャプションには理由を入れ、仕組みは入れません:「エージェントが自発的に到達した」は「右上隅に通知が表示された」よりも優れています。

ここで一旦停止し、コピーを承認してもらいます。 テキスト承認→スタイリング。スタイリング後にテキストが却下されると、無駄な再レンダリングが発生します。

フェーズ3b——キャプション言語の選択

パイプラインは言語に依存しませんが、スタイリング前に言語ごとに3つのことを決定する必要があります。

1. フォントファミリー ——プラットフォームが解決できるものを選択します(scripts/doctor.sh <lang> で確認):

キャプション言語macOSWindowsLinux
中国語 (zh)Hiragino Sans GB / PingFang SCMicrosoft YaHeiNoto Sans CJK SC
日本語 (ja)Hiragino SansYu Gothic / MeiryoNoto Sans CJK JP
韓国語 (ko)Apple SD Gothic NeoMalgun GothicNoto Sans CJK KR
ラテン文字 (en/fr/de/es…)Helvetica NeueSegoe UIInter / DejaVu Sans

2. 読む速度 ——予算はスクリプトに依存します。scripts/export_cues.py は自動的に適切なものを選択し(--lang で上書き可能)、超過したキューにフラグを立てます。

スクリプト快適速すぎとしてフラグ
漢字/仮名 (zh, ja)毎秒3.5~5.5文字> 6.0
ハングル (ko)毎秒4.0~6.0文字> 6.5
ラテン文字毎秒12~18文字> 19

3. 行の折り返し ——中国語と日本語には単語間のスペースがありません。句読点(U+FF0C, U+3001, U+3002, U+FF1A)で区切り、製品名は決して分割しません。韓国語とラテン文字はスペースで区切ります。韓国語の助詞は名詞に付けたままにします。行が収まらない場合は、フォントを縮小する前に折り返します——小さな書体は、行を追加するよりも可読性を大きく損なう可能性があります。

4. スクリプトごとの行幅 ——グリフの幅はスクリプトによって異なるため、同じ Fontsize で異なる行長になります。行の予算は floor(available_px / (fontsize * k)) で計算します。ここで k は、漢字で約1.0、ハングルと仮名で約1.05~1.1(これらは幅広に設定されます)、ラテン文字で平均0.5です(単語によって変わります)。

4K、Fontsize 165 で測定:13ハングル文字≈2,150 px(2,450 pxのスロット内で安全)。一方、18文字≈2,970 px——これはオーバーフローし、行がフレームの左端からはみ出します。行が収まらない場合は折り返し、収まるようにフォントを縮小しないでください。

二言語キャプション(原文+翻訳):両方の行が短い場合のみ。2つの言語の行は読む負荷を倍増させ、デモではUIと競合します。クライアントが別途要求しない限り、ファイルのバージョンごとに1言語を推奨します。必要な場合は、原文を上に、翻訳を下に重ね、ブロック全体を翻訳の読む速度に対して予算を組んでください。

右から左へのスクリプト(アラビア語、ヘブライ語):libassはRTLをサポートしていますが、シェーピング品質はビルドのharfbuzzに依存します。コミットする前にプレビューフレームをレンダリングします——文字が正しく結合し、配置アンカーが意味をなすことを確認します。

フォントの太さはスクリプトによって異なります。 日本語と韓国語のフォントファミリーは、同じ Fontsize でも中国語のものよりも視覚的に大きく表示される傾向があります。1つのプレビューフレームで確認し、ルールではなく目視で調整します。

フェーズ4——スタイル(2層のグロー)

単一のASSスタイルでは「白い文字に太い色付きの輪郭とソフトな色付きハロー」を生成できません——Outline は1つのリングしか描画しません。2つのレイヤーでキューごとに2つのイベントを使用します。

レイヤー塗りつぶし輪郭ぼかし目的
0透明 (&HFF000000)太い、半透明の紫\blur 20–30ハロー
1白細く、不透明な紫なし / \blur1読みやすいテキスト

開始点(assets/caption-template.ass を参照)。色は &HAABBGGRR です——アルファ 00 は不透明、FF は完全に透明で、バイト順は BGR です。

紫色 RGB(124,46,240)  →  &H00F02E7C
ハロー、35%不透明    →  &H55F02E7C

PlayResでスケーリングし、感覚で行わない:4Kレンダリングでは fontsize 180 / outline 16 / halo 28 / blur 24 を使用。1080pで同じ見た目は 80 / 7 / 12 / 12。PlayResX/Y を出力解像度に設定し、ScaledBorderAndShadow: yes を維持します。

フェーズ5——フォーカスに従った配置

中央下部はデモでは間違いです。下部は通常UIであり、デモパネルは可視でなければならない唯一のものです。各キューを表示中のものに隣接する自由領域にアンカーします。

このキューのフォーカスアンカー理由
サイドパネル/チャットパネルが開いている\an6\pos(<panel_left - 40>, <panel_vertical_middle>)右端がパネルに密着し、ブロックは左方向に成長。幅が変わっても再計算不要
右上のトースト/通知\an3\pos(<toast_right>, <toast_bottom + 80>)右下アンカーがブロックをトーストのすぐ下に配置
全幅ページ、パネルなし\an5\pos(<visual_center_x>, <y between two content blocks>)中央揃えだが、計測された隙間に配置——幾何学的中心ではない
大きな組み込みタイトルがあるタイトルカード\an5\pos(center_x, BELOW the title)幾何学的中心は動画自身の見出しと衝突する

書き込む前に最大行幅を計算します:max_line_px ≈ fontsize × characters。行が自由領域を超える場合は、折り返します(1つのキューのためだけにフォントを縮小しない)——句読点で分割し、製品名は分割しないでください。

右アンカーのブロックでは、平坦な背景に数十ピクセルはみ出しても問題なく、意図的に見えます。デモパネルにはみ出してはいけません。

アンカーをフレーム端から離して配置します。 輪郭とハローは、グリフボックスからおおよそ outline + blur ピクセル(4Kで outline 15 / blur 22 の場合、約40 px、さらにアンチエイリアス)拡張します。したがって、右端から50 pxに配置された \an3 アンカーはハローがクリップされ、壊れた単語のように読み取られます。アンカーはフレーム内部から少なくとも150 px離して配置し、プレビューフレームで結果を確認してください——クリッピングは .ass ファイルでは見えず、画面で明らかです。

フェーズ6——焼き込み

scripts/burn.sh INPUT.mp4 subs.ass OUT.mp4 [crf]

これは -i IN -vf "ass=subs.ass" -c:v libx264 -crf 17~18 -pix_fmt yuv420p -movflags +faststart で、元の音声トラックを保持します。ハード字幕はデモに適切なデフォルトです——すべてのプレイヤー、アップロード、画面共有で生き残ります。要求された場合のみ .ass/.srt を一緒に提供します。

フェーズ7——検証

まず安価な静止画をレンダリングし、次にエンコードされたファイルを検証します。

scripts/preview_frame.sh INPUT.mp4 subs.ass 16.0 /tmp/check1.jpg

各フレームでチェック:デモパネル上のテキスト、フレーム外のテキスト、予期しない行折り返し、終了すべきだったキュー、明るい背景でベタになってしまったハロー。次に、完成したファイルから3フレームを再抽出し(字幕は焼き込まれ、品質は最終版)、再確認します。

トラップ(それぞれ実際に発見に時間がかかったもの)

  1. -ss を -i の前に置くと ass フィルターが無効になる。 入力シークはタイムスタンプをゼロにリセットするため、フィルターはすべてのフレームで t≈0 の字幕状態をレンダリングします。結果:動画に字幕がまったく焼き込まれず、ffmpegはエラーを出力しません。常に -ss を -i の後(出力シーク)に置くか、-copyts を追加します。バンドルされているスクリプトは既にこれを行っています。
  2. プレビューフレームの座標は嘘をつく。 1280×720にスケーリングされたが、あなたに表示されるフレームは……(翻訳のため省略)

関連スキル