subtitles: Premiere Pro 字幕を修正する
初回セットアップ: キットルートの INSTALL.md を参照。プラグイン使用時は、セットアップコマンドは bash "${CLAUDE_PLUGIN_ROOT}/install.sh" --data "${CLAUDE_PLUGIN_DATA}"、手動インストール(skills/ フォルダ4つを ~/.claude/skills/ にコピー)の場合は、キットルートで bash install.sh を実行。スクリプトはこのスキルの scripts/ フォルダにあります。このスキルは他のスキルを ../rough-cut/... として参照します。
このスキルの機能
Premiere が生成する自動文字起こし(.json 書き出し)は、特に固有名詞(カメラマン名が無関係な2語に分解されるなど)に誤変換が多発します。whisper.cpp はより正確に文字起こしします。このスキルでは、ビデオをローカルで再度文字起こしし、Premiere の正確な形式(そのまま再インポート可能)で JSON を再構築します。参照スクリプトで固有名詞を修正し、句読点と大文字を復元し、作品タイトルを引用符で囲みます。最後に強制アライメントで音声にタイミングを再調整します(whisper のタイムスタンプはずれます)。
- 対応: ローカル文字起こし、固有名詞修正、句読点と大文字化、タイトル引用符、音声へのタイミング再調整(強制アライメント)、Premiere と同一フォーマットで再インポート可能。
- 非対応: ビデオのカットやラフカット(
rough-cut)、翻訳、既に正しい字幕ファイルの再同期、.srt/.vtt字幕(この形式は Premiere ネイティブ JSON)。
期待する入力
- Premiere から書き出したビデオ(
.mp4/.mov): 文字起こしする音声を含む。 - Premiere から書き出したキャプション
.json: コピーするターゲット形式(キー、speakerUUID、言語)を提供。 - ビデオの参照スクリプト(プレーンテキスト、Markdown、Google ドキュメント、Notion ページ、貼り付けたテキストなど、読み取り可能な形式): 固有名詞(人名、作品名、地名、ブランド名)の綴りの正規情報源。ない場合は確認を取るか、明らかな誤字のみ修正し、不確かな名前は whisper の綴りを保持します。
言語
句読点周りのタイポグラフィ(スペース)、アクセント記号、句読点セット、引用符、綴りの正規化は languages/<code>.json(fr、en 組み込み)に定義され、コード内にはありません。言語は --lang <code> で選択し、未指定の場合は Premiere JSON の言語、それもなければ whisper 文字起こしの言語、最後に en をデフォルトとします。ファイルの仕組みと言語の追加方法は ../rough-cut/references/languages.md を参照(同じ仕組みですが、このスキルは独自の languages/ フォルダを持ちます)。
要件(マシンごとに1回)
whisper-cli + ffmpeg + モデル ~/Models/whisper/ggml-large-v3-turbo.bin。../rough-cut/references/whisper-setup.md(共通セットアップ)を参照。確認: which whisper-cli ffmpeg && ls ~/Models/whisper/。
強制アライメント(音声へのタイミング再調整、ステップ6b):
pip3 install --break-system-packages ctc-forced-aligner torch torchaudio unidecode
初回実行時に約 1.2 GB の ONNX モデルがダウンロードされます(キャッシュ ~/ctc_forced_aligner/)。確認: python3 -c "import ctc_forced_aligner"。
ワークフロー
以下の
scripts/...コマンドは、スキルフォルダ内で実行することを前提としています。作業ディレクトリはシェル呼び出しごとにリセットできるので、一度スキルフォルダにcdするか、すべてのスクリプトに絶対パスを付けてください。作業ファイル(音声、文字起こし、修正)は、同期フォルダの外にあるスクラッチフォルダに置きます。
1. ビデオと入力形式を確認
最初に: オリジナルをスナップショット。 Premiere は再書き出し時に入力 .json を書き換えることがあり、ステップ7でタイミングを確認する唯一の真実の同期情報です。
cp "<Reel.json>" /tmp/subs_original_premiere.json
ffprobe -v error -show_entries format=duration:stream=codec_type,r_frame_rate \
-of default=noprint_wrappers=1 "<video>"
duration(最後の単語がこの範囲内に収まる必要があります)と fps を記録します。次に入力 Premiere JSON を調べて形式を確認します。
python3 scripts/inspect_format.py "<Reel.json>"
これにより、キー(トップ/セグメント/ワード)、speaker UUID、言語、セグメント数、および現在のテキストをセグメントごと(=修正すべき誤字リスト)が表示されます。想定される形式は references/premiere-format.md に文書化されています。検査で異常なキーが表示された場合はこれを参照してください。
2. ローカルで文字起こし(whisper.cpp)
python3 scripts/extract_audio.py "<video>" /tmp/subs_audio.wav
python3 scripts/transcribe.py /tmp/subs_audio.wav /tmp/subs_transcript.json --model large-v3-turbo --language <code>
Apple Silicon で3分あたり約8秒。文字起こし結果は、text(句読点付き、大文字小文字区別あり)とタイムスタンプ付き words を持つセグメントで構成されます。
2b. whisper が句読点を付けなかった場合(連続配信): 再句読点化
高速なモノローグでは、whisper が句読点や大文字なしのストリームを返すことがあります(セグメントが文の途中で切れ、./, がない)。明らかな症状: build_subs.py が eos: 0 を報告する。句読点がないとキャプションが読めません。簡単な確認: python3 scripts/words_of.py /tmp/subs_transcript.json 0 1 2 --plain。
その場合は、自分で再句読点化します(これは LLM の仕事であり、whisper の仕事ではありません)。各セグメントの text に句読点と文頭の大文字を書き直しますが、whisper の単語は一字一句そのまま保持します。追加するのは句読点と大文字小文字のみです。語彙の修正(名前、頭字語)は corrections.json に残します。句読点は常に単語に付ける(so? であって so ? ではない)。前置きスペースがある記号は分離トークンとなり、アライメントが静かに削除します(114個の ?/! が一度に失われたことがあります)。build_subs.py は言語のスペーシング(フランス語の場合はノーブレークスペース)を自動的に追加します。結果を JSON {index: "punctuated text"} に格納し、次のコマンドを実行します。
python3 scripts/repunctuate.py --transcript /tmp/subs_transcript.json --punct /tmp/punct.json --output /tmp/subs_transcript_punct.json
このスクリプトは、単語を追加、削除、変更した場合拒否します(差分を表示します)。これにより単語とタイミングの対応関係が維持されます。セグメント間の流れに注意してください。文を継続するセグメントは小文字で始まり、文を開始するセグメントは大文字で始まります。この句読点化された文字起こしを残りの手順(ステップ6の --transcript)で使用します。
また、クリップ末尾の幻覚も削除します。最終的な無音部分で、whisper は字幕クレジットや「ご視聴ありがとうございます」などの行を誤って生成することがよくあります(各言語の典型的な行は languages/<code>.json の whisper_hallucinations にリストされています)。seq_rule N to 0 で削除します。もう一つの幻覚: 連続する同じテキストのセグメントで、単語の継続時間がほぼゼロ(start==end)、無音部分で繰り返される幻覚(「さあ行こう」x3)。検出: セグメント i と i+1 が同じテキストで、単語の継続時間が 0。修正: ビルド前にトランスクリプトから削除します(del segs[i:j])。
3. whisper 文字起こしと参照スクリプトを読む
文字起こしのテキストをセグメントごとに表示し、ステップ1の入力 Premiere テキストと比較します。whisper は多くの問題を修正しますが、固有名詞のタイポは残ります。参照スクリプトを読んで、固有名詞と引用された作品タイトルの正規の綴りを入手します。
4. corrections.json を構築する(インテリジェントな核)
ここが品質を左右します。whisper が聞いた内容とスクリプトの名前を比較して修正ファイルを構築します。影響を受けるセグメントの単語単位の分割を調べて、正確なルールを記述します。
python3 scripts/words_of.py /tmp/subs_transcript.json 2 9 38 # 調べるセグメントのインデックス付きトークン
出力はすべての単語にインデックスを付けます(9:Smyth 10:Jones)。これにより、トークン化(名前がいくつのトークンになるか、ハイフンが保持されるかなど)を推測することなく正確なルールを記述できます。
corrections.json の形式(すべてのキーはオプション):
{
"quote_char": "\"",
"normalize_cotes": true,
"seq_rules": [
[["jon","smyth"], ["John","Smith"]],
[["train","park"], ["Trent","Parke"]],
[["arthus","bertrand"], ["Arthus-Bertrand"]]
],
"word_rules": {"macdum": "Magnum", "scholler": "Schoeller"},
"titles": [
{"match": ["the","decisive","moment"], "canonical": ["The","Decisive","Moment"]},
{"match": ["errance"], "canonical": ["Errance"], "sep_comma": true}
]
}
seq_rules: 複数単語の名前、または 1対N / N対1 の変換。照合は単語を小文字にし、句読点を取り除いた状態で行われます。2つ以上の whisper 単語にまたがる名前や、単語数が変わる場合に使用します。大文字小文字: ターゲット単語はそのまま使用されます。文の中での位置を含め、最終的な大文字小文字を記述します(単語が文頭の場合は["Today","we",...]、["today",...]ではありません)。同じ長さのソース/ターゲット: 句読点(内部のコンマ)とタイミングはインデックスごとに保持されます。異なる長さ: タイミングは再分配され、最初と最後の単語の句読点のみが残ります(置き換えられた単語の内部コンマは失われます。ステップ7でそれらのセグメントを再読してください)。削除: 空のターゲット[["uh"], []]は単語を削除します(本当に孤立したノイズで whisper が倍増した場合にのみ使用。誤って文字起こしされた単語は、削除するよりも修正してください。そうしないと文の一部が失われます)。word_rules: 1単語の名前で、大文字小文字を強制します(小文字キーから正確な値へ)。titles: 引用作品(書籍、シリーズ、タイトル付き写真)に自動引用符(最初の単語に開き引用符、最後の単語に閉じ引用符)が付き、タイトルの正しい大文字小文字のcanonicalが含まれます。sep_comma: trueはタイトルの後にコンマを追加します(複数のタイトルが連続する場合に便利)。作品を引用符で囲み、人物名やブランド名は囲みません。normalize_cotes: 言語ファイルの綴り正規化(normalize_words)を適用します。whisper が一貫して書かない変種向け。設定しない限りオフ。french_typography: 言語のタイポグラフィスペーシングのスイッチ(名前は歴史的なものですが、使用中の言語に適用されます)。デフォルトは言語ファイルから取得します。フランス語の場合は二重句読点と閉じ引用符の前、開き引用符の後にノーブレークスペース(U+00A0)。英語の場合はなし。falseに設定すると、句読点がくっついた状態になります。ステップ2bで再句読点化する場合、句読点は必ず単語に付けてください(Flop:)。build_subs.pyが自動的にスペーシングを戻します。
5. ユーザーと不確実性を解決する(決して推測しない)
whisper は時々、曖昧または理解不能な固有名詞以外の単語を生成します。黙って推測しないでください。 これらのポイントをタイムコードとともにリストアップし、ユーザーに実際の発言を尋ね、コンテキストやスクリプトが示唆する場合は仮説を提案します。以下の区別をしてください。
- 直接修正: スクリプトで確認された固有名詞、明らかで確実なタイポ。
- 先に質問: 曖昧な固有名詞以外の単語、または意味を変える修正(例: whisper の「who feels」に対してスクリプトの「without」: 意味が逆転、スクリプトで決着がついていてもフラグを立てる)。
口語スタイル(スラング、フィラーフレーズ、繰り返し)は書き直さないでください。キャプションは音声に従わなければなりません。
回答を待っている間にファイルに何を書くか(生成をブロックする必要はありません): すべての安全な修正と、一般的な曖昧な単語に対する最良の仮説を適用した最初のバージョンを作成します(ただしフラグを立てます)。ただし、スクリプトにない不確かな名前は whisper の生の綴りを保持します。絶対に固有名詞を推測しないでください。そのバージョンを、確認すべきポイント(タイムコード)のリストとともに提出し、corrections.json を更新して回答を得た後に再生成します。これが通常のフローです。生成、質問とともに提示、改良。
6. 修正済みファイルを生成する
python3 scripts/build_subs.py \
--transcript /tmp/subs_transcript.json \
--original "<Reel.json>" \
--corrections /tmp/corrections.json \
--output "<folder>/<name>_corrige.json" [--lang <code>]
このスクリプトは、オリジナルの形式(speaker UUID、言語、ソートされたキー、最小化UTF-8)をクローンし、句読点と大文字小文字を復元し、修正を適用し、引用符を追加し、eos を再計算して、検証レポートを出力します。
<name>_corrige.json をオリジナルの隣に書き込み、オリジナルのエクスポートを上書きしないでください(ユーザーのソースです)。_corrige サフィックスはスクリプトが依存するファイル名です。これを維持してください。
6a. build_subs.py の2つの頻出トラップをチェックする(毎回)
多くのビデオで見られる2つのバグ。毎回ビルド後に確認します。
- 小数点の数字の重複。 whisper は
6.6を2つのトークン(6+.6、同様に1,33、または4 AMを4+AM)に分割します。句読点再注入により出力で重複します(1.4 .4、6.6. .6)。seq_rulesのマッチングも誤ります(正規化が内部の./,を除去しないため)。検出: 最終出力でパターン\d[.,]?\s+[.,]\dを grep。修正: ビルド前にトランスクリプトに対して外科的修正を行います。2つのトークンを1つにマージします({'text':'1.4','start':a.start,'end':b.end,...}、whisper 単語スキーマはtext/start/end/confidenceでありdurationではない)、その後再ビルド。 - 孤立した文末句読点の削除。 whisper セグメントの
textに存在する?/!/:が、それ自体の単語トークンを持たない場合に消失します(How did we do it ?は?を失う)。検出: whisper テキストと出力のcount('?')(および!、:)を比較。修正: 出力 JSON を直接編集し、前のトークンに?(言語のスペーシング付き)を再追加します。
トラップ1が検出された場合は、トランスクリプトを修正して再ビルドします(ステップ6)。トラップ2の場合は、出力を直接パッチします。
6b. 強制アライメントで音声にタイミングを再調整する(常に)
whisper の単語タイミングはズレます(高速なマルチテイクモノローグで最大 7 秒早く、非線形に測定済み)。whisper はテイク間の無音を圧縮するため、音声がキャプションより遅れて届きます。典型的な不満: 「'Ah' が表示されたときには、もう次の文が聞こえている」。DTW(-dtw)は何も変えません(同じオフセット)。まばらなアンカーでのタイムワープは、既に正しかった領域を歪めます。whisper のテキストは良好ですが、タイミングのみが間違っています。
強制アライメントは、すべての単語を新たに配置します。
python3 scripts/force_align.py \
--transcript /tmp/subs_transcript.json \
--audio /tmp/subs_audio.wav \
--output /tmp/subs_transcript_aligned.json [--lang <コード>]
このスクリプトは次の処理を行います。
- 文字起こしテキストをデコードします。
- 各単語の文字に対する音素の確率を計算します。
- テキストを音声波形に動的に合わせます。
- 単語の
start/endを更新します。 - セグメントの境界を単語から再計算します(セグメントの
start= 最初の単語のstart、end= 最後の単語のend)。
一度完了したら、これに相当する --transcript を指すようにステップ6を再実行します。
### 7. 結果を検証する
```bash
python3 scripts/check_eos.py "<folder>/<name>_corrige.json"
オリジナルと新規のキャプションを上下に並べて、ビデオの該当する部分を Premiere で確認します。
- 各キャプションは最後の単語の終わりに表示されますか?(強制アライメントは全体をシフトしますが、1~2フレームの微調整は手動のままです。Premiere はタイムラインで各キャプションを 1 フレーム単位で修正できます)。
- キャプションは文の途中で切れていませんか?(
eosの継承の問題?join_shortの誤り?)。 - Max は本当に Max であり、macks ではありませんか?(固有名詞のルールが正しい)。