Communitygithub.com

businessbaum/vedit

このスキルは、ユーザーが字幕や画面上のテキストを要求したときに使用します。「brenn die Untertitel ein」「mach Untertitel」「burn in the captions」「add subtitles」「word-by-word captions」「fix a caption typo」などの指示に対応します。ASS生成、ドイツ語のピクセル幅グループ化ルール、フォントテーブル、libassによる焼き込みをカバーします。

vedit とは?

このスキルは、単語レベルの文字起こしからASS字幕ファイルを生成し、動画レンダリングに焼き込むためのものです。特にドイツ語の処理に特化しており、複合名詞(例:`Geschwindigkeitsbegrenzung`)によるフレーム溢れを防ぐため、文字数制限ではなくピクセル幅予算に基づいたグループ化ルールを採用しています。また、可変フォントがlibass経由でArialにフォールバックしてしまう問題を回避するため、静的で単一ウェイトのフォントファイルを正確なフォント名で指定する必要があります。このスキルは、Claude Codeや他のAIエージェントツールで使用可能で、`engine/ve.py`というPythonベースのエンジン上で動作します。処理の流れはプリセット選択、ASS生成、テキスト修正(数字や固有名詞の上書き)、焼き込み、そしてフォントが実際に読み込まれたことを確認するQCチェックから構成されます。フォントのフォールバックを検出するためには、プロジェクトスタイルとArialコントロールスタイルでそれぞれ1フレームずつレンダリングし、ハッシュが異なることを確認するQCチェック(e)が必須です。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/businessbaum/vedit/tree/HEAD/.agents/skills/captions

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

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

ドキュメント

captions

単語レベルの文字起こしからASS字幕ファイルを生成し、レンダリングに焼き込みます。 引数: $project スラグ、$preset は presets/captions/*.json から選択する名前。完全なリクエストは $ARGUMENTS に格納されます。空の $プレースホルダは、このスキルがスラッシュ呼び出しではなく自動トリガーされたことを意味します。その場合、セッションコンテキストの PIPELINE_STATE からプロジェクトを取得し、選択したプロジェクトを明示します。動詞名はビルド契約に従います。python engine/ve.py --help が優先されます。

動作を左右する2つのドイツ語固有ルール

1. max_chars でグループ化しない

ドイツ語の複合名詞は分割不可能な1つのトークンであり、文字数上限を簡単に超えます。 Geschwindigkeitsbegrenzung は26文字で、90pxで1920フレームの**約60%**を占有します。 max_chars: 22 のルールではこれを分割できず、オーバーフローするか、グループ化処理が静かに制約を破ります。

代わりにピクセル幅予算でグループ化し、予算を超える単一の単語については自動縮小ルールを適用します。 estimate_text_width_px() が計測を担当し、group_words() が予算を管理します。詳細は docs/fonts.md および engine/vedit/captions.py のモジュールドキュメントに記載されています。

これは最も一般的なドイツ語字幕の失敗であり、完全に回避可能です。

2. フォントは静的で単一ウェイトのファイルを使用し、正確なnameID 1で指定する

可変フォントはASSからウェイトを選択できません。InterVariable.ttf はlibassを通じてArialとバイト単位で同一の出力を生成します。ファイルを読み込んだログは出力されますが、md5は同一になります。終了コード0、警告なし。Style: D,Inter,...,Bold=-1 と指定するとDirectWriteのArialが使用され、YouTubeにエクスポートされるまで誰も気づきません。

以下のフォントのみを使用し、正確な Fontname 文字列と Bold 値を指定してください。

ファイルASS FontnameBold
Inter-Regular.ttfInter0
Inter-Bold.ttfInter-1
Inter-SemiBold.ttfInter SemiBold0
Inter-ExtraBold.ttfInter ExtraBold0
Montserrat-Bold.ttfMontserrat-1
Montserrat-ExtraBold.ttfMontserrat ExtraBold0

assets/fonts/ にはフォントファイルのみを配置してください。 libassはこのディレクトリ内のすべてのファイルを読み込み、フォント以外のファイルがあるとエラーになります。たとえば、README.mdが置かれていると Error opening memory font 'README.md' が発生します。このディレクトリは、この理由から設定で ask 権限が設定されています。フォントに関するドキュメントが必要な場合は docs/fonts.md に配置してください。

ステップ1 - プリセットを選択する

presets/captions/*.json には、スタイル、グループ化予算、配置、ハイライト動作が定義されています。 好みではなく output.platform に基づいて選択してください。

  • 縦型プラットフォームでは、大きく中央揃えで1~3単語、セーフエリアの高い位置に配置します。画面の下3分の1はプラットフォーム自身のUIで隠れます。
  • YouTubeの長尺動画では、2行で下端に固定し、スマートフォンでも読みやすいサイズにします。

生成前に選択内容をユーザーに確認し、何が変更されるかを説明してください。

ステップ2 - ASSを生成する

python engine/ve.py captions $project --preset $preset

2つの構造要件があり、どちらも違反すると静かに失敗します。

  • 23フィールドのV4+ Style: Format: 行(Name .. Encoding、libass自身のフィールドリスト。24番目のフィールド AlphaLevel はSSA v4でありASS V4+ではありません)と、10フィールドのEvents Format: 行は正規の形式である必要があり、各行の下の行は正確にその数のカラムを持つ必要があります。カラム数が一致しない場合、libassは終了コード0で終了し、レンダリングテキストに先頭に余分なカンマを挿入し、輪郭と太字を静かに削除します。 これは5フィールドのFormatに対して6フィールドのDialogueで再現されました。captions.validate_ass() は焼き込み前に両方のカラム数をチェックします。これは必須であり、burn_in() は失敗時に処理を拒否します。
  • ファイルはUTF-8である必要があります。UTF-8の場合、ドイツ語のダイアクリティカルマークはlibassを通じて正しくラウンドトリップすることが確認されています。Größe Straße Übermäßig äöüÄÖÜß はエンドツーエンドで検証済みです。

サイドカーファイルは projects/<slug>/out/captions.ass に書き込まれ、captions.sidecar_path から参照されます。

タイミングは、カットに使用されたものと同じ単語リストから取得され、_computed のタイムラインポジションにマッピングされます。したがって、字幕はカットの変更によって無効になります。パッチを当てるのではなく、カット後に再生成してください。

ステップ3 - テキストを修正する(文字起こしは修正しない)

数字は予測可能な問題です。文字起こし時に --suppress_numerals が必須であるため、話者が「20」と言った場合でも文字起こしは zwanzig と出力します。これを修正するには captions.overrides[] に {word_index, text} として追加し、文字起こし自体を決して編集しないでください。文字起こしはプロジェクト内のすべての単語範囲のアンカーであり、書き換えるとカットが同期しなくなります。

固有名詞、製品名、GmbH 形式の略語についても同様です。これらは score の低い単語をスキャンすることで一回のパスで収集します。アライメントスコアはフィラー信号としては役に立ちませんが、「アライナーがこの単語が何か確信を持てなかった」という信号としては優れており、これはまさに固有名詞の特徴です。

ステップ4 - 焼き込み

python engine/ve.py render $project

焼き込みは最終マルチプレクス時、連結された動画に対して1回だけ行われます。セグメントごとに焼き込むことはありません。セグメントごとの焼き込みは、カット境界ごとに同じ字幕を再レンダリングし、カットの両側に部分的な行が配置されることになります。

subtitles= と fontsdir= のエスケープは正確かつ非自明です。すべてスラッシュ(前方)を使用し、値は引用符で囲み、ドライブのコロンは単一エスケープします。

subtitles='C\:/Users/me/.../out/captions.ass':fontsdir='C\:/Users/me/.../assets/fonts'

vedit/ffmpeg.py の escape_filter_path() がこれを担当し、引用符を含めた文字列を返します。自分で引用符を追加しないでください。ネストの深さではなく、引用が重要です。引用符なしの単一エスケープ形式は No option name near '/Users/...' で失敗し、引用符で囲まれた形式のみがパス内のカンマ、セミコロン、括弧(Windowsではすべて有効)を正しく処理できます。escape_filter_path_nested() は、ダブルエスケープされた引用符なしの C\\:/... 形式を返します。この制限はドキュメント化されています。

すべてはPythonからのargvリストを経由します。Git Bashで同じパスを構築すると C\;C:\Program Files\Git\Users\... のようになります。これは実際に再現された問題です。

captions.enabled と captions.burn_in を設定し、stage を "captions" に設定します。

ステップ5 - フォントが実際に読み込まれたことを確認する

QCチェック(e)は必須であり、サイレントフォールバックを検出する唯一の方法です。 プロジェクトスタイルで1フレームのキャプションをレンダリングし、Arial コントロールスタイルで別の1フレームをレンダリングし、ハッシュが異なることを確認します。ハッシュが同一である場合、libassがフォールバックしたことを意味し、エクスポートは間違った書体で出力されます。

その後、さらに以下の項目も確認してください。Format: 行のフィールド数、ピクセル予算を超える行がないこと、セグメント境界をまたいで断片が残るようなキャプションがないこと。

障害モード

症状原因修正
キャプションがArialのように見える可変フォント、または誤った Fontname静的ファイル + 正確なnameID 1の文字列を使用。QC(e)を実行
テキストに先頭カンマ、輪郭なし不正な Format: 行正規の23/10フィールド行を出力
Error opening memory font 'README.md'assets/fonts/ にフォント以外のファイルが存在そのディレクトリにはフォントのみを配置
長い単語がフレームからはみ出るmax_chars でグループ化ピクセル幅予算 + 単一単語の自動縮小
No option name near '/Users/...'fontsdir の単一エスケープコロンの前のバックスラッシュを二重にする
数字がスペルアウトされる--suppress_numerals(仕様通り)captions.overrides エントリ
再カット後に字幕が非同期になる古いASS再生成。手動でタイミングを修正しない

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

businessbaum/vedit

This skill should be used when the user asks for mechanical tightening of material that is already selected - "mach den Rohschnitt", "bau den Rohschnitt", "schneid die Ähms raus", "nimm die Füllwörter raus", "entferne die Pausen", "kürz die Pausen", "schneid die Versprecher raus", "mach den Radioschnitt", "straff das", "build the rough cut", "cut the fillers", "remove the ums", "tighten the pauses", "trim the dead air", "cut the retakes", "radio edit". Turns kept story blocks into enabled segments in project.json. Does not decide what the video is about - that is cut-story.

businessbaum/vedit

This skill should be used when the user asks to decide what the video is actually about before any mechanical cutting - "bau die Story", "was soll ins Video", "was kann raus", "welche Themen bleiben", "kürz das auf 6 Minuten", "mach daraus ein kurzes Video", "wo ist der Hook", "sortier die Themen", "worum geht es hier eigentlich", "build the story", "decide what stays", "structure this", "cut this down to N minutes", "find the hook", "reorder the topics". This is the CONTENT-selection pass that writes the "story" block in project.json; it runs after transcribe and before cut-rough.

businessbaum/vedit

This skill should be used when the user asks for the finished video file - "exportier das", "render das fertig", "mach das final", "renderte das Video", "mach den Endexport", "bau das Video zusammen", "mach es fertig für YouTube", "gib mir die MP4", "export the final", "render the final cut", "make the master", "export for TikTok", "give me the mp4". Covers segment rendering with the cache, guarded concat, the one-pass audio, loudness normalisation and the single mux.

関連スキル