Communitygithub.com

rocketmandrey/conversational-video-subtitles

会話/対話形式の短尺動画(Reels、Shorts、TikTok、1:1、16:9)に、単語タイミングを使って字幕を焼き付けます。すべての話し言葉を1行に、固定の下端ベースラインで中央揃え(リフトなし、横ずれなし)。話者ごとに色アクセントで区別し、フレームごとにWCAGコントラストチェックを実施。

conversational-video-subtitles とは?

このスキルは、会話や対話中心の短尺動画(Reels、Shorts、TikTok、縦横さまざまなアスペクト比)に、単語単位のタイムスタンプから高品質な字幕を焼き付けるためのツールチェーンです。出力は、信頼できる唯一のデータ源である `manifest.json`、焼き込み済みプレビュー動画、プレーンなSRT、スタイル付きASSとWebVTT、さらにレンダリング品質を検証するバリデータを含みます。 アルゴリズムの核は、単語のタイミングと顔・手・プロップなどの禁止領域(`forbid.json`)を考慮したセグメンテーションと配置にあります。デフォルトの配置モードは固定下端中央揃えで、動画全体でベースラインが変動せず、顔などの重要な要素と重なる場合は「重複許容」としてログに記録されます(配置そのものは変更しません)。オプションで衝突リフトモードも利用可能で、その場合はリフト量と理由が記録されます。 視覚的なスタイルとして、アウトライン、ソフトシャドウ、バックライト(WCAG AA基準の4.5:1以上を保証)、話者ごとのアクティブワードカラーによる区別を備えています。フレームごとのコントラストチェックや、不正なリフト・重複の検証など、包括的なQAレポートを生成します。 ElevenLabs Scribe、Groq Whisperなどからの単語タイムスタンプデータ(`words.json`)を入力として使用でき、CLIコマンド `plan`、`burn`、`export`、`validate` を通じて一貫したパイプラインを提供します。また、ファイルサイズやFPS、セーフゾーン、話者カラーなどを指定するプロファイル(karaoke、reveal、cards、kinetic、hybrid)をサポートしています。

対応~Claude Code~Codex CLI~Cursor
npx skills add rocketmandrey/conversational-video-subtitles-skill

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

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

ドキュメント

会話動画用字幕

概要

キャプションは独立したレイヤーです。話されたすべての単語を担い、フレーム内の描画に依存しません。 フレーム内の物体のラベル(部品名、賛否、矢印など)は別のレイヤーであり、異なる視覚言語(オブジェクトの横に配置されるコールアウト)を使用します。「画面上に既に表示されているから」という理由でキャプションの単語を削ってはいけません。

パイプライン: words.json + forbid.json(キャプションが覆ってはいけない領域) + config.json + プロファイル → plan → manifest.json(正規データ) → burn(焼き込み済みプレビュー=視覚的な真実)/ export srt|ass|vtt → validate。

各フォーマットの役割(混同しないこと):

フォーマット担うもの担わないもの
manifest.json単語、タイミング、レイアウト、placement、許容された重なり、リフトとその理由、レンダリングチェック:データの信頼できる唯一の情報源ピクセル
.srtテキスト + タイミングウェイト、色、アウトライン、アクティブワード、位置。SRTではスタイルを約束してはいけません
.assファミリー + Bold/\b700、libassメトリクスに変換されたサイズ、アウトライン、バッキングボックス、タイムスライスごとのアクティブワード、位置フェード;フォントが必要: subtitles=x.ass:fontsdir=fonts
.vtt::cue CSS(ファミリー、ウェイト、text-shadowによるアウトライン、バッキング)+ タイムスライスごとのクラス .active_<spk> / .em_<spk> / .dim / .u正確な位置;ページは@font-faceでフォントをロードする必要あり
焼き込み済み preview.mp4すべて:唯一の視覚的な真実の情報源—

使用するタイミング

  • 会話スキット、インタビュー、ナレーター付きの解説動画:縦または正方形、最大2行。
  • 単語タイミングがある(または音声がある:環境変数キーによるオプションのクラウドSTT)場合、顔/手/主要プロップの位置(ボックスで指定;3Dシーンの場合はフラットカラーのマットをレンダリングしてボックス化する)が分かっている場合。
  • 歌詞動画には使用しない(バウンス、スケーリング単語は意図的にサポートされていません)。

アルゴリズム

セグメンテーション(プロファイル内のchunk):pause_s(0.35秒)より長いポーズ、文の終わり、話者交代でハードブレーク;チャンクが2語以上になった場合のカンマでソフトブレーク;max_words、ピクセル幅(セーフゾーンのmax_width_frac)、max_dur_s(約1.2秒の音声が読みやすい)で制限。チャンクが機能語(не、в、а、and、the…)で終わってはいけません:次のチャンクに移動します。孤立した単語は、同じ文の隣接単語と結合できる場合は結合します。単語の継続時間は中央値の2.5倍にクランプされるため、単語がポーズにまで残ることはありません。タイミング:単位は最初の単語の50ミリ秒前までに表示され、直前の単語を決して切らず、最後の単語の後にhold_s秒間留まり、少なくともmin_show_s秒間表示されます。

配置 — placement: {mode: fixed_bottom_center, collision_lift: false}(対話のデフォルト)

  1. 動画全体で1つのベースライン:baseline = safe.bottom - baseline_margin_px(または明示的に設定)。すべての単位はその上に下端揃えされ(y + h = baseline)、フレームの中央に配置されます(x + w/2 = W/2)。何も移動しません:リフトなし、横ずれなし、話者ごとのx移動なし。行幅は、中央揃えされた行がセーフゾーン内に収まるように制限されます。
  2. 禁止領域とベースライン上で交差する行はそのまま配置されます:単位にはoverlap_accepted: [クラス]が記録され、QAシートには「重複許容(固定ベースライン)」として黄色い枠が描画されます。情報であり、失敗ではありません。プラットフォームUIとの交差は依然としてエラーです(フォントを縮小するか、フレーズを分割してください)。
  3. オプション collision_lift: true(モード baseline_lift):話者の下にxを配置(align: speaker)、または中央揃え+左右フォールバック;すべてのxで衝突する場合は、lift_step_pxステップずつ持ち上げて空き領域が見つかるまで繰り返します。単位にはlift = {px, reason: [ベースラインで衝突したクラス], t0, t1}が記録され、マニフェストにはすべてのリフトがcollision_liftsにリストされます。理由なし → リフトなし。顔を絶対に隠さないようにするよう指示された場合にのみ使用します。
  4. 単語を決して削除しない。

見た目。アウトライン(outline_px)+ ソフトシャドウ + 背景の背後にある最も暗い色相が4.5:1(WCAG)以上を維持するために必要な分だけアルファが引き上げられた明るいバッキング。話者はアクティブワードの色で区別され、ハイブリッド/カードモードでは話者色の4pxのバーが追加されます。吹き出しや重いネームプレートはありません。落ち着いた動き:アルファフェードのみ、スケールポップなし、垂直スライドなし。

QA(scripts/validate.py、demoでも実行):

  • スキーマ:templates/manifest.schema.jsonに対するマニフェストの検証;
  • 行数 ≤ 2;
  • 配置:fixed_bottom_centerではすべての単位のy-ベースラインとx-センターのばらつきが0(許容差0.5px)であり、リフトがないこと;baseline_liftではすべてのリフトがグリッド上にあり、理由が存在し、ベースラインで実際に衝突していること;
  • すべてのフレームでプラットフォームUIとの重複がゼロ;禁止領域:baseline_liftでは0、fixed_bottom_centerではoverlap_accepted(情報)として報告;
  • すべての単語が正確に1回、順序通り、話されている間に表示される;
  • コントラスト ≥ 4.5、バウンスなし(両方ともburnでフレームごとに測定);
  • フォントが解決される:ファイルが存在し、Pillowのファミリーとスタイル、OS/2ウェイト、fc-scan、libassのfontselectが宣言されたファイルとウェイトを選択すること;
  • ピクセル:焼き込みフレーム上とASSのlibassレンダリングの両方で、アクティブカラーが存在し、グリフの下端がレイアウトから3px以内、ステム幅が宣言されたウェイトの参照ラスターから1px以内、アウトラインがoutline_pxから1px以内であること。

プロファイル (profiles/*.json)

プロファイル説明最適な用途
karaoke一度に2〜4語、話し中の単語を話者色で表示、残りは暗く最も安定;固定レーン、説明動画
reveal単語が音声に合わせてフェードイン;レイアウトは事前に固定;≤ 2行落ち着いたナレーション
cardsカードあたり1つの思考(≤ 8語、2行)、密集したパネル、キーワードに色付けゆっくりとした、引用に適した行;音声より先に読む
kinetic一度に1トークン、大きめのタイプ決め台詞のみ;文脈はほとんどなし
hybrid(デフォルト)≤ 3語、単語がフェードイン、話し中の単語に下線、強調色、話者バー対話

R&Dシステムからのオプション:テンポ = chunk.max_dur_s: 1.2 + timing.min_show_s: 0.83(hybridではオン);ストレス = emphasis: "energy"(--audioからの話者ごとの単語ラウドネスzスコア)、またはkeywordsリスト付きの"keyword"。

コマンド

python -m subkit demo examples/fixture                     # 5プロファイル + hybrid_lift -> プレビュー、srt/ass/vtt、validation_report.{md,json}
python -m subkit plan --words W --forbid F --config C [--profile hybrid] [--audio A] -o manifest.json
python -m subkit burn manifest.json --forbid F --video in.mp4 [--audio A] -o preview.mp4 --qa qa.jpg
python -m subkit export manifest.json --format ass -o captions.ass     # srt | ass | vtt
python scripts/check_clean.py                                          # コミット前: media/keys/絶対パスがないことを確認
ELEVENLABS_API_KEY=... python -m subkit stt audio.wav -o words.json     # オプション;または --provider groq で GROQ_API_KEY

落とし穴

  • libass ≠ Pillowのフォントサイズ。 libassはOS/2 winAscent+winDescentでサイズを決定し、ベースラインを行頭からwinAscentの位置に配置します;エクスポートは両方を変換します。変換しないと、ASSのテキストは約35%小さく、細く、低く表示されます。
  • ASS内のバリアブルフォントはデフォルトインスタンス(通常はRegular)にフォールバックします。 使用するウェイトのスタティックインスタンス(ここでは Onest-Bold.ttf、700)を同梱し、fontsdirを渡してください。
  • ffmpeg -ss を -i の前に置くとタイムスタンプがリセットされます:サブタイトルフィルタはt=0をレンダリングします。ASSを焼き込むときは -i の後にシークしてください。
  • \k/\kf カラオケはセンチ秒単位で、話されたすべての単語を永続的に色付けします。「現在の単語のみ」の場合は、タイムスライスを出力してください(エクスポートが行う方法)。
  • SRTにはスタイルがありません:納品物にウェイトやハイライトが必要な場合は、ASS/VTTまたは焼き込み動画を提供してください。
  • 浮遊する行(話者ごとのx、ヒステリシス、「見た目が良い」リフト)は対話ではジッターと解釈されます。デフォルトの固定モードを維持してください;baseline_liftでは、バリデータが各リフトが実際に衝突していることを再チェックします。
  • コントラストは、背景の平均ではなく、背景の最も明るい10%のピクセルに対する色相で判断されます:火、雪、ハイライトは平均で失敗します。
  • 画面上に既に描画された単語を削除しないでください。代わりに、描画されたラベルを注釈レイヤーに移動してください。
  • templates/safe_zones.json のセーフゾーンは近似値です;プラットフォームUIは変更されるため、公開前に再確認してください。

チェックリスト

  • words.json が音声全体をカバーしている(クラウドSTTまたは強制アライナー);話者がラベル付けされている
  • forbid.json:ショットごとの顔、手、火/プロップ(align: speaker 用の speakers ボックスも含む)
  • config:サイズ、fps、safe_zone、話者色(異なる色相、両方ともバッキング上で ≥ 4.5:1)
  • plan → マニフェスト;overlap_accepted(固定モード)または collision_lifts(リフトモード、それぞれに実際の理由がある)を確認
  • burn → QAシート(シアン=ベースライン、黄色=重複許容、オレンジ=理由付きのリフト)と実際のフレームを確認
  • validate がすべてPASS;焼き込み動画を提供(+ プレイヤーがレンダリングする場合はASS/VTT;SRTはプレーンテキストとしてのみ)

関連スキル