mcpField
E
explainer

explainer

github.com/pika-labsクリエイティブ・メディア411.7k 回インストール14 週間前に更新
リポジトリ

explainer について

explainerは、URLを約60~80秒のナレーション付き解説動画にするスキルです。製品ページ、ドキュメント、記事、GitHubリポジトリの説明に使え、GitHub URLにはリポジトリ向けの処理があります。

主な機能

  1. 01ブラウザーで指定URLを案内
  2. 02ナレーションとアバターのリップシンクを生成
  3. 03ビートシートに沿った構成と対象要素のズーム
  4. 04字幕を標準で追加
  5. 05GitHubリポジトリのスキャンとデモ検出

活用シーン

  1. 01製品ページを動画で解説
  2. 02ドキュメントや記事を動画で案内
  3. 03GitHubリポジトリとライブデモを紹介

MCP 対応

MCP経由でアバターや画像の生成、メディア分析、音声合成、リップシンク、字幕、動画合成、残高確認、タスク状態の確認を行います。MCPサーバーとして機能を公開するとの記載はありません。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/pika-labs/pika-plugins --skill explainer

インストール後、Claude Code で/explainerを入力して読み込まれたか確認します。

詳細

/pika:explainer

約60~80秒のURL解説動画を生成します。ビートシートのタイムラインに沿って実際のブラウザーでURLを操作し、ナレーションに合わせたアバターのリップシンクを生成して、1280×800のmacOS Sonomaフレームに合成します。アバターは内側が240ピクセル、3pxの白いストローク枠を含む外側が246ピクセルで、キャンバス上の位置は(20, 476)です。中盤の各ビートでは、要素を指定してズームします。あらゆるURLで動作します。製品ページ、ドキュメントサイト、ブログ記事、ローンチページに対応しています。GitHub URLでは、リポジトリ対応モード(READMEのスキャン+ライブデモの検出)が有効になります。それ以外のURLでは、汎用ページウォークスルーのフローを使用します。

使い方: /pika:explainer <url> [--focus "angles"] [--avatar <url>] [--voice <id>] [--lipsync-provider pika|kling] [--preview] [--live-url <url>]

コストの透明性に関するゲート

有料のMCP呼び出しを行う前に、identity_balance({verbose: true})を一度呼び出してください。現在の残高、直近の消費ペース、残りの利用可能期間を提示してから、次の正確なメッセージで実行を確認してください。

推定コスト:約50~500クレジット(約$0.50~$5.00)。リップシンクプロバイダー、ナレーション、字幕、プレビューのモードによって異なります。$5に達する可能性があります。続行するにはproceed、停止するにはcancelと返信してください。

ユーザーがproceedと返信するまで、有料のMCPツールを呼び出してはいけません。ユーザーがcancelと返信した場合は、生成せずに停止してください。このゲートは、URLと任意のフラグが確定した後、アバター生成、音声生成、リップシンク、字幕、動画合成を行う前に実行します。

動作

デフォルト — すばやく実行し、途中で不要な確認をしない
  • アバター/音声は確認なしで決定します。 実行前に「あなたのアバターを使いますか?」や「どの音声にしますか?」と尋ねてはいけません。明示的な上書き指定(--avatar、--voice)があればそれを使用し、なければプレゼンター用アバターを生成してデフォルトの音声を選び、そのまま進めます。解決方法の全体はステップ1を参照してください。
  • proceedを求めるのはコストの透明性に関するゲートだけです。 ステップ5のプレビューは、明示的に指定されたアバターの場合、通常は--previewによるオプトインです。ただし、アバターのソースがgeneratedまたはregeneratedのフォールバックである場合は、自動プレビューが必須になります。コストゲートの後は、このフォールバックアバターのプレビューという必須の安全策を除き、最後まで一気に実行します。
  • --focusも尋ねないでください。 ページ構造に基づいて確信を持って最初の案を作成します。切り口が合わなかった場合は、ユーザーが--focus "X"を指定して再実行します。

これらのデフォルト設定は、メディア生成ツール(Midjourney/Sora/Runway/HeyGen/Pika.art)の業界標準である「送信 → レンダリング → 返却」に準拠しています。アカウントのクレジット残高とプロバイダーのフェイルオーバー(ステップ9)が、標準的な安全策です。

Claude Desktopでのローカルアバター画像

Claude Desktopでは、MCPツールにインラインで貼り付けた画像をまだ渡せません(Anthropic側の制限です)。ユーザーが写真をインラインで貼り付けた場合、または--avatarに指定するローカルファイルについて言及した場合は、ステップ1を一時停止し、次のように案内してください。

ご注意ください。Claude Desktopでは、貼り付けた画像はまだMCPツールに渡りません(Anthropic側の制限です)。アバターを指定する簡単な方法は2つあります。

  • URLを貼り付ける: すでにホストされている場合(Imgur、S3、ご自身のサイトなど)は、これが最も早い方法です。
  • 画像ファイルを添付する: こちらで生成前にアップロードできます。

ローカルファイルを受け取ったら、upload_assetで公開URLに変換し、返されたpublic_urlを--avatar <url>として使用してから、ステップ1に進んでください。すでにホストされているhttps://...のURLはそのまま使えるため、この手順は不要です。アバターがまったく指定されていない場合は、ステップ1でプレゼンターのポートレートを確認なしで生成します。

ステップ 0 — URL の解決(引数が空の場合のメニュー)

$ARGUMENTS からフラグ(--focus、--avatar、--voice、--live-url、--lipsync-provider、--no-captions、--preview、--skip-preview、--yes)と key=value パラメーターを取り除きます。残った文字列に https://... の URL が含まれていない場合(または空/空白のみの場合)は、このメニューをそのまま全体の応答として表示し、停止してユーザーの次のメッセージを待ちます。ここでツールを呼び出すと、誤ったページを記録したり説明したりするおそれがあります。$ARGUMENTS にすでに URL が含まれている場合は、このステップを無言でスキップし、ステップ 1 に進みます。

どの URL を案内しましょうか? 以下のいずれにも対応しています。

  • GitHub リポジトリ — 例:https://github.com/anthropics/claude-code(リポジトリ対応モードが有効になります:README のスキャン+ライブデモの検出)
  • 製品ページ/ローンチページ — 例:https://pika.art
  • ドキュメントサイト — 例:https://docs.anthropic.com
  • ブログ記事/記事の URL

出力:1280×800 の macOS Sonoma フレーム。左下にアバターのリップシンクを配置し、中盤の各ビートで要素を対象にズームします。デフォルトのフローはコストゲートの後、最初から最後まで実行されます。明示的に指定したアバターのリップシンクを 3 秒間確認したい場合は、--preview を渡してください。生成または再生成されたフォールバックアバターでは、このプレビューのガードレールが自動的に実行されます。

URL を返信すると、開始します。

ヒント:/pika:explainer と入力する必要はありません。「 を案内して」「 のデモ動画を作って」「このリポジトリを説明して:」のように伝えるだけで、このスキルが自動的に実行されます。

ユーザーが URL を返信したら、それを解決済みの入力として扱い、ステップ 1 に進みます。再度プロンプトを表示しないでください。

ステップ 1 — 入力の解析とモードの検出

必須:url(https://... である必要があります)。 任意:--avatar <url>(プレゼンターの写真。省略した場合は生成されます)、--voice <minimax-voice-id>、--focus "..."(vo_text に織り込む編集上のガイダンス)、--live-url <url>(ライブデモ URL を強制指定 — GitHub モードのみ)、--lipsync-provider <pika|kling>(デフォルトは pika — parrot a2v、所要時間は約 2〜5 分で、頭の動きはやや大きめです。顔を中心にした、より安定した出力には kling を渡してください。所要時間は約 5〜30 分です。Kling はプレゼンターの頭の動きを最小限に抑えますが、最も時間のかかる工程です。重要度の高いレンダリング用に確保してください)、--no-captions(ステップ 11 の字幕焼き込みをスキップ — デフォルトでは字幕が有効です)、--preview(明示的に指定したアバターでステップ 5 のプレビューゲートを有効にします。生成または再生成されたフォールバックアバターでは、完全なリップシンクの前にプレビューゲートが自動的に実行されます)。後方互換性のため、--skip-preview と --yes は何もしないオプションとして受け付けます。

モードの検出:

  • GitHub モード — URL のホストが github.com で、パスが /{owner}/{repo} に一致する場合(リポジトリのルートより先にパス要素がない場合)。リポジトリ対応の追加機能(README のスキャン、ライブデモの検出、GitHub 固有のセレクター)が有効になります。
  • 汎用 URL モード — それ以外のすべて(製品ページ、ドキュメントサイト、ブログ記事、/blob/HEAD/path のような GitHub の階層が深いパスなど)。GitHub 固有の追加機能をスキップし、汎用 CSS セレクターを使って URL 自体を案内します。

アバターの解決(無言で実行 — ユーザーには尋ねないでください):

  1. --avatar <url> が渡された場合は、それを使用します。
  2. それ以外の場合は、プロンプト "professional presenter, friendly tech narrator, studio portrait, 1:1, natural lighting" を指定して generate_image を 1 回呼び出し、返された URL を使用します。「生成しますか?」とユーザーに尋ねないでください。無言で生成してください。

avatar_source は explicit、generated、regenerated のいずれかとして記録します。

アバター適性ゲート(リップシンクに費用をかける前に必ず実施):

ステップ 4 の TTS より前、および generate_lipsync のプレビュー/完全版を呼び出すより前に、解決済みのアバター画像に対して analyze_media(media=<avatar>, query=<gate_query>) を 1 回呼び出します。これはアバターの分析が必須となる唯一のケースです。アバターはプレゼンターの中心的な素材であり、生成されたアバターの品質が悪いと、レンダリング全体のコストが無駄になる可能性があるためです。

ゲートのクエリ:

Return JSON only: {
  "is_single_front_facing_coherent_human_face": boolean,
  "has_visible_mouth": boolean,
  "is_faceless_or_masked": boolean,
  "is_mascot_illustration_or_non_human": boolean,
  "face_suitability_score": 0-100,
  "apparent_gender": "female" | "male" | "unclear",
  "reason": string
}
Is this image suitable for talking-head lipsync: a single front-facing coherent human face with a visible mouth? Flag faceless, masked, mascot, illustration-only, non-human, or distorted avatars.

is_single_front_facing_coherent_human_face == true、has_visible_mouth == true、is_faceless_or_masked == false、is_mascot_illustration_or_non_human == false、かつ face_suitability_score >= 75 の場合にのみ合格とします。

アバターが不合格で、avatar_source が explicit の場合は、有料のリップシンクに進む前に停止し、--avatar <real-looking-photo-url> を指定するか、プレゼンターのポートレート生成を許可するようユーザーに求めます。アバターが不合格で、avatar_source が generated の場合は、プロンプト "realistic professional presenter portrait, single front-facing coherent human face, visible mouth, friendly tech narrator, neutral studio background, 1:1, natural lighting" を指定して generate_image を1回呼び出し、avatar_source = "regenerated" に設定してから、再生成したアバターでこの適性ゲートを再実行します。再生成したアバターも不合格の場合は、リップシンクを試みず、明確なエラーを出して中止します。

avatar_source が generated または regenerated の場合は、avatar_auto_preview_required = true に設定します。それ以外の場合は、ユーザーが --preview を指定していない限り、false に設定します。

音声の解決(サイレント — ユーザーには決して尋ねない):

  1. --voice <id> が指定されている場合は、それを使用します。

  2. それ以外の場合は、解決済みアバターから推測される性別に合った、カジュアルな MiniMax speech-2.8-hd プリセットを選択します。

    • 女性に見えるアバター → English_PlayfulGirl(温かみのあるカジュアルな声で、明確に女性の声 — 確認済み)
    • 男性に見えるアバター → English_Jovialman(温かみのあるカジュアルな男性の声)
    • 判別できない/ジェンダーニュートラル → English_Jovialman(デフォルト)

    性別はアバター適性ゲートの結果(apparent_gender)から推測します。ユーザーには尋ねないでください。

    English_FriendlyPerson は使用しないでください — MiniMax のカタログでは「女性」に分類されていますが、表示名は「Friendly Guy」で、再生すると男性の声に聞こえます。English_PlayfulGirl が、カジュアルな女性の声として標準の選択肢です。女性の声として確認済みのその他の候補:English_Upbeat_Woman、English_LovelyGirl、English_radiant_girl。

以下のフローでは、各ステップに GitHub のみ、汎用のみ、または 両方 のモードを注記しています。

ステップ 2 — ソースを読み込む(MCP 呼び出しなし)

両方のモード: 入力 URL に対して Claude の WebFetch を使用し、ページの主要なコンテンツ(h1、ヒーローセクション、見出し、主要な本文)を取得します。

ソースの読み込み中に proper_noun_glossary を作成します。URL/ドメイン、ページタイトル、h1、README の見出し、リポジトリのメタデータ、パッケージ名、繰り返し出現する大文字表記のトークンから、製品名、リポジトリ名、会社名、モデル名、フレームワーク名、固有名詞の正式な表記を含めます。GitHub リポジトリの場合は、README/ソースのスキャンで確認できた正確な表記を維持します(例:Ollama、Llama、DeepSeek、Gemma)。この用語集は、後でナレーションを作成するときや手動字幕を付けるときに使用し、字幕の表記が「Olama」や「DeepSeq」のような誤った読みを反映しないようにします。

GitHub モードでの追加事項: トップレベルのファイルツリー、(可能な限り)package.json/pyproject.toml、および GitHub API のリポジトリメタデータ(gh api repos/{owner}/{repo} を使用した homepage、description、language、topics)も取得します。以下の優先順位で候補の live_url を検出します。

  1. ユーザー指定の --live-url。
  2. GitHub API の meta.homepage フィールド — メンテナーが GitHub の設定でリポジトリのホームページを設定している場合に値が入ります。
  3. package.json の "homepage" フィールド。
  4. README で最初に見つかった https?://[^\s)\"'<>]+(?:vercel\.app|netlify\.app|github\.io|fly\.dev|railway\.app|render\.com|herokuapp\.com|surge\.sh)[^\s)\"'<>]* に一致する URL。
  5. README のバッジ領域、または「Live Demo」/「Project Page」/「Demo」のテキストが示すその他の URL。 上記の許可リスト正規表現では任意のカスタムドメイン(例:<project>-project-page.com)を検出できません。README がプロジェクトページを明示している場合は、github.io のフォールバックよりもそちらを優先します。
  6. GitHub Pages の慣例に基づく https://{owner}.github.io/{repo} — ただし、ディープツリーにフロントエンドを示すファイル(index.html、App.tsx、App.jsx、App.vue、app.py、main.py のいずれか)がある場合に限ります。

候補が見つからない場合、ビートシートではビート 6〜7 を省略します。

汎用 URL モード: ビートでたどる URL は入力 URL のみです。live_url の推定や追加のメタデータ取得は行いません。ステップ 2.5 とステップ 3.0 は省略し、ステップ 3 に進みます。

ステップ 2.5 — live_url に到達できるか確認する(GitHub モードのみ、MCP 呼び出しなし)

候補の live_url が選択されている場合、ビート 6〜7 を作成する前に、実際のコンテンツが配信されることを確認します。候補の URL に対して WebFetch を使用し、レスポンスを確認します。

  • レスポンスのステータスが 4xx/5xx の場合は、live_url を None に設定し、ビート 6〜7 を省略します。特に github.io のフォールバックは、ホスト名には到達できても、Pages が有効になっていないリポジトリでは 404(「There isn't a GitHub Pages site here」)を返すことがよくあります。この 404 ページを記録すると、解説動画の約 12 秒が誤ったコンテンツに費やされます。
  • レスポンスが GitHub Pages の「404 — There isn't a GitHub Pages site here.」テンプレートを表示する場合(ヒューリスティック:レスポンス本文に "There isn't a GitHub Pages site here" が含まれる)、live_url を削除し、ビート 6〜7 を省略します。
  • それ以外の場合は、ビート 6〜7 で live_url を使用します。

これは、短いタイムアウトを設定し、リダイレクトに追従して live_url を確認していた従来の到達性ゲートを踏襲しています。

ステップ 2.6 — 汎用 URL の事前確認(汎用 URL モードのみ、MCP 呼び出しなし)

GitHub 以外の URL を対象にビートを作成する前に、入力 URL に対して WebFetch を実行し、レスポンスを調べます。このステップでは、汎用 URL モードでよく発生する次の 3 つの問題を防ぎます。(a)コンテンツではなく CAPTCHA/ボットブロックのページを記録してしまう、(b)Cookie/同意バナーによって動画の最初の約 3 秒が占有される、(c)汎用 CSS セレクターではページの実際のヒーローやセクションを特定できない。

A. ボットブロック/CAPTCHA の検出 — 一致した場合は中止します:

レスポンス本文に次のいずれかが含まれている場合:

  • "Verify you are human"/"verify you are not a robot"
  • "captcha"/"CAPTCHA"/"reCAPTCHA"
  • "403 Forbidden"/"Access Denied"
  • "Just a moment" と cf-chl-bypass の両方(Cloudflare のチャレンジ)
  • "We're sorry, something went wrong"(Amazon 形式のボットブロック)
  • <title> または h1 が「Robot Check」/「Are you a robot?」のみの場合 → 中止し、ユーザーに次の明確なエラーを表示します。"Generic-URL mode can't render this site — the page is showing a bot-detection / captcha challenge under headless Chrome. Try a different URL, or run a real-user version of the page first to verify it loads cleanly."

B. Cookie / consent-banner の検出 — extra_css と任意のクリックで無効化します:

レスポンスから次のパターンを検索します(大文字と小文字は区別しません):

  • onetrust-、truste-、cookie-banner、cookie-consent、gdpr-、consent-、cmp- で始まる ID / クラス
  • (?i)accept (all )?cookies / (?i)agree.{0,10}cookies / (?i)i (accept|agree) に一致するボタン
  • Apple 固有のバナー:ID ac-gdpr-banner またはクラス as-globalfooter-curtain
  • Google の同意画面:テキスト「Before you continue」を含む [role="dialog"]

検出した場合は、cookie_banner_present = true を設定します。多層防御のため、記録では次の両方を使用します:

  1. **CSS の挿入(extra_css)**を capture_website 呼び出しで行い、一般的なバナーを一律に非表示にします。下のクリックが失敗しても、バナーは視覚的に表示されなくなります。
  2. 最も可能性の高い閉じるセレクター(例:#onetrust-accept-btn-handler、[aria-label*="Accept all" i]、button[id*="accept"])を WebFetch の DOM から抽出し、at_s: 0.0 で click timed_action を実行します。

extra_css のペイロード(そのまま使用してください。約 80% の同意管理プラットフォームをカバーします):

#onetrust-banner-sdk, #onetrust-pc-sdk, #onetrust-consent-sdk { display: none !important; }
#truste-consent-track, #truste-consent-content, .truste_box_overlay { display: none !important; }
[id*="gdpr-cookie"], [id*="cookie-consent"], [id*="cookie-banner"] { display: none !important; }
[class*="cookie-banner"], [class*="cookie-consent"], [class*="consent-banner"] { display: none !important; }
[class*="CookieBanner"], [class*="CookieConsent"], [class*="ConsentBanner"] { display: none !important; }
#ac-gdpr-banner, .as-globalfooter-curtain { display: none !important; }  /* Apple */
[role="dialog"][aria-label*="cookie" i], [role="dialog"][aria-label*="consent" i] { display: none !important; }
.cmp-container, .cmp-modal, .cmp-banner { display: none !important; }

C. 実 DOM 要素の特定 — 具体的なセレクターを出力します:

一般的な CSS セレクター(h1、[class*="hero"]、section h2)は、セマンティックなサイトや適切にマークアップされたサイトでは機能しますが、大手企業サイトの難読化されたクラス名には一致しません(apple.com では hero-* ではなく、tile-headline / as-headline-section-title が使われています)。各ビートでは、WebFetch で確認した実際の DOM 要素を優先します:

  • WebFetch が返したレンダリング済み HTML / Markdown を読みます。ページの実際のプライマリ <h1> のテキストとクラスを確認します。
  • ページのセクション構造(h2 見出しとその親コンテナ)を確認します。
  • 目立つ CTA / サインアップ / 料金要素があれば確認します。
  • すべての Generic-URL zoom_target.selector に対して、次のセレクターの優先順位を上から順に適用します:
    1. レンダリング済み DOM で確認した安定した ID またはクラス(#hero、.tile-headline、.as-headline-section-title、.pricing-card)。人が読みやすく、具体的な場合に使用します。
    2. CTA またはナビゲーションのビートには、アクセシビリティ属性 / リンク属性([aria-label*="Get started" i]、a[href*="pricing"]、a[href="/new"])を使用します。
    3. クラス名が生成されたように見える場合(Tailwind _1a2b3c、CSS modules module__hero___xYz)は、セマンティックな構造(main > section:nth-of-type(N) h2、main section:nth-of-type(N) [role="img"]、footer h2)を使用します。
    4. 最初の 3 つの選択肢が使えない場合に限り、広範なフォールバック(h1、main、section h2、button、a[href])を使用します。

出力するセレクターはすべて、document.querySelector で解決できる標準 CSS でなければなりません。:contains(...)、:has-text(...)、text=...、XPath は出力しないでください。これらは Playwright / テキスト検索用の便利な記法であり、capture_website のスムーズスクロール経路では機能しません。テキストによる照合が必要な場合は、近くにある安定した href、aria-label、ID / クラス、または位置セレクターを対象にしてください。

D. SPA / 遅延レンダリングの検出 — 初期待機時間を延長します:

WebFetch のレスポンスに表示可能な見出しが 3 つ未満しかない、またはテキストコンテンツがごくわずかな場合、ページが domcontentloaded 後に SPA としてレンダリングされている可能性があります。デフォルトの 600ms の安定待機時間ではなく、ビートの実行前に、初期の wait アクションを長く設定します({type: "wait", at_s: 0.0, ms: 2500})。

E. --focus が指定された場合は尊重します(要求しないでください):

--focus が指定されていない場合は、一般的な構造上の手がかりからビートを選び、確信を持って最初の試行を行います。実行前に「何に注目すればよいですか?」とユーザーに尋ねてはいけません。最初の試行で希望する切り口を逃した場合、ユーザーは --focus "the X feature" を付けて再実行します。--focus が指定されている場合は、そのフレーズに基づいてビートを選びます。一致する具体的なページセクションを使い、無関係なマーケティング要素は無視します。

ステップ 3.0 — 必須 README セクションのスキャン(GitHub モードのみ、MCP 呼び出しなし)

ビートシートを作成する前に、README 全体を対象に、大文字と小文字を区別せずに、次のセクション名が含まれていないかスキャンしてください。一致するものが見つかった場合は、ステップ 3 でそのセクション専用のビートを必ず追加してください。必要に応じて、汎用ビート 4〜5 のいずれかと置き換えてください。

README に含まれる語句…必須のビート
overview / what is該当する見出しまで scroll_to し、.markdown-heading:has(#user-content-overview) .heading-element(または対応するスラッグ)をズームします
how it works該当する見出しまで scroll_to し、.markdown-heading:has(#user-content-how-it-works) .heading-element をズームします
audio layer / audio timelineオーディオレイヤーの図まで scroll_to し、レンダリングされた図、またはその周囲の見出しをズームします
claude code / mcp integration該当するセクションまで scroll_to し、article pre または .highlight(ターミナルのスクリーンショット/コードブロック)をズームします
architecture / system design該当するセクションまで scroll_to し、.markdown-heading:has(#user-content-architecture) .heading-element をズームします
features(冒頭で目立つ場合)該当する見出しまで scroll_to し、.markdown-heading:has(#user-content-features) .heading-element をズームします
getting started / quick start / installation該当する見出しまで scroll_to し、.markdown-heading:has(#user-content-installation) .heading-element(または対応するスラッグ)をズームします。代わりにインストール用コードブロックを使う場合は article pre にフォールバックします
usage / examples該当する見出しまで scroll_to し、.markdown-heading:has(#user-content-usage) .heading-element(または対応するスラッグ)をズームします。または、その下にある最初のコードブロックを使います

GitHub の見出しスラッグのルール: 小文字にし、スペースをハイフンに置き換え、[a-z0-9-] 以外の文字を削除します。たとえば、「How it works」は #user-content-how-it-works、「Quick Start」は #user-content-quick-start になります。現在、GitHub は README の見出しを .markdown-heading ラッパー、.heading-element、および兄弟要素のパーマリンクアンカーとしてレンダリングします。スラッグでラッパーを指定してから、見出しを指定してください:.markdown-heading:has(#user-content-<slug>) .heading-element。

2026-05-28 に検証済みの GitHub DOM: 現在のリポジトリルートページでは、リポジトリ名は strong[itemprop="name"] a、レンダリングされた README 本文は .markdown-body、README のタイトルは .markdown-body h1.heading-element、README のセクション見出しは .markdown-heading:has(#user-content-<slug>) .heading-element で取得できます。また、セクション見出し全般には .markdown-body h2.heading-element を使用できます。検証中、GitHub のビートで action_bboxes[].found の値が false の場合は、続行せずに GitHub の現在の DOM を再検証してください。リポジトリのビジュアルを巡る際、デフォルト位置へのフォールバックに頼ってはいけません。

セレクターの要件: bbox_selector は、document.querySelector で解決できる標準 CSS である必要があります(capture_website はアクション後のスムーズスクロール JS を page.evaluate 経由で実行し、ブラウザーのネイティブセレクターエンジンを使用します)。:has-text("...")、text=...、:visible などの Playwright 拡張は避けてください。これらは Playwright の page.query_selector では解決されるため bbox のキャプチャ時には要素を見つけられますが、スムーズスクロールの document.querySelector では失敗し、ページが目的の位置までスクロールされません。その結果、bbox.y が top - 60 px ではなくドキュメント上の Y 座標になり、ステップ 8b の bbox.y > recording_viewport.h という退化フィルターに該当して、デフォルト位置へのズームにフォールバックします。CSS Level 4 の :has(...) は標準 CSS であり、最新の Chromium でサポートされています。

これらのセクションは、説明に適したリポジトリの多くで最も情報量の多いビジュアルです。見落とすと一般的なウォークスルーになりますが、含めることで、説明の各ビートに具体的な「語るより見せる」要素を加えられます。ステップ 3.0 では、これらを付随的な指針ではなく必須要件として扱い、OSS の README によくある、情報量の多い見出しを 7 つ挙げています。

ステップ 3 — ビートシートの作成(メインスレッド、MCP 呼び出しなし)

8〜10 個のビートからなる JSON 配列を作成します。合計時間は必ず 65〜80 秒、合計語数は必ず 165〜200 語とします(読み上げ速度は毎秒 2.5 語を想定)。各ビート:

{
  "t_start": 0.0,
  "t_end": 7.5,
  "action": { "type": "navigate" | "scroll_to" | "hover", "url": "...", "selector": "..." },
  "zoom_target": { "selector": "...", "description": "..." },
  "vo_text": "exact words to speak — 1 to 2 conversational sentences"
}

厳守すべき制約(ビートシートを出力する前に検証してください。違反があればドラフトを却下してください):

  1. 各ビートに、t_start、t_end、action(type と url を含む)、zoom_target(selector を含む)、vo_text の5項目をすべて含めてください。項目が欠けている場合は、却下して作成し直してください。
  2. ビート0の t_start = 0.0 とします。t_end[i] == t_start[i+1] とし、連続性を保ってください。
  3. 各ビートについて、len(vo_text.split()) / 2.5 ≈ t_end - t_start となるようにしてください。この推定値の±10%を目標にします。ドラフトの速度が2.5 wpsを超える場合は、収まるまで vo_text を短くしてください。
  4. 最後のビートの t_end の合計は80秒以下としてください。(参考出力はイントロを含めて86.5秒、リップシンク音声は約83秒です。現在の負荷では、Klingのアバター/image2videoが音声90秒を超えると安定して停止します。80秒を超えると、Klingが20分でタイムアウトするリスクがあります。)
  5. 発話語数の合計は165~200語としてください。
  6. 各ビートの zoom_target.selector は、そのビートの移動先ページで有効なCSSセレクターである必要があります。GitHubモードでは、現在のGitHubリポジトリ/READMEのセレクターを優先してください:strong[itemprop="name"] a、.markdown-body h1.heading-element、.markdown-heading:has(#user-content-<slug>) .heading-element、.markdown-body h2.heading-element、.blob-code-inner、.highlight、.octicon-star、nav。汎用URLモードでは、堅牢な汎用セレクターを優先してください:h1、[role="main"]、main、header、nav、.hero、.feature、section h2、[class*="cta"]、[class*="hero"]、button、a[href]。**セレクターは、ビートのアクションが完了した後のレンダリング済みページで解決できる必要があります。**出力前に、WebFetchで確認できるDOMに対して検証してください。
  7. vo_text は会話調の1~2文にしてください。開発者の語り口にします。演出指示は含めないでください。Markdownは使用しないでください。
  8. action.type == "navigate" の場合、action.url は有効な https://... URLである必要があります。必須です。

Step 4の前のセルフチェック: total_words が [165, 200] の範囲内、かつ total_seconds(= beats[-1].t_end)が [65, 80] の範囲内であることを確認してください。どちらかが範囲外なら、ビートシートを作り直してください。TTSには進まないでください。(どこかに「出力」する必要はありません。これは内部ドラフトの検証です。条件を満たすまでドラフトを却下して作り直してください。)

構成の骨格 — GitHubモード(ビジュアル要件の要。元の構成に合わせてください。ただし、該当する場合はStep 3.0が優先されます):

  • ビート1: リポジトリのルートに navigate し、strong[itemprop="name"] a(リポジトリのタイトル)にズームし、フックとなる文を入れます。
  • ビート2~3: 特定のソースファイル(https://github.com/{owner}/{repo}/blob/HEAD/<path>)に navigate し、.blob-code-inner または .highlight にズームします。ナレーションの主張に合ったファイルを選んでください。触れないファイルには移動しないでください。
  • ビート4~5: READMEのセクションまで scroll_to し、.markdown-heading:has(#user-content-<slug>) .heading-element、.markdown-body h2.heading-element、または .markdown-body にズームします。Step 3.0で必須セクションが示された場合は、これらのスロットを必須セクションに置き換えてください。
  • ビート6~7(live_url がStep 2.5を通過した場合のみ): live_url に navigate し、nav/h1/.hero/main/button/.feature にズームします。
  • ビート8: リポジトリのルートに戻り、.octicon-star にズームして締めくくります。

構成の骨格 — 汎用URLモード:

  • ビート1: 入力URLに navigate し、h1 または [class*="hero"] h1(ページの主見出し)にズームして、フックとなる文を入れます。
  • ビート2~3: ページのヒーロー/価値提案/最初の機能セクションまで scroll_to します。.hero、[class*="hero"]、[class*="feature"]、または section:nth-of-type(1) h2 にズームします。ナレーションで触れる、表示中の要素を選んでください。
  • ビート4~5: さらに下のセクション(機能一覧、スクリーンショット、料金、利用者の評価)まで scroll_to します。section h2、[class*="feature"] img、[class*="testimonial"]、[class*="pricing"]、またはページ上の目立つ意味的要素にズームします。
  • ビート6~7: CTA/サインアップ/デモ埋め込みまで scroll_to します。[class*="cta"]、button、a[class*="button"]、または [id*="signup"] にズームします。(汎用モードではライブデモへの移動はありません。入力URL自体がデモです。)
  • ビート8: フッター/締めくくりの要素まで scroll_to し、footer h2、footer、またはトップに戻って h1 にズームします。締めくくりの文を入れます。 --focus が指定されている場合は、構造的な骨組みを変更せずに、その観点を vo_text に織り込んでください。zoom_target.selector では text_content より CSS セレクターを優先してください。バウンディングボックスの取得にはセレクターのみを使用します(「既知の制約」を参照)。
ステップ 4 — TTS

provider: "minimax-tts"、text: <full vo_text join>、任意の voice_id(--voice またはステップ 1 のデフォルトプリセットから)を指定して generate_speech を呼び出してください。result.audio_url(ディスパッチャーは音声を url ではなく audio_url で返します)と result.duration_seconds を取得します。

古い音声のフォールバック検出: ディスパッチャーは、Minimax の status_code:2054(音声 ID が見つからない。通常は、Minimax が非アクティブ状態が 7 日続くと自動削除したエージェントごとのワークスペースポインター)に対して、デフォルトの Calm_Woman 音声で 1 回再試行します。再試行が成功すると、レスポンスには文書化されたスキーマにない追加フィールドが 2 つ(そのまま渡されます)含まれます。voice_id_requested(ワーカーが最初に試した、登録済みだが古くなった ID)と、fallback_reason: "invalid_minimax_voice_id" です。レスポンスに fallback_reason == "invalid_minimax_voice_id" がある場合は、ユーザーに次のような一文で知らせてください。「Minimax で登録済み音声の有効期限が切れました(非アクティブ状態が 7 日続いたため自動削除されました)。システムのデフォルト音声を使用しました。パーソナライズを再び利用するには、clone_voice で音声を再クローンしてください。」レンダリングは失敗せず、デフォルト音声が使われるだけなので、これは情報提供であり、再試行のきっかけではありません。

Cookie バナー表示時の音声パディング(ステップ 2.6 §B で cookie_banner_present == true となった汎用 URL モード): ステップ 4 の呼び出しでデフォルトの MiniMax 経路を使う場合は、generate_speech を呼び出す前に、MiniMax のポーズマーカー <#1.5#> を text: 引数の先頭に追加してください。MiniMax の speech-2.8-hd は <#N#> を N 秒間の無音として扱います。返される audio_url と duration_seconds には、1.5 秒のリードインが最初から含まれます。ステップ 4 で provider="elevenlabs" を使う場合は、MiniMax のマーカーを使わず、以下のフォールバック音声ミックスによるパディングを使ってください。これにより、ステップ 4.5 で適用される画面録画の Cookie 非表示後 +1.5 秒のオフセットに音声を合わせられます。

フォールバック(スモークテストで、この音声ではマーカーが無視されることが確認された場合のみ):通常どおり generate_speech を呼び出した後、edit_audio_mix を呼び出して、結果を 1.5 秒の無音ベースに 1.5 秒のオフセットで重ねてください。その後、analyze_media(url=<padded_audio_url>) を呼び出してパディング後の長さを調べ、ステップ 4.5 で使う前に duration_seconds = result.duration_seconds と再設定してください。 analyze_media は唯一の信頼できる長さの取得方法です。edit_audio_mix の戻り値に頼らないでください(その長さフィールドは、契約上保証されていません)。

ステップ 4.5 — 音声の長さの検証とビートシートの再スケーリング

ステップ 4 の後に audio_duration_seconds に適用します(Cookie の導入パッドがある場合はその分も含みます)。最終状態では、beats[].t_start/t_end は音声の再生タイムラインに一致する絶対経過秒数になります。beats[] の変更はすべてここで行います。ステップ 6 と 8 は読み取り専用の処理です。

ゲート 1 — Kling の停止上限(プロバイダー上限、未加工の audio_duration_seconds): audio_duration_seconds > 90 の場合は中止し、より厳しい語数上限でビートシートを作り直します。Kling のアバター/image2video は約 90 秒を超えると停止します。

ゲート 2 — 退化した TTS(発話内容の長さ): narration_duration = audio_duration_seconds - (1.5 if cookie_banner_present else 0.0) を計算します。narration_duration < 30s の場合はステップ 4 を一度再試行し、再試行の音声から narration_duration を再計算します。再試行でも narration_duration < 30s の場合は中止して調査します。考えられる失敗の原因は、MiniMax の応答が途中で切れた、音声が無音、または vo_text が正しく結合されていないことです。

ゲート 3 — 再スケーリング:

  • narration_duration = audio_duration_seconds - (1.5 if cookie_banner_present else 0.0)
  • scale = narration_duration / beats[-1].t_end
  • scale < 0.5 または scale > 1.5 の場合は中止して作り直します。TTS の構造が壊れている(または語数上限が大幅にずれている)状態です。再スケーリングでは修正できません。
  • 各ビートについて、beat.t_start *= scale; beat.t_end *= scale
  • cookie_banner_present の場合、各ビートについて beat.t_start += 1.5; beat.t_end += 1.5
  • 最終クランプ: beats[-1].t_end = audio_duration_seconds(厳密に一致)。Cookie モードの有無や浮動小数点の累積誤差にかかわらず、不変条件の浮動小数点数の一致を保証します。

ゲート 3 を通過した後、実行後の診断でスケール値を確認できるよう、運用ログを 1 行出力します。

Rescaled beats by scale=X.XX (audio=Y.YYs, narration_duration=Z.ZZs, cookie_pad=W.Ws)

参考情報(ゲートではありません): スケールが 1.0 に近いほど理想的です。scale > 1.2 は、音声が予測よりかなり遅いことを意味します。映像は「引き伸ばされた」ように感じられますが、同期は保たれます。scale < 0.85 は、音声が速いことを意味します。映像は「急ぎ足」に感じられますが、同期は保たれます。どちらもゲートを通過します。「同期していない」ではなく「テンポがずれている」とユーザーが報告した場合は、語数上限を厳しくするか緩めて作り直します。

ステップ 5 — プレビューゲート(オプトイン、またはフォールバックアバターの自動プレビュー)

ユーザーが --preview を指定せず、かつ avatar_auto_preview_required == false の場合に限り、ステップ 5 をスキップします。アバターが通知なしに生成されたフォールバック、またはアバター適合性ゲートの後に再生成されたアバターの場合は、自動プレビューが必須です。顔のないアバターや人間以外のアバターが気づかれずに入り込む可能性がある経路のため、低コストの短いプレビューをガードレールとして、60〜80 秒のリップシンク処理にコストをかける前に確認します。

後方互換性のため、--skip-preview と --yes は何もしないオプションとして受け付けます。これらは以前のオプトアウトフラグです。

--preview が指定された場合、または avatar_auto_preview_required == true の場合:

  1. generate_speech を provider: "minimax-tts"、任意の voice_id、および text: "Hi, I'm your presenter. Let's explore this repo together." で実行します → preview_audio_url。

  2. generate_lipsync を provider: <resolved_lipsync_provider>(デフォルトは pika。指定されている場合は --lipsync-provider kling に従います)、image: <avatar>、audio: preview_audio_url で実行します → preview_lipsync_url(リップシンクのみ、約 3 秒)。ここでは、ステップ 9 で全音声に使うものと同じプロバイダーを使用します。プレビューの目的は、時間のかかるレンダリングの前に、アバター+音声+プロバイダーの組み合わせを確認することです。

  3. 次の内容をそのままユーザーに提示します。

    プレビューの準備ができました:<preview_lipsync_url> アバターと音声の組み合わせを確認できます。全体のレンダリングにはかなり時間がかかります(全音声の Kling リップシンクで約 5〜30 分)。 続行するには yes、キャンセルするにはそれ以外を返信してください。

  4. ^(yes|go|proceed|confirm|y)$ に一致するか確認します(大文字と小文字は区別しません)。それ以外の場合は停止し、以降の MCP 呼び出しは行いません。

ステップ 6 — timed_actions の構築と記録

ビートシートを capture_website の timed_actions に変換してください。ビートごとに timed_action を1つ設定します。bbox_selector にはビートの zoom_target.selector を指定します。capture_website は対象要素のアクション後の bbox を内部でキャプチャします(従来の600 msの待機 → top - 60 px までスムーズスクロール → アニメーション後1300 ms待機 → 計測。すべてサーバー側で実行)。

各ビートについて、順番どおりに1つのエントリーを出力します。

  • navigate ビート:{type: "navigate", at_s: <t_start>, url: <action.url>, bbox_selector: <zoom_target.selector>}。ワーカーはページに移動し、絶対時刻 at_s + 0.6 s まで待機してから、bbox_selector を表示範囲内にスクロールし、bbox を計測します。呼び出し側で後続のステップをスケジュールする必要はありません。
  • scroll_to / hover ビート:{type: "scroll", at_s: <t_start>, selector: <action.selector or zoom_target.selector>, bbox_selector: <zoom_target.selector>}。アクションの selector はページのスクロール先を指定し、bbox_selector は bbox の計測対象を指定します(同じセレクターでも異なるセレクターでもかまいません。通常は同じです)。capture_website には hover がないため、スクロールして表示範囲内に移動する操作が相当します。

作成したビートの前に、イントロ用のスクロール操作を追加しないでください。 リップシンク音声はビートシートの t=0 を基準にタイミングが設定されています。イントロを追加すると、画面録画は約3秒先送りされる一方、音声のタイミングは変わらず、音声と映像がずれてしまいます。録画は t=0 にビート0のURLがすでに読み込まれた状態で始まるため、最初に作成したビートが画面の基準点になります。

capture_website を呼び出します。

  • url: <ビート0のaction.url>
  • timed_actions: <上記の手順で作成したN要素のリスト>(ビートごとに1つ)
  • duration_s: ceil(audio_duration_seconds) — beats[].t_start と t_end はStep 4.5でTTS音声のタイムラインに合わせて再スケーリング済みのため、duration_s は音声の長さだけで決まります。TTSの超過を想定した従来の max(...) による保護は不要です。

汎用URLモードでの追加設定(Step 2.6の事前確認に従います)。

  • extra_css: <Step 2.6 §BのCookieバナーを隠すCSSペイロード> — 念のため、一般的な同意管理プラットフォームを display: none !important; で非表示にします。任意のクリック操作が失敗した場合でも、録画にバナーが映りません。
  • SPA/遅延レンダリングされるページでは、wait アクション {type: "wait", at_s: 0.0, ms: 2500} を先頭に追加します(Step 2.6 §Dに従います)。「通常」のページでは1500 msを指定します。これにより、最初のビートが実行される前に、ヒーロー画像の遅延読み込み、フォントの切り替え、スクロール時のアニメーションが完了する時間を確保できます。
  • Step 2.6 §Bで cookie_banner_present が確認された場合は、click アクション {type: "click", at_s: 0.5, selector: <WebFetch DOMから検出した閉じる操作用セレクター>} も先頭に追加します。Cookieバナーを閉じるため、beats[] 配列はStep 4.5ですでに +1.5s シフトされ、TTS音声にもStep 4ですでに1.5秒の無音が追加されています。ここでさらにシフトする必要はありません。Cookieモードでは、ビート1の timed_action.at_s は beats[0].t_start をそのまま参照し、その値は1.5です。
  • cookie_banner_present == false の場合、Cookieバナー用のアクションは不要です。先頭に wait アクションのみ追加します。

video_url、recording_viewport、action_bboxes を取得します。結果には、video_url とともに recording_viewport: {w, h} および action_bboxes: [{idx, selector, found, bbox: {x,y,w,h}}] が返されます。

action_bboxes[].idx の意味:idx フィールドは、入力の timed_actions 配列における位置です。

  • GitHubモード:ビートごとに timed_action が1つあるため、idx はビートのインデックスと1対1で対応します。Step 8では entry.idx をそのまま beat_idx として使用します。
  • 汎用URLモード:先頭に追加した wait(および任意のCookieバナーを閉じる click)により、配列の位置が1つまたは2つずれます。prepend_count が1(wait のみ)または2(wait + click)のとき、beat_idx = entry.idx - prepend_count として計算します。beat_idx < 0 のエントリーはスキップします(ビートではなく、先頭に追加したセットアップアクションです)。

各エントリーの selector フィールドには、アクション自体の selector ではなく、bbox_selector(つまり zoom_target.selector)が設定されます。 汎用 URL の bbox ヒット率に関する警告: capture_website の戻り後、Step 8 で測定値を使用する前に bbox のカバレッジを計算します。

bbox_total_count = number of authored beats with a zoom_target.selector
bbox_found_count = count of beat entries where found == true and bbox is not degenerate
bbox_hit_rate = bbox_found_count / max(1, bbox_total_count)

上記で説明した同じ prepend_count マッピングを使用し、前置されたセットアップアクションがヒット率に影響しないようにします。Step 8b のフィルター(bbox.y > recording_viewport.h または bbox.h > recording_viewport.h * 1.5)を使って、bbox が縮退しているかどうかを判定します。bbox_hit_rate < 0.70 の場合は、bbox_warning を次のユーザー向けの文に設定し、Step 12 まで引き継ぎます。

要素を対象としたズームは <missed>/<total> 個のビートで失敗したため、それらのビートではフレーム中央にズームします。このサイトでは、難読化されたクラス名やスクロールをトリガーとするレンダリングが使われている可能性があります。

Step 7 — ブラウザーの枠

edit_browser_frame:

  • video_url: <Step 6 video_url>
  • url: (GitHub モードで Step 2.5 を通過した場合は live_url、それ以外は input_url。65 文字に切り詰め)
  • tab_title: <30 文字のタイトル> — GitHub モード:(meta.description または repo_name、いずれもなければ "") の先頭 30 文字。汎用 URL モード:ページの <title>(Step 2 の WebFetch から取得)、または URL のホスト名を 30 文字に切り詰めます。None または空の場合に備えて処理します。

framed_url(1280×800 の Sonoma とブラウザー枠)を返します。

ステップ 8 — zoom_keyframes を構築して適用する

定数:

  • INTRO_BEATS = 2 — ビートシートのインデックスで判定します。ビートインデックス 0 と 1(上記の構造スケルトンの「Beat 1」と「Beat 2」)ではズームをスキップします。
  • HOLD_GAP = 0.6 — 各ズームインの前と各ズームアウトの後に設ける、1.0×で表示する秒数です。
  • MIN_BEAT_DUR = 1.5 — これより短いビートはスキップします(意味のあるズームを行う余地がありません)。
  • SCALE = 1.35(要素を正確にターゲットとするズーム)。
  • FALLBACK_SCALE = 1.25(使用可能な bbox がない場合のデフォルト位置のフォールバック)。
  • FALLBACK_RAMP = 0.4。

注意: beats[].t_start / t_end はステップ 4.5 で音声のタイムラインに合わせて再スケーリングされ(該当する場合は Cookie の時間分もシフトされ)ています。HOLD_GAP(0.6 秒)、MIN_BEAT_DUR(1.5 秒)、および 1.0 秒の内部区間チェックは、いずれもこれらの最終値を基準に処理します。これらはレンダリング後の動画における実際の表示秒数です。

edit_browser_frame のコンテンツ領域内オフセット:CONTENT_X=56, CONTENT_Y=108, CONTENT_W=1168, CONTENT_H=637。

座標変換(録画ピクセル → フレーム化後ピクセル):

cx_framed = 56  + (bbox.x + bbox.w/2) * (1168 / recording_viewport.w)
cy_framed = 108 + (bbox.y + bbox.h/2) * (637  / recording_viewport.h)

ビートごとのデフォルト値と bbox による上書きのパターンで、ズームリストを構築します。 従来のリグでは、「イントロ以外のすべてのビートをズームする。bbox から位置を導出できる場合はその位置を使い、できない場合はデフォルト位置を使う」というルールに従っていました。ここでもこの動作を再現します。

ステップ 8a — イントロ以外で、十分な長さのすべてのビートにデフォルト位置のキーフレームを事前設定します。

デフォルト位置の定数:

  • DEFAULT_CX = 56 + 1168 // 2(フレーム化したキャンバスの画面中央)
  • DEFAULT_CY = 108 + 637 // 3(コンテンツ領域の上 3 分の 1。GitHub UI で最も目立つ要素が多く表示される位置です)

ビートシートを INTRO_BEATS(= 2)のインデックスから末尾まで順に確認します。各ビートに対して、次の処理を行います。

  • t_end - t_start < MIN_BEAT_DUR(1.5 秒)の場合は、意味のあるズームを行うには短すぎるためスキップします。
  • キーフレームの内部区間を [t_start + HOLD_GAP, t_end - HOLD_GAP] として計算します。この区間が 1.0 秒未満の場合はスキップします。
  • それ以外の場合は、ビートごとのマップ(zoom_keyframes_by_beat[beat_idx] とします)で、そのビートのスロットに {cx: DEFAULT_CX, cy: DEFAULT_CY, scale: FALLBACK_SCALE (1.25), ramp_s: FALLBACK_RAMP (0.4)} と、トリミング後の t_start / t_end を事前設定します。

ステップ 8b — action_bboxes に使用可能な計測値がある場合は、bbox から導出した正確なズームで上書きします。

action_bboxes の各エントリに対して、次の処理を行います。

  • GitHub モード: ステップ 6 ではビートごとに 1 つの timed_action を出力するため、beat_idx = entry.idx とします。
  • 汎用 URL モード: ステップ 6 では待機アクションと、場合によっては Cookie を閉じるクリックを先頭に追加するため、beat_idx = entry.idx - prepend_count とします。beat_idx < 0 の場合はスキップします。このエントリは作成したビートではなく、セットアップに属します。
  • beat_idx < INTRO_BEATS の場合はスキップします。
  • entry.found が false の場合はスキップします。
  • ビートがすでに zoom_keyframes_by_beat に存在しない場合(ステップ 8a の MIN_BEAT_DUR / 1.0s ルールで除外済みの場合)はスキップします。
  • 不正な bbox を除外します: bbox.y > recording_viewport.h の場合(画面外でのキャプチャ。ページが間に合わず要素を表示位置までスクロールできていません)、または bbox.h > recording_viewport.h * 1.5 の場合(ページ全体を含む <main> 要素で、ズーム中心が意味のない位置になります)はスキップします。
  • 上記の録画ピクセル → フレーム化後ピクセルの変換を使い、bbox の中心から cx_framed / cy_framed を計算します。そのビートのスロットを {cx: cx_framed, cy: cy_framed, scale: SCALE (1.35), ramp_s: min(0.5, (t_end - t_start) * 0.15)} で上書きします。

最終リスト: zoom_keyframes_by_beat の値を t_start でソートし、zoom_keyframes 配列を作成します。

これにより、イントロ以外で十分な長さのすべてのビートがズームされます。bbox のキャプチャに成功した場合は正確な位置を使い、それ以外の場合はデフォルト位置を使います。実行時間全体を通して動画が「平坦」になる失敗を防ぎます。

len(zoom_keyframes) > 0 の場合は、video_url: framed_url, zoom_keyframes を指定して edit_animate_zoom を呼び出します。返される値は zoomed_url です。それ以外の場合(該当するビートがないケースは、ステップ 3 の 65~80 秒という制約を考えるとまれなはずです)はスキップし、framed_url を zoomed_url として使用します。

ステップ 9 — 音声全体にリップシンクを適用する

generate_lipsync:

  • provider: <resolved_lipsync_provider> — デフォルト:pika(parrot a2v)。--lipsync-provider kling が明示的に渡された場合は、それに従います。
  • image: <avatar>
  • audio: <Step 4 audio_url>
  • kling 専用の設定項目:provider == "kling" の場合、洗練されたプレゼンターらしい雰囲気にするため、mode: "pro" と prompt: "talking head, face centered, mouth syncs to audio, minimal head movement, professional presenter" を追加します。どちらも pika では無視されます(parrot には専用のドライバーがあります)。

プロバイダーごとのトレードオフ:

プロバイダー所要時間頭の動き使用する場面
pika(デフォルト)約2~5分やや大胆で、自然な動きほとんどの実行でデフォルトとして使用します。高速に反復でき、見やすい出力が得られ、kling より約10倍高速です
kling(オプトイン)約5~30分最小限で、顔を中央に配置したプレゼンター風アバターを洗練されたプレゼンターのように見せる必要がある重要度の高いレンダリングに使用します。長い処理時間を許容できる場合に適しています

サーバー側の await により呼び出しはインラインで処理されます。レスポンスの形式が {task_id, status: "queued"} の場合は、ステータスが終端状態(completed、failed、または cancelled)になるまで、間隔を空けずに task_status をポーリングします。completed になったら lipsync_url を取得します。failed または cancelled になった場合は、以下のフェイルオーバーの説明に従ってもう一方のプロバイダー(kling ↔ pika)に切り替えます。

フェイルオーバー:

  • pika が失敗した場合(まれです。parrot a2v は一般的な解説音声の長さで安定しています)→ provider: "kling" でもう一度試します。
  • kling がワーカーの上限である1200秒を超えて停止した場合(完了せずに processing ステータスが繰り返し表示されます)→ provider: "pika" に切り替えます。Step 4.5 の音声長チェックで長い音声のケースを事前に検出できますが、フェイルオーバーで残るリスクに対処します。

pika をデフォルトにする理由:

  • 速度 — リップシンクが最も時間のかかる処理のため、通常の解説動画では全体の所要時間が約10~15分から約5~7分に短縮されます。
  • 十分な品質 — parrot a2v は自然な仕上がりです。アバターを円形 PiP で表示する60~80秒のクリップでは、少し大きめの頭の動きが気を散らすものではなく、親しみやすさにつながります。
  • Kling の pro モードによる洗練は、246ピクセルの円の中ではほとんど分かりません。顔の表示領域が小さすぎるため、ほとんどの視聴者には頭の動きの違いが認識できません。

参照出力のような典型的な「洗練されたプレゼンター」の雰囲気にするには、--lipsync-provider kling を明示的に渡してください。

顔の一貫性チェック(PiP 合成前に必須):

lipsync_url を取得した後、Step 10 の前に未加工のリップシンク動画からフレームをサンプリングします。extract_frame(video_url=<lipsync_url>, at_times=[1.0, audio_duration_seconds * 0.5, max(1.0, audio_duration_seconds - 1.0)]) を呼び出して、開始・中間・終了のフレームを取得します。バッチモードでは frame_urls = extract_result.urls を取得し、urls がない場合のフォールバックを除き、従来の単一の url フィールドは無視します。analyze_media.media は URL 文字列を1つだけ受け取るため、各フレーム URL に対して analyze_media を1回ずつ呼び出します。配列は渡さないでください。

各 frame_url に対して、次のクエリを使って analyze_media(media=<frame_url>, query=<single_frame_gate_query>) を呼び出します。

Return JSON only: {
  "face_integrity_score": 0-100,
  "is_coherent_human_face": boolean,
  "has_visible_mouth": boolean,
  "is_abstract_melting_mask_or_non_human": boolean,
  "observation": string,
  "recommended_action": "pass" | "retry_other_provider" | "abort"
}
Does this frame show a coherent human presenter face with a visible mouth, not an abstract blob, melting mask, mascot, faceless illustration, or distorted non-human face?

続行する前に、各フレームの判定結果をまとめます。サンプリングしたすべてのフレームにおける face_integrity_score の最小値(最悪値)が >= 75 であり、すべてのフレームで is_coherent_human_face == true、has_visible_mouth == true、is_abstract_melting_mask_or_non_human == false を満たす場合のみ合格です。中止時のエラーメッセージで開始・中間・終了のどのフレームが失敗したかを示せるよう、フレームごとの観察結果を保持します。

最初のプロバイダーでチェックに失敗した場合は、同じアバターと音声を使って、もう一方のプロバイダー(pika ↔ kling)で1回だけ再試行し、2つ目の未加工の lipsync_url に対してこの顔の一貫性チェックを再実行します。両方のプロバイダーで失敗した場合は、明確なエラーを返して中止し、チェックの観察結果も返します。顔が崩れたプレゼンターのまま Step 10 に進んではいけません。2つ目のプロバイダーが合格した場合は、そのプロバイダーの lipsync_url を後続の処理で使用します。

Step 10 — PiP 合成

edit_pip:

  • main_video_url: <zoomed_url>
  • overlay_video_url: <lipsync_url>
  • shape: "circle"
  • size_px: 246 ← 外径を 246px に固定(内側のアバター 240px + ストロークのリング 3+3)
  • stroke_width_px: 3
  • stroke_color: "white"
  • position_px: {x: 20, y: 476} ← ドックとの間隔を確保するための 800 − 246 − 78

size ではなく size_px を渡します。これらのフィールドは相互排他的です。final_url が返されます。

マスター尺/音声ソースの契約: edit_pip はデフォルトで shortest=1 のセマンティクスを使用します。つまり、合成動画の尺は(ズーム済み画面録画)と(リップシンク動画)の短い方になります。Step 4.5 ではビートを音声のタイムラインに合わせて再スケーリングし、Step 6 の duration_s = ceil(audio_duration_seconds) によって、画面録画の長さはリップシンクと完全に一致します。合成動画の尺は、edit_pip の shortest=1 のセマンティクスにより、リップシンク動画で決まります。音声にはリップシンク動画の音声トラックを使用します(リップシンクには元の TTS 音声が埋め込まれています)。独立した audio_url は再ミックスされません。リップシンク動画が画面録画より短い場合(Kling が末尾の無音部分をカットすることがあります)、画面映像はリップシンク動画の終端で途切れますが、これを許容してください。代替策(画面映像のループ)は、解説コンテンツには適していません。

Step 11 — 字幕を焼き込む

最終的に結合した vo_text に proper_noun_glossary の正式表記を適用し、caption_script_text を作成します。解説動画では、ブランド名やモデル名の自動文字起こしに頼らないでください。ブラウザのフレームに正しいテキストが表示されていても、固有名詞が発音に基づいて誤記されることがあります。

ツールを呼び出す前に、字幕の配置を選択します。

  • 通常の解説ページでは、デフォルトの position: "bottom"(caption_position = "bottom")を使用します。
  • 現在のビートプランまたはキャプチャしたページで、重要なヒーローテキスト、見出し、バリュープロポジション、CTA、コードが下部 3 分の 1 にあり、従来の下部バーで隠れてしまう場合は、position: "top"(caption_position = "top")を使用します。

add_captions(video_url=<final_url>, style="classic", caption_mode: "manual", subtitle_text: <caption_script_text>, position: <caption_position>) を呼び出します。手動モードでは、修正済みのナレーションテキストを検出された尺に合わせて配置し、固有名詞の表記揺れを防ぎます。結果を captioned_url として取得します。

このステップを省略するのは、ユーザーが --no-captions を指定した場合(Step 1 で解析)に限ります。デフォルトでは字幕を付けます。(注:/pika:podcast では字幕を焼き込みません。解説動画のナレーションは、テンポの速い 2 人の対話よりも文字起こししやすいためです。)

Step 12 — 返却

Step 6 で bbox_warning が設定されていた場合、一部のビートで映像が中央部分のズームに劣化したことがユーザーに伝わるよう、最終 URL の直前に出力します。その後、captioned_url(Step 11 を省略した場合は final_url)を 1 行で出力します:Done: <url>。

飛行後の品質ゲート

成功を宣言する前に、captioned_url または final_url に対して analyze_media を呼び出し、構造化された判定を求めます。

Return JSON only: {
  "verdict": "clean" | "degraded" | "catastrophic",
  "observations": string[],
  "quality_warning": string | null,
  "re_roll_suggestion": string | null
}
Check that captions are present unless --no-captions was used, zoom target / bbox focus lands on the narrated UI element, there are no blank frames or white flash issues, and the avatar PiP does not cover important page content.
Also verify that the presenter face remains coherent (not abstract, melting, faceless, masked, or non-human), proper nouns from `proper_noun_glossary` are spelled correctly in captions, and captions do not occlude or cover hero text, headlines, value-prop copy, CTAs, or code that the narration is pointing at.
  • verdict が clean の場合、最終 URL を通常どおり出力します。
  • verdict が degraded の場合、最終 URL と quality_warning を出力し、公開前にユーザーが確認できるようにします。
  • verdict が catastrophic の場合、解説動画の完成を宣言せず、成功を宣言する代わりに判定結果と re_roll_suggestion を提示します。

失敗モード

症状考えられる原因対処方法
最初の MCP 呼び出しで OAuth / 401 が発生するユーザーの Pika コネクターのトークンがない、または期限切れです処理を停止し、ユーザーに Pika MCP コネクターの再認証を依頼してください。認証に成功するまで、料金が発生する手順を再試行しないでください。
capture_website が空白のフレーム、空の action_bboxes、またはブロックされたページを返す対象 URL にアクセス制限がある、ボットとして検出される、Cookie に覆われている、または安定化待機時間の後にレンダリングされる事実確認には十分な場合、WebFetch のテキストフォールバックを使用してください。それ以外の場合は、ソース資料の貼り付けを依頼するか、より単純な URL を尋ねてください。
TTS またはリップシンクのプロバイダーが一時的な 5xx / 429 を返すプロバイダーのキューまたはレート制限指示されたバックオフ時間の後、まったく同じ呼び出しを 1 回再試行してください。再度失敗した場合は、プロバイダーのエラーと最後に完了したチェックポイントを示して停止してください。
キャプションまたは最終 QA で、固有名詞が読めない、遮蔽されている、フレームが空白、またはアバターに一貫性がないと報告される生成されたメディアが公開できる状態ではありませんQA JSON と具体的な再生成案を添えて not publish-ready を返してください。致命的な判定が出た場合に成功と宣言しないでください。

重要なフレーズ

これらのアンカーは、ページの種類を問わずビジュアルの仕様を維持します。

フレーズ該当箇所重要な理由
vanilla CSS that resolves via document.querySelectorセレクターの仕様capture_website 内で、スクロール、bbox のキャプチャ、ズームの対象を一致させます。
GitHub URLs activate repo-aware modeモード検出README/コードの解説に代わって、一般的な製品ページ向けの構成が使われるのを防ぎます。
8-10 beats、65-80 seconds、165-200 wordsビートシートの作成ナレーション、画面録画、リップシンク、キャプションを、信頼性の高い尺の範囲内に収めます。
all beats[] mutations happen here音声の再スケール手順後続のキャプチャ/ズーム/合成手順で、一貫したタイムラインを使用できるようにします。
Cookie バナーを隠す extra_css ペイロード一般 URL の事前チェックバナーのクリックに失敗した場合に、最初のフレームがバナーで遮られるのを軽減します。

エンジンの選択:デフォルトは Pika リップシンク、Kling はオプトイン

処理が速く、ほとんどの解説動画を短い反復サイクルで作成できるため、デフォルトでは Pika/parrot のリップシンクを使用します。Kling は、ユーザーが明示的に --lipsync-provider kling を指定した場合、または重要度の高いレンダリングで、より中央に配置されたプレゼンターの見た目が必要であり、長いボトルネック段階を許容できる場合にのみ使用してください。画面キャプチャ、ブラウザフレーム、ズーム、PiP、キャプションは、いずれもそのリップシンク方式を取り囲む決定的な編集/合成手順のままです。

実行時の目安

通常の所要時間は、Pika リップシンクで 5~10 分、Kling リップシンクで 10~30 分以上です。

手順所要時間備考
URL の読み込み+事前チェック10~60 秒GitHub README の確認、または一般 URL の DOM/Cookie チェック
TTS+音声の再スケール30~90 秒実際の音声の長さに合わせてビートのタイミングを正規化します
画面録画60~180 秒ページの読み込み時間とナビゲーション回数によって異なります
ブラウザフレーム+ズーム1~3 分決定的な編集/合成段階
リップシンクPika は 2~5 分/Kling は 5~30 分Kling はボトルネックとなるため、オプトイン方式です
PiP+キャプション1~3 分--no-captions が設定されている場合、キャプションはスキップされます

既知の不足点(サーバー側での後続作業として引き継ぎ)

  • Kling のアバターモードとプロンプトを利用できます。 プレゼンターを洗練された見た目にするには、--lipsync-provider kling を渡し、Step 9 の呼び出しに mode: "pro" と、"talking head, face centered, mouth syncs to audio, minimal head movement, professional presenter" のようなプロンプトを追加します。これは、リップシンク時の大きな頭の動きを抑えるための品質調整手段です。
  • 画面録画の白いフレームを呼び出し側でトリミングできません。 capture_website には内部的なトリミング用ヒューリスティックがありますが、呼び出し側には公開されていません。ページの読み込み中に、解説動画の冒頭で短い白いフラッシュが見えることがあります。at_s: 0.0 の 800ms の wait アクションにより、ページが描画される時間を確保して多少は軽減できますが、すでに録画された白いフレームはトリミングされません。ワーカーの機能強化が必要です。
  • ビートごとのナビゲーションに networkidle 待機がありません。 capture_website は domcontentloaded まで待機した後、bbox キャプチャ分岐でアクション後に 600 ms 待機します(bbox_selector が設定されている場合のサーバー側の処理)。ただし、最終的な描画が domcontentloaded 後に行われる SPA の blob ページでは、マウント前のコードブロックを対象に bbox が取得されることがあります。ワーカーの機能強化として、timed_actions[].navigate に wait_until の設定項目を公開する必要があります。
  • ステップごとの出力サイズ検証ゲートがありません。 堅牢なファイルサイズチェックでは、各ステップの後に TTS が 50KB 以上、プレビューが 100KB 以上、画面録画が 200KB 以上、リップシンクが 500KB 以上、最終出力が 1MB 以上であることを検証します。MCP の経路から返されるのは URL のみです。ファイルサイズの検証にはステップごとに analyze_media の呼び出しを追加する必要があり、毎回約 30 秒のオーバーヘッドが発生します。ユーザー側のレイテンシ予算に余裕ができたら追加する価値があります。現時点では、下流の失敗が連鎖するケース(例:サイズ 0 バイトの TTS → 無音のリップシンク → 空白の合成動画)は Step 11 まで表面化しません。
  • text_content の bbox キャプチャは実装されていません。 capture_website v1 が返す action_bboxes に含まれるのは、CSS selector を持つステップのみです。text_content だけを指定したステップはエントリーを生成しません。ズーム範囲を確実にカバーするには、zoom_target で CSS セレクターを優先してください。
  • ビートシートの文言は非決定的です。 同じ入力を 2 回実行しても、vo_text とズーム位置は異なる場合があります。契約で保証されるのはビジュアルの種類であり、ピクセル単位での完全な再現ではありません。
  • 一般的な URL モードの品質はサイトによって異なります。 セマンティックなマークアップ(<h1> + 明確な <section> + 名前付きクラスのフック)を持つ、モダンなインディー系や SaaS のランディングページはうまく動作します。大手企業のサイト(apple.com、microsoft.com、amazon.com)では、既知の制限がいくつかあります。(a)ボット検出 — ヘッドレス Chrome では簡易版のページが表示されたり、CAPTCHA が表示されたりすることがあります。Step 2.6 §A はこれらを検出すると中止しますが、ヒューリスティックですべてを網羅できるわけではありません。(b)難読化されたクラス名 — hero-title の代わりに tile-headline が使われていると、汎用セレクターでは検出できません。Step 2.6 §C の WebFetch による DOM スキャンで改善できますが、完全ではありません。(c)スクロールをトリガーとするアニメーションが再生されない — IntersectionObserver によるヒーロー要素の表示は、Playwright の scrollIntoView ではなく実際のユーザー操作によるスクロールで発火するため、録画されたフレームが静的なプレースホルダーになることがあります。(d)遅延読み込み画像 — loading="lazy" を指定した picture/source 要素は、600ms または 2500ms の待機時間内に読み込みが完了しないことがあります。その場合、bbox は透明なプレースホルダーを対象にします。回避策:ローンチデモでは、よりシンプルで小規模なマーケティングページを優先してください。ビート選択の基準を明確にするため、必ず --focus "the X feature" を渡してください。また、大手サイトへの対応にはサーバー側での追加 PR が必要になることを考慮してください(Cookie バナーのクリック再試行 + wait_until=networkidle + IntersectionObserver ポリフィルによるアニメーションのトリガー)。
  • Cookie バナーのクリックは 1 回のみ試行されます。 Step 2.6 §B は、WebFetch の DOM から抽出した非表示化用セレクターに対して click を 1 回実行します。WebFetch の HTML にバナーが含まれていない場合(JavaScript の実行後に描画される場合など)や、セレクターが間違っている場合、クリックは何もせずに失敗します。この場合、extra_css ペイロードが主要な防御策となります。ワーカーの機能強化として、click アクションでフォールバックセレクターのリストをサポートし、ワーカーが順に試すようにする必要があります。

認証

呼び出しが 401 を返す場合、ユーザーの OAuth トークンの有効期限が切れているか、まだ発行されていません。次に認証が必要な MCP 呼び出しを行うと、OAuth が自動的に開始されます(@pika.art の Google ログイン画面がブラウザーで開きます)。非対話型環境では、MCP_AUTH_TOKEN を設定してください。

使用例

GitHub モード(リポジトリを認識:README のスキャン+ライブデモの検出):

  • /pika:explainer https://github.com/leigest519/OpenGame
  • /pika:explainer https://github.com/anthropics/claude-cookbooks --focus "Claude Code MCP integration"
  • /pika:explainer https://github.com/openai/whisper --preview(新しいアバターのテスト時に、プレビューの確認ゲートをオプトインします)

汎用 URL モード(GitHub 以外の URL に対応。ページを直接操作します):

  • /pika:explainer https://pika.art
  • /pika:explainer https://vercel.com --focus "the deployment workflow"
  • /pika:explainer https://docs.anthropic.com/en/docs/claude-code/plugins
  • /pika:explainer https://your-product-page.com --avatar https://cdn.example.com/me.png --preview

コメント