/dubbing
Perso AI Dubbing API を介してビデオを自動吹き替えするスキル。
基本ルール(厳守)
- ワーカーのみが生のキーを見ることができます。
Readで開いたり、エコーバックしたり、コマンドライン引数として渡したりしないでください。ワーカーはXP-API-KEYヘッダーでのみ送信します。 - 1コマンド=全ジョブ。
scripts/dubbing.mjsは、アップロード、分割、翻訳、マージ、保存を単独で処理します。その前にprepare_input.mjsやprobe_split.mjsを実行しないでください。 これらはデバッグツールであり、probe_split.mjsは実際にアップロードを行うため(ビデオが2回アップロードされることになります)。 dubbing.mjsは常にバックグラウンドで実行してください。 ジョブは数分から数時間かかります。フォアグラウンドのシェルタイムアウトにより、実行が途中で停止します。- 言語が指定されていない場合 → ソース
auto、ターゲットen。 - 複数の言語は1つのコマンドで指定します(
--target en,zh,ja)。アップロード/分割は1回実行され、言語ごとに再利用されます。言語ごとに1回実行しないでください(ソースが毎回再アップロードされます)。 - 進捗を忠実に中継します。 ワーカーの標準出力
[progress]行を、そのまま、または要約してチャットに伝えます。また、表示されなかったステップ(分割/マージは制限を超えたビデオにのみ存在します)については報告しないでください。インデントされた標準エラーの詳細ログは中継する必要はありません。 - 実行中はユーザーに情報を提供し続けてください。 吹き替え、リップシンク、音声分離はすべて
[status]ハートビート(ステージ+アイテム数、例:ja 4/6 · en 1/6またはseparating — file 2/5、next check ~Nmヒント付き)を出力し、各完了アイテムは[progress]行としてストリーミングされます。起動後約3分後にバックグラウンド出力を確認し、その後はヒントされた間隔(メディアが10分以下の場合は10分、より長い場合は30分)で確認します。新しい[progress]行があればすぐに中継し、そうでなければ最新の[status]行を1行の更新として投稿します。パーセンテージ、経過時間、ETA を決して作り出さないでください。行が言っていることだけを中継してください。ユーザーに停止を促す行([space-select]、[split-confirm]、[free-limit]、[resume-check]、[credit-check]、クレジット切れ、完了)はプロセスを終了させるため、ポーリングなしで表示されます。 - テレメトリ用にランタイムに名前を付けます(サイレント)。 ワーカーコマンドに
--host <claude-code|codex|cursor|antigravity|gemini>を追加し、実行しているエージェントを指定します(わかる場合)。これは使用状況テレメトリにタグを付けるためだけです。ユーザーに表示したり、ユーザーに問い合わせたりしないでください。不明な場合は省略します。 - サポートされていないフォーマットは自動的にスキップされ、残りは処理を続行します。スキップ通知を中継します。
- 一時停止は失敗ではありません。
[space-select]、[split-confirm]、[free-limit]、[credit-check]、または[resume-check]行は、実行が あなたに何かを尋ねるために停止した ことを意味します。これは終了コード0で終了します(そのためエラーのように見えることはありません)が、ジョブは 完了していません。これらの行のいずれかが存在する場合は、実行が完了したと報告せず、それを中継して行動に移してください。合図は終了コードではなく、これらの行です。
セットアップ(遅延方式 — 事前ステップなし)
- API キー — 最初に実行するステップではありません。 ワーカーは、Perso API を必要とするコマンドが実行された瞬間に、それ自体をゲートします。最初の実行時に、ブラウザページを開くか(サインイン、ワンクリック — キーは暗号化されて保存されます)、またはブラウザを開けない場合はファイルベースの手順を表示します。それらをユーザーに中継します。ヘッドレス/SSH マシンでは、ワーカーはブラウザフローを完全にスキップし、ファイル/
--import登録で開始します(ブラウザページは、それが実行されているマシンにのみキーを配信できます)。同じようにそれらの手順を中継します。自分でキー登録をトリガーしないでください。ワーカーは必要なときにconnect.mjs/resolve_key.mjs --watchを起動するため、これらを実行したり、事前ステップとしてresolve_key.mjs --checkを実行したりしないでください(ユーザーが明示的にキーが登録されているかどうかを尋ねた場合のみ — キーがないことはエラーではありません)。キーをチャットに貼り付けないでください。キーを取得する: https://developers.perso.ai/api-keys - ffmpeg/ffprobe — ビデオがプランの制限を超えて分割する必要がある場合にのみ自動インストールされます(アクセス許可が要求された場合は承認してください)。手動チェック:
node scripts/check_deps.mjs。
実行
入力(ローカルパスまたは URL — ない場合は再度質問)を収集し、バックグラウンド で実行します。ワーカーは最初の必要時にそれ自体でキーゲートを実行します。登録が開始された場合は、表示された手順を中継するだけです。
- 単一:
node scripts/dubbing.mjs "<file|URL>" [--source auto] [--target en] [--space "space name"] [--out result.mp4] [--lipsync] [--no-save] - 多言語 / 複数入力(1つ以上の入力;URL とファイルは混在可能):
node scripts/dubbing.mjs "<URL>" "<file>" --target en,ja— 入力 × 言語ごとに1つの出力が各ソースの隣に保存されます(--out <folder>でそれらを収集します)。 - フォルダ(バッチ):
node scripts/dubbing.mjs "<folder>" [--target en,zh] [--recursive] [--out output-folder]
言語コード — --target は言語コード(en、ja)または地域タグを受け取ります。地域バリアントが存在するのは 英語(en = 米国デフォルト、en-GB = 英国)、ポルトガル語(pt = ブラジルデフォルト、pt-PT = ポルトガル)、スペイン語(es = メキシコデフォルト、es-ES = スペイン)のみです。en-US、pt-BR、es-MX はそれらのデフォルトの許容されるスペルです。他のすべての言語には単一のバリアントがあり、--source は言語のみを必要とします(地域タグはプレーン言語として扱われます)。
- リクエストで地域が指定された場合 → その正確なタグを使用します。バリアントがない 地域(例:オーストラリア英語)の場合 → リストされているオプションのみが存在することをユーザーに伝え、実行前に選択させます。
- 2文字のトークンは国ではなく言語コードです:
ukは ウクライナ語 — イギリス英語はen-GBです。ca(カタルーニャ語)、be(ベラルーシ語)、eu(バスク語)も同様の罠があります。ユーザーの表現がどちらか解釈できる場合は、実行前に どちらを意味するか尋ねてください。 - 地域なしで言語が指定された場合 → デフォルトのバリアント。これら3つの言語については、配信ノートで適用されたデフォルトを明記します(例:「スペイン語(メキシコ) — スペインの場合は
es-ESを使用してください」)。
保存場所 — --out は、結果をその中に収集する フォルダ(既存のディレクトリ、または / で終わるパス)、または単一入力の ファイルパス(パスに拡張子がない場合は実際の拡張子が追加されます)を受け取ります。デフォルトは各ソースファイルの隣です。尋ねる必要はありません。セッションの最初のジョブでは、開始行にそれを記載します(例:「結果は元のファイルの隣に保存されます — 別のフォルダをご希望の場合は今お知らせください」)。すぐに開始し、回答を待つ必要はありません。ユーザーがフォルダ名を指定した場合 — このリクエストで、またはこのセッションの後でいつでも — --out "<folder>" を付けて実行し、ユーザーが変更するまで後続のすべてのジョブでそのフォルダを使用し続けます。ユーザーが既に開始された実行の後にリダイレクトした場合は、実行が完了したときに完了したファイルをそこに移動します。例外:すべての入力が URL である場合(ローカルソースフォルダがない場合)、開始前に保存場所を尋ねてください。
スペース選択 — 複数のワークスペースがある場合、ワーカーはアップロード前に停止し、[space-select] 行(名前 | (プラン) | 残りクレジット)を表示して停止します。ユーザーにそれらのオプション のみ を表示し(内部番号は表示しない)、どれかを尋ね、--space "<space name>" を付けて再実行します。スペースが1つの場合は質問なし。PERSO_SPACE_SEQ で固定します。
分割確認 — 入力が長さまたはサイズ制限を超えており、自動分割&マージ(吹き替え、吹き替え+リップシンク、または音声分離)が必要な場合、ワーカーは停止し、[split-confirm] 行を表示します。それらを中継し、ユーザーに尋ねます。長さ/サイズ制限を超えているため、自動分割 → 処理 → マージ が必要であり、自分で分割する場合と比較して 仕上がりが劣る可能性があります — 自動的に進めますか?「はい」の場合は、同じコマンドに --allow-split を付けて 再実行します(この時点までは課金されないため、再実行は無料です)。バッチ実行の場合:--allow-split は実行内のすべての分割を承認します。
保存しない(サーバーのみ) — ユーザーがビデオを吹き替えたいが ローカルファイルとして保存したくない 場合、--no-save を追加します。ワーカーは結果をユーザーの Perso ワークスペースに残し、ダウンロードをスキップし、Kept on server, not saved: … → project <seq> を表示します([project-ref] 行は引き続き出力されるため、ダブは後でリップシンクしたり取得したりできます)。分割されていない単一ビデオのみ — 分割されたビデオのマージされたファイルはローカルダウンロードが必要なため、通常どおり保存され、ワーカーはそのように伝えます。--lipsync と組み合わせることはできません(リップシンクされたビデオはダウンロードする必要があります)。
無料プラン(結果は Perso に残ります) — Free ワークスペースは通常どおり吹き替え、リップシンク、分離ができますが、サーバーは すべての ダウンロードを拒否するため、ワーカーはそこにファイルを保存しません。Free plan — <name>: … ready on Perso → <url> (downloads need a paid plan) を表示します。そのリンクを 配信された結果として 中継します。実行は 完了 しており、何も失敗しておらず、再開するものはありません。ユーザーが実際のファイルを希望する場合、有料プランが必要です — アップグレードパスを提案します(プランアップグレード&クレジット を参照)。再実行は決して行いません。
無料プランの [free-limit] 一時停止 — 2種類あり、どちらも課金前に発生します。
- 30秒プレビュー。 30秒を超える吹き替え/リップシンク入力は、
[free-limit]行で停止します。Free では 最初の30秒のみ が吹き替えられ、残りは除外されます。質問を中継し、「はい」の場合は 同じコマンドに--allow-previewを付けて 再実行します(ワーカーは最初の30秒をローカルでトリミングし、その部分のみを送信します)。Perso がインポートしたプラットフォームリンクはトリミングできません。その場合は、ローカルファイル、短いクリップ、またはアップグレードが回答です。 - プラン制限を超えています。 プランの長さ/サイズ制限を超えるメディアには、Free では分割オプションは提供されません(
--allow-splitは適用されません)。[free-limit]行は、短いファイルまたはアップグレードが唯一の方法であることを示します。それらを中継して停止します。
実行中(待機の説明用):
- 分割: 最初にファイル全体がアップロードされます。プラン制限の拒否があった場合のみ、ffmpeg をインストールしてロスレスで分割します。外部 URL(YouTube・TikTok・Drive・Vimeo)はサーバー側で処理されます。
- キュー: すべての入力 × パーツ × 言語は1つのプールを共有します。満杯のキューは5分ごとに再チェックされます。パーツでエンジンエラーが発生すると、そのパーツの他の言語がキャンセルされます。サイレントパーツは元のものをそのまま通過させます。アイドルガードにより、無限にハングするのを防ぎます。
- 保存: パーツは(入力 × 言語)ごとに1つのファイルにマージされて戻されます。分割されていない出力は Perso のファイル名を保持します。マージされたものは
<original-name>.dubbed.<lang>.<ext>になります。競合がある場合は_2、_3… が付きます。
リップシンク
リップシンク(口の動きを吹き替えオーディオに合わせる)は 吹き替えの後、完成した吹き替えプロジェクトに対して実行されます。ビデオのみ — オーディオ入力は拒否されます。これは長時間のジョブです。バックグラウンドで実行し、吹き替えよりもかなり時間がかかることを事前にユーザーに伝えてください。 クレジット(サーバー課金が信頼できます):吹き替え ≈ 秒数×1 · リップシンク ≈ ×2 · 両方 ≈ ×3 — 今吹き替え+後でリップシンクしても、両方を同時に行うのと同じコストです。 4K以上のソース:プロ/ビジネス/エンタープライズプランではすべてのレートが ×3(例:1分の4K 吹き替え+リップシンク ≈ 60×3×3 = 540) — ビデオが4Kの場合はこれを伝えてください。
存在するものに基づいてフローを選択します。
- 新しいビデオ+リップシンク — 1つのコマンドでチェーン全体(吹き替え → リップシンク → 保存)を実行します。両方のステージで課金されることを警告します。
node scripts/dubbing.mjs "<file|URL>" --target en --lipsync[credit-check]行が表示されて停止した場合、見積もりが残りクレジットを超えています。それらの行を含めて表示し(トップアップURLも含む)、ユーザーがトップアップするか、続行を承認した場合 のみ--forceを付けて再実行します。 - このセッションで以前に吹き替え済み — 完了した実行はすべて
[project-ref] {...}行を出力します。それを保持し、ユーザーには絶対に表示しないでください。 再吹き替えなしのリップシンク(×2、吹き替え料金なし):node scripts/dubbing.mjs --lipsync-only '<that [project-ref] JSON>' - このセッションに [project-ref] がない場合 — ユーザーが Perso ポータルからプロジェクト番号を知っているかどうかを尋ねます(
--lipsync-only <number>、×2)。そうでない場合は、ビデオを再度吹き替える必要があります(--lipsync、×3)。再吹き替えの前に確認します。
ルール:
- 同じプロジェクトでリップシンクを繰り返すと再度課金されます(サーバー側の重複排除はありません)。このセッションで既にリップシンク済みの場合は、既存のファイルを指摘し、明示的な確認があった場合のみ再実行します。
- リップシンクが失敗した場合、ワーカーは代わりに吹き替えビデオを保存し、最終報告でその旨を伝えます — それを明確に中継してください。吹き替えクレジットは無駄になりません。
- 吹き替えとリップシンクの間でクレジットが不足した場合:吹き替えビデオは保存され、リップシンクのみが続行されます。トップアップURLを中継し、支払い後は
[resume-state]パスを介して続行します(中断と再開 を参照)。
スピーカー(吹き替えプロジェクトのみ)
既に吹き替えられたプロジェクトの1つ以上の文に 新しいスピーカー を割り当てるには、以下を実行します。
node scripts/speaker.mjs "<project URL|projectSeq>" --at "5s,22s,98s"(カンマ区切りのタイムコード/範囲)
node scripts/speaker.mjs "<project URL|projectSeq>" --text "first line|second line"(パイプ区切り、引用符で囲まれたテキスト)
node scripts/speaker.mjs "<project URL|projectSeq>" --sentence 1234,1288([sentence-select] 質問の後に再実行)
node scripts/speaker.mjs "<project URL|projectSeq>" --list(プロジェクトの文を参照して適切なターゲットを見つける)
吹き替えプロジェクトのみ — ワーカーは字幕(STT)、音声分離、リップシンクのプロジェクト、および未完了/失敗したプロジェクトを拒否します。プロジェクトリンクからワークスペースを自動的に特定するため、どのスペースかを尋ねる必要はありません。
一時停止は失敗ではありません。 [sentence-select] 行は、ワーカーが単一のターゲットを特定できなかったことを意味します(何も指定されていない、一致するものがない、または複数の文が一致した)。失敗したためではなく、何かを尋ねるためにクリーンに終了します。その下の番号付きリスト(インデックス、時間ラベル、テキスト)は、そのままユーザーに中継しても問題ありません。そのリストの直後に、[sentence-ref] 行が同じ番号とワーカーの内部的な文番号の JSON マップを出力します。その行は あなただけの参照用 です。ユーザーに表示したり、読み上げたりしないでください。ユーザーが番号を選択したら、そのマップで検索し、同じコマンドを --sentence にその値を設定して再実行します。それと同時に [speaker-resolve] ブロックがある場合、いくつかのターゲットは既に解決されたが まだ何も書き込まれていない ことを意味します。これには独自の番号付きリストと独自の [sentence-ref] 行があります。再実行の --sentence リストには、既に解決された値と新しく選択された値の両方を含める必要があります。そうしないと、解決された値が削除されます。1回の実行で複数の [sentence-select] グループ(複数のあいまいなターゲットが一度に)が出力される場合、各グループは…
[翻訳のため省略]