auto-subtitle — 音声・動画から自動で SubtitleCue を生成
1 コマンドで字幕生成:音声・動画ファイルを入力すると、<同名>.cues.json と <同名>.srt の 2 つのファイルを出力します。データ契約は video-motion の SubtitleCue と完全に一致:{start, end, text}(秒、小数点以下 3 桁、1 行テキスト、長文は分割済)です。subtitles 配列は video-motion のタイムラインにそのまま取り込めます。
Step 1 · 素材と言語の確認
- mp4 / wav / flac 形式に対応(PyAV で直接デコード)。デコードエラーが発生した場合は、事前に
ffmpeg -i in.mp4 -vn -ac 1 -ar 16000 out.wavで再エンコードしてください。 --langで言語を指定:zh(デフォルト)、enなど whisper の言語コード。- 純粋な音楽や効果音素材は実行不要:VAD により空または極端に短い結果が出力されます(仕様であり、バグではありません)。
- 既知のテキスト(台本、セリフ、原文)がある場合は UTF-8 で保存しておいてください——Step 3 の L2 比較基準として使用し、基準のずれを防ぎます。
Step 2 · 文字起こしの実行
venv はリポジトリ外に配置(284 MB のビルド成果物。.gitignore の .venv/ でカバー済み)。テンプレートにビルド成果物を含めないという本リポジトリの規約(node_modules を含めないのと同様)により、venv はリポジトリ外に作成し、全プロジェクトで再利用します:
python -m venv "$env:USERPROFILE\.cache\vibe-studio\asr-venv"
& "$env:USERPROFILE\.cache\vibe-studio\asr-venv\Scripts\python.exe" -m pip install -r skills/auto-subtitle/templates/asr/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
cd skills/auto-subtitle/templates/asr
& "$env:USERPROFILE\.cache\vibe-studio\asr-venv\Scripts\python.exe" transcribe.py <入力.mp4|wav> --outdir <出力ディレクトリ>
- デフォルトモデルは
large-v3-turbo(約 1.6GB、初回自動ダウンロード、中国語の品質ライン)。素早く検証する場合は--model small(約 484MB)を追加。モデルは~/.cache/huggingfaceにキャッシュされ、一度ダウンロードすれば全プロジェクトで再利用されます。 - モデルダウンロード失敗時は
HF_ENDPOINTでミラーを指定:デフォルトは公式直結(2026年8月現在動作確認済)。代替例:$env:HF_ENDPOINT="https://hf-mirror.com"(注意:hub 1.x では 308 リダイレクトの互換性問題があるため、公式を優先)。 - 標準出力に 1 行の英語サマリを出力:
segments=N cues=M total_audio=... model=... lang=... out=...。exit 0 で成功、1 で失敗。
Step 3 · 3段階チェック(必須、省略不可)
L1 プログラムレベル:JSON が json.load 可能であること。subtitles が配列であり、各要素が start / end / text の 3 フィールドのみを持つこと。start < end かつ単調非減少であること。数値は小数点以下 3 桁であること。最後の end が ffprobe の再生時間 + 1秒以下であること。SRT のタイムスタンプ行が HH:MM:SS,mmm --> HH:MM:SS,mmm の形式に一致し、インデックスが 1 から連続していること。
L2 正確性:既知テキストがある場合 → difflib.SequenceMatcher で比較(両者から句読点と空白を除去して正規化)。全体の類似度 ≥0.85、各文 ≥0.70(連続する cue を結合したウィンドウで max を取る。分割/結合を考慮)。既知テキストがない場合 → 先頭 / 中央 / 末尾の 3 箇所を聴取確認。
L2.5 タイムライン(通常チェック):字幕ウィンドウは単語レベルの音声ウィンドウと一致し、文間の無音部分に字幕が存在してはならない。隣接する cue の間隔を監査:文間には 1 秒以上の実際の無音ギャップが存在する必要がある(文内分割 gap=0 は正常)。0.3 秒未満のフラッシュフラグメントが存在してはならない。最初の cue の start は音声開始点と一致し、最後の cue の end は末尾の無音を切り捨ててはならない。内部では単語レベルのタイムスタンプ(最初の単語の start / 最後の単語の end)でウィンドウを決定しており、このチェックは成果物レベルの検証です。
L3 結論行:すべて合格 → ALL GREEN。赤項目がある場合 → 分割方法 / VAD パラメータを調整して再実行・再チェック。2 回連続で赤 → 実行を中止し報告。闇雲な調整は禁止。
Step 4 · 納品
- 報告:
cues.jsonの絶対パス + cue 数 + 音声時間 + モデル/言語 + 3段階チェック結果 - video-motion 連携:
subtitles配列をそのまま video-motion プロジェクトのsrc/cues.tsのcues.subtitlesに貼り付けるだけ。フィールド変換は不要。 - 対象外(今後の機能): 話者分離、単語単位のタイムスタンプ出力、翻訳、TTS 音声合成
Step 5 · 再実行コマンドの記録
納品報告の末尾に、完全な再実行コマンド(入力パス / --lang / --outdir / --model)を添付してください。これにより、後続のセッションや他のメンバーが正確に再現できます。成果物(cues.json / srt)は出力領域(本リポジトリの products/、クロスリポジトリの場合はプロジェクトルートの output/)に配置し、git には含めません。コマンド自体が資産です。
境界と注意点
- GBK コンソール:スクリプトの print は全て英語。ファイル書き込みは明示的に
encoding="utf-8"、JSON はensure_ascii=False。 - 分割ルール:1 行 20 字を超える場合は句読点(。!?;,、)で分割。ウィンドウ内に句読点がない場合は強制分割し、末尾の断片は最低 3 文字を確保(1 文字フラグメント防止)。0.3 秒未満の断片は前の行に結合し、結合後も 20 字上限を超えない。
- 単語レベルのタイムスタンプが字幕ウィンドウの唯一の正しい情報源——セグメントレベルのタイムスタンプを使用すると、文間の無音が吸収され、字幕が約 1 無音区間分早く表示される(修正済み。
word_timestamps=Falseに戻さないこと)。 - CPU int8 推論:28 秒の音声で約 1 分以内に結果出力。長い音声は比例して時間見積もり。large-v3-turbo の初回実行はダウンロードを含むため、十分なタイムアウトを設定。
- 中国語認識では数字がアラビア数字に変換されることがよくあります(「两百毫秒」→「200毫秒」)。L2 比較時の正常な差異であり、認識エラーではありません。
納品時の連携
最終的な SRT / cues は、元の音声ソースとモデルバージョンとともにアーカイブします。字幕を焼き付けた mp4 だけを残さないでください。動画納品時は、media-qc を使用して字幕ウィンドウと実際の再生時間をチェックします。正確性は既知の原稿または聴取確認に基づきます。デフォルトモデルのダウンロードとローカル推論は、人の声によるナレーションの代わりにはなりません。