mcpField
V
Video Extract

Video Extract

ローカル
github.comメディア・デザイン75 週間前に更新

Video Extract について

Video Extractは、AIエージェント向けのローカルMCPサーバーで、リンクやローカルファイルから動画を取得し、文字起こしやキーフレーム抽出を行います。取得と分析を別々に使え、字幕を優先した処理をAPIキーやアップロードなしで実行できます。

主な機能

  1. 01URLから動画を特定してダウンロード
  2. 02動画全体または指定区間を取得
  3. 03字幕取得または音声のローカル文字起こし
  4. 04シーンや映像の特徴に基づくキーフレーム選択
  5. 05複数動画の処理とバックグラウンド実行

活用シーン

  1. 01動画の文字起こしと重要場面の確認
  2. 02長い動画の指定区間だけを取得
  3. 03ブラウザーのCookieを使った年齢制限付きメディアの分析

MCP 対応

MCPサーバーは、MCPクライアントに動画取得と分析のツールを公開します。処理はローカルで行われ、結果は要約とファイルパスで返されます。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"com-yanlinglabs-video-extract": {
"command": "npx",
"args": [
"-y",
"@yanlinglabs/video-extract-mcp"
],
"env": {
"VIDEO_EXTRACT_COOKIES_FROM_BROWSER": "<VIDEO_EXTRACT_COOKIES_FROM_BROWSER>",
"VIDEO_EXTRACT_MODELS_DIR": "<VIDEO_EXTRACT_MODELS_DIR>",
"VIDEO_EXTRACT_MAX_CONCURRENCY": "<VIDEO_EXTRACT_MAX_CONCURRENCY>"
}
}
}
}
起動コマンドnpx -y @yanlinglabs/video-extract-mcp
設定が必要な環境変数
VIDEO_EXTRACT_COOKIES_FROM_BROWSERSet to 'auto' to borrow browser cookies only when a platform refuses a download (age-restricted, members-only, or bot-checked media).
VIDEO_EXTRACT_MODELS_DIRWhere local speech models live. Defaults to ~/.cache/video-extract-mcp/models; they are fetched on demand.
VIDEO_EXTRACT_MAX_CONCURRENCYHow many analyses may run at once. Default 2; each peaks at about 2 GB when Whisper transcribes, 1.1 GB with SenseVoice.

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
npm@yanlinglabs/video-extract-mcp0.16.2

ツール

3

掲載時のツール一覧 · 2 週間前に更新

パラメーター型必須説明
videosarrayはいanalyze_video または resolve_video に渡したものと同じ動画 URL またはファイルパスです。

詳細

video-extract-mcp

AI エージェントに動画リンクを渡すだけで、ファイルをダウンロードしたり、文字起こしを読んだり、必要なフレームだけを取得したりできます。すべてお使いのマシン上で実行されます。

できることは2つあり、それぞれ単独でも使えます。

動画を取得する。 YouTube のリンク、TikTok、WeChat Channels の共有 URL、生の .mp4 ファイル、あるいは誰も知らないようなサイトのページでも、リンク先を特定して、動画全体または指定した部分をダウンロードします。それだけでよければ、そこで終了できます。分析を強制されることはありません。

動画の内容を読む。 字幕があるプラットフォームでは実際の字幕を、ない場合はローカルの音声認識による文字起こしを取得できます。また、千枚ものほとんど同じ静止画ではなく、重複を除去し、シーンを考慮してスコア付けした少数の重要なキーフレームを取得できます。

AI エージェント向けに構築されています。MCP ツールは3つ。クラウドも API キーも Python も不要です。

npm License: MIT Node TypeScript CI MCP MCP Registry


claude mcp add --scope user video-extract -- npx -y @yanlinglabs/video-extract-mcp@latest
codex mcp add video-extract -- npx -y @yanlinglabs/video-extract-mcp@latest

できること

ほぼどこからでも動画を取得できます。 リンクを貼り付けると、YouTube、TikTok、Instagram、X、Facebook と Reels、Twitch、Vimeo、Reddit、WeChat Channels、単体の .mp4 や .m3u8、あるいは誰も知らないようなサイトのページから動画を取得します。ローカルファイルにも対応しています。サポートされていないものは、クラッシュせず、明確なエラーを返します。

そこから、必要な処理を選べます。

  • 情報だけを確認する。 タイトル、クリエイター、長さ、チャプター一覧、説明、コメント数を、1バイトもダウンロードせずに取得できます。長い動画の場合、分析する価値のある箇所を見つけるのに便利です。
  • ファイルをダウンロードする。 動画全体でも、300~420秒の部分だけでも取得できます。プラットフォームが範囲指定取得に対応している場合、動画全体ではなく指定した部分だけを転送します。
  • 文字起こしを取得する。 プラットフォームに字幕があれば、公開されているあらゆる言語の実際の字幕を取得します。字幕がなければ、ローカルで音声認識を行います。どちらを取得したかは結果に表示されます。字幕のみをリクエストすれば、ダウンロードは完全に省略され、数分ではなく数秒で済みます。
  • 重要なフレームを取得する。 一定間隔でフレームを取得するのではなく、シーンの変化、画面上のテキストの出現、明確に新しい映像などを検出し、重複を除去してスコア付けします。1時間の動画でも、通常は数十枚程度です。必要であれば、等間隔でのサンプリングや、指定した秒数のフレーム1枚の取得もできます。
  • 複数の処理をまとめて実行する。 動画のリストを渡すと、それぞれの動画について結果を取得できます。ひとつが失敗しても、ほかには影響しません。
  • 非公開コンテンツやレート制限のあるコンテンツにもアクセスする。 ブラウザーの Cookie を指定すると、年齢制限、メンバー限定、フォロワー限定のメディアを取得でき、匿名ダウンロードを妨げるボットチェックも回避できます。
  • 時間のかかる処理をバックグラウンドで実行する。 1時間の動画を完全に分析するには数分かかります。バックグラウンドタスクとして実行し、ローカルの HTTP エンドポイントで進捗を報告できます。クライアントが待機を諦めた後でも、結果を返せます。

すべてお使いのマシン上で処理されます。API キーもアップロードも、サードパーティーサービスも不要です。

これが必要な理由

実際には、2つの問題があります。まず、動画を取得することです。どのプラットフォームもそれぞれ異なる仕組みでメディアを隠しており、どこもスクリプトによる取得を歓迎していません。次に、動画を読み取ることです。LLMは動画を視聴できません。一般的な回避策である「Nフレームごとに取り出してコンテキストウィンドウに詰め込む」方法では、直前のフレームと98%同じフレームに膨大なコンテキストを費やす一方で、ほかに何も動いていないときに変化したスライドを見落とします。

video-extract-mcpは、この両方に対応します。

  • ほぼあらゆる場所から取得できます。 主要なプラットフォーム、メディアの直接 URL、一般的なサイトをひとつのリゾルバーチェーンで扱います。プラットフォームが許可する場合は範囲指定取得に対応し、ログインが必要な場合はCookieを使えます。ダウンロードは、このツールの完全な用途のひとつであり、ほかの処理へ進むための単なる手順ではありません。

  • 出所を偽らない文字起こしです。 動画に字幕がある場合は、常にプラットフォーム独自の字幕を使います。まず人手で作成された字幕を使い、なければプラットフォームの自動字幕を使います。音声をローカルで文字起こしする(WhisperまたはSenseVoice)のは、動画に字幕がない場合、または字幕を取得できなかった場合だけです。結果には、どちらを使ったかがtranscript.sourceで示されます。ローカルで文字起こしした場合は、transcript.asrReasonに理由(no_captionsまたはcaptions_failed)が示され、取得に失敗した場合はwarningsにも記載されます。

  • キーフレームはサンプリングではなく、選び出します。 シーン境界の検出、ぼかしや品質のフィルタリング、画面上のテキストの新規性(字幕を考慮するため、映像に焼き込まれた字幕によって冗長なフレームが残ることはありません)、画像埋め込みの類似度を、反復型の多様性を考慮した選択処理に取り入れます。

  • 出力はコンテキストではなく、ディスクに保存されます。 ツールの応答は簡潔な要約とファイルパスです。エージェントが必要とするのが3つの数値だけなら、35フレームのマニフェストや文字起こし全文を会話に含める必要はありません。

  • すべてお使いのマシン上で実行されます。 サードパーティのAPIも、アップロードも、キーも必要ありません。長時間の分析はMCPバックグラウンドタスクとして実行でき、ツールはすぐにハンドルを返して進捗を通知します。詳しくは「バックグラウンドタスク」をご覧ください。

クイックスタート

まず、システムプログラムをインストールしてください。これらはnpmからはインストールできません。ffmpeg(ffprobeを含む)、yt-dlp、Deno(yt-dlpがYouTubeで必要とします)、中国語データを含むtesseract、およびNode ≥ 22.12が必要です。

macOS(テスト済みのプラットフォーム):

brew install node ffmpeg yt-dlp tesseract tesseract-lang    # Homebrew's yt-dlp brings Deno along

Windows(まだ実行していません。以下はパッケージマニフェストに基づいています)。PowerShellで実行します。

winget install OpenJS.NodeJS.LTS
winget install yt-dlp.yt-dlp            # also installs Deno and an ffmpeg build with ffprobe
winget install tesseract-ocr.tesseract

その後、新しいターミナルを開いてください。TesseractのインストーラーはPATHに自動で追加しません。また、wingetは中国語データを追加しません。両方の手順はINSTALL.mdをご覧ください。

Linux: お使いのディストリビューションのyt-dlpは、YouTubeで使うには古すぎることが多く、Denoもパッケージ化されていません。Debian/UbuntuとFedoraの手順はINSTALL.mdをご覧ください。

インストール後、エージェントを再起動してください。起動するサーバーが新しいプログラムを認識できるようになります。

次に、MCPクライアントでパッケージを指定します。方法は2つあり、どちらがよいかを判断するのに30秒かける価値があります。

方法A — npx、インストール不要。 最も簡単で、新しいリリースも自動的に取得します。

Claude Code:

claude mcp add --scope user video-extract -- npx -y @yanlinglabs/video-extract-mcp@latest

Codex:

codex mcp add video-extract -- npx -y @yanlinglabs/video-extract-mcp@latest

別のエージェントをお使いですか? **INSTALL.md**を参照させれば、エージェント自身でインストールできます。Gemini CLI、Grok、opencode、Hermes Agent、OpenClaw、Pi、Kilo Code、Cline、DeepSeek Harnessに対応し、各手順の確認方法と、それぞれで延長が必要なツール呼び出しのタイムアウトについても説明しています。完全な分析には数分かかりますが、1分で待機を終了するものもあります。

@latestはそのままにしてください。 付けない場合、npxは最初にキャッシュしたバージョンに固定され、更新されなくなります。

方法B — グローバルにインストール。 起動が速くなり、ステータスCLIのvideo-extractを通常のコマンドとして使えます。

npm install -g @yanlinglabs/video-extract-mcp

次に登録します — Claude Code:

claude mcp add --scope user video-extract -- video-extract-mcp

Codex:

codex mcp add video-extract -- video-extract-mcp
npx(A)グローバルインストール(B)
更新@latestを指定した場合のみ自動 — @yanlinglabs/video-extract-mcpだけを指定したnpx -y @yanlinglabs/video-extract-mcpは、最初にキャッシュしたバージョンに固定され、更新されません手動:npm update -g @yanlinglabs/video-extract-mcp。実行するまでインストール済みのバージョンを使い続けます
起動約0.9秒(起動のたびにnpmで解決)約0.1秒
シェルでのvideo-extract statusPATHに含まれません — npx -y -p @yanlinglabs/video-extract-mcp video-extract statusが必要ですそのまま動作します
作業ディレクトリこのパッケージ自体のチェックアウトであってはなりません(下記参照)関係ありません

どちらもエージェントにできることには影響しません。エージェントは、返信で渡されたstatusUrlを使ってバックグラウンド処理の状況をHTTPで確認し、シェルコマンドは使いません。CLIは人間向けです。

または、任意のMCPクライアントの設定で、Aの場合は"command": "npx", "args": ["-y", "@yanlinglabs/video-extract-mcp@latest"]、Bの場合は引数なしで"command": "video-extract-mcp"を指定します。

{
  "mcpServers": {
    "video-extract": {
      "command": "npx",
      "args": ["-y", "@yanlinglabs/video-extract-mcp@latest"]
    }
  }
}

npxには注意点が1つあり、影響を受けるのはコントリビューターだけです。 このパッケージ自体のgitチェックアウト内でnpx @yanlinglabs/video-extract-mcpを実行すると、command not foundで失敗します。npxはローカルのpackage.jsonにその名前があるのを確認し、まだ作成されていないローカルのnode_modules/.bin内でバイナリを探して、見つからずに終了します。MCPクライアントはプロジェクトを作業ディレクトリに設定してサーバーを起動するため、このリポジトリではオプションAは使えません。このツール自体を開発する場合は、そのプロジェクトだけビルドしたものを指定してください — claude mcp add --scope local video-extract -- node "$PWD/dist/mcp.js" — これにより、npm run buildの結果が公開の手順を挟まずすぐに反映されます。それ以外の場所では、npxで問題ありません。

字幕がある動画であれば、これで十分です。字幕を優先するトランスクリプトポリシーのおかげで、ほとんどの動画に字幕があります。ビジョンモデルは初回使用時に自動でダウンロードされます。

音声モデルが必要なのは、字幕がまったくない動画だけです。そのような動画を初めて処理するときに、自動で取得されます。ダウンロードされるのはその動画に必要なエンジンだけです — 中国語/日本語/韓国語モデルは233 MB、Whisperは1.3 GBで、保存先は~/.cache/video-extract-mcp/modelsです。手動のままにするにはVIDEO_EXTRACT_AUTO_FETCH_MODELS=0を設定するか、次のように自分で事前に取得してください。

npx -y -p @yanlinglabs/video-extract-mcp@latest video-extract cookies   # installs the package, then exits
curl -fsSL https://raw.githubusercontent.com/yanlingLabs/video-extract-mcp/main/scripts/fetch-models.sh \
  | bash -s -- ~/.cache/video-extract-mcp/models

~/.cache/video-extract-mcp/modelsはツールがデフォルトで参照する場所です。VIDEO_EXTRACT_MODELS_DIRで変更できます。取得が無効になっている場合や失敗した場合でも、字幕のない動画からフレームは返され、トランスクリプトがない理由を説明する警告が記録されます — 失敗するのではなく、機能を縮退して動作します。

ソースから(コントリビューター向け)
git clone https://github.com/yanlingLabs/video-extract-mcp.git
cd video-extract-mcp
npm install && npm run build
./scripts/fetch-models.sh    # into ./models, which takes precedence when present
npm run preflight            # verifies ffmpeg / ffprobe / yt-dlp / tesseract
環境変数
変数用途
VIDEO_EXTRACT_MODELS_DIR音声モデルの保存場所です。そこに ./models が存在する場合はそれを使用し、存在しない場合は ~/.cache/video-extract-mcp/models がデフォルトになります。
VIDEO_EXTRACT_AUTO_FETCH_MODELS0 に設定すると、音声モデルのオンデマンドダウンロードを停止します。その場合は自分でモデルを用意する必要があります(scripts/fetch-models.sh)。字幕のない動画は警告とともに処理が劣化します。
VIDEO_EXTRACT_COOKIES_FILENetscape 形式の Cookie ジャーのパスです。YouTube、Instagram、Facebook、X、TikTok、Twitch など、すべての yt-dlp ソースに一括で使用されます。認証が必要なソースを参照してください。
VIDEO_EXTRACT_COOKIES_FROM_BROWSER代わりにローカルブラウザーから Cookie を読み込みます:chrome、firefox、safari、edge、brave、chromium、opera、vivaldi、whale、および任意で browser:profile。VIDEO_EXTRACT_COOKIES_FILE が設定されている場合は無視されます。
VIDEO_EXTRACT_WECHAT_COOKIEWeChat Channels のリンク用の Yuanbao セッション Cookie です。userCookies 呼び出しでブラウザーからセッションを取得することもできるため、設定は任意です(呼び出しごとに指定し、再起動不要を参照)。意図的に上記とは別になっています。認証情報を独自に持つ別のプロトコルです。
VIDEO_EXTRACT_MAX_CONCURRENCYanalyze_video の項目実行における同時実行数の上限です。通常の呼び出しとバックグラウンドタスク、バッチ項目と個別の呼び出しはすべて同じ上限にカウントされます。デフォルトは 2 です(各分析で約 2 GB を使用する可能性があります。下記のメモリーを参照)。resolve_video はモデルを読み込まないため、対象外です。制限する処理がありません。
VIDEO_EXTRACT_TASK_TTL_MS完了したバックグラウンドタスクのハンドルが、期限切れになるまで照会可能な時間です。デフォルトは 1800000(30 分)です。0(または 0 以下の値)を指定すると、ハンドルは期限切れになりません。対象はメモリー内のハンドルのみです。destinationPath にすでに書き込まれたファイルは、ハンドルが期限切れかどうかにかかわらず、ツールによって削除されることはありません。
VIDEO_EXTRACT_STATUS_PORTlocalhost の /status エンドポイントのポートを固定します(進行状況の確認を参照)。未設定の場合、起動ごとにエフェメラルポートが選ばれます(デフォルト:エンドポイントは有効)。値 0 を指定するとエンドポイント全体が無効になります。上記の VIDEO_EXTRACT_TASK_TTL_MS では 0 が「期限切れなし」を意味し、無効化ではない点に注意してください。
認証が必要なソース

公開されていないメディアは数多くあります。その場合の答えは、各プラットフォームが求めるものと同じで、Cookie です。指定方法は 2 つあります。設定不要で呼び出しごとに指定する方法と、環境変数に継続的な設定として指定する方法です。

呼び出しごとに指定し、再起動不要:userCookies

どちらのツールも userCookies: true を受け取ります。これは「この呼び出しに限り、ユーザーが自分のブラウザーの使用を許可した」という意味です。サーバーはデフォルトのブラウザーから Cookie を読み込み、取得対象のサイトにのみ送信します。情報は引き継がれないため、次回もエージェントが改めて尋ねる必要があります。ツールの説明では、シェルツールがサンドボックスの外に出る前に確認するのと同じように、毎回、まずユーザーに尋ねるよう指示しています。環境変数も再起動も必要ありません。

Cookie が送信されずにリクエストが拒否された場合(auth_required、auth_expired、rate_limited)、返信ではその旨を伝え、ユーザーへの確認を提案します。各項目では送信した内容が報告されますが、値そのものは報告されません。

cookies意味
"none"何も送信されませんでした。
"browser:safari"(または chrome、firefox、…)userCookies または VIDEO_EXTRACT_COOKIES_FROM_BROWSER を介して、そのブラウザーの Cookie が送信されました。
"cookies_file"VIDEO_EXTRACT_COOKIES_FILE で指定されたジャーです。
"wechat_cookie"VIDEO_EXTRACT_WECHAT_COOKIE です。
サインインが必要な場合は、専用のページでサインインを求められます。 誰も言及していないサイトのサインインページが突然表示されるのは、不安を感じさせます。そのため、ログインページを直接開く代わりに、サーバーはブラウザーで 127.0.0.1 から配信される小さな専用ページを開きます。ターミナル出力のような数行のプレーンテキストで、MCP クライアントの名前(「Claude Code is trying to download … using your browser cookies」)、不足している Cookie のサイト(「Cookies for yuanbao.tencent.com, required for the WeChat Channels download, were not found」)、緑色のサインインリンクが表示されます。YouTube、Instagram、TikTok、X、Facebook、Twitch、Vimeo、Reddit、Bilibiliの場合はそのサイトのサインインページ、それ以外の場合はそのサイトのホームページが表示されます。その下には、2つの操作ボタンを備えたライブステータス行があります。[I've signed in] と [don't sign in] です。後者を押すと待機が即座に終了し、エージェントに再試行しないよう伝えます。認証情報は保持しません。Cookie は引き続きブラウザーから読み取ります。ページを開くと、ブラウザーが前面に表示されます。呼び出しは最大3分間待機します。その間にサインインしないと、サインインを完了して再試行するよう求めるメッセージとともに失敗します。

実際の利用時にWeChatのサインインがどのように表示されたか(macOS、Safari、Claude Code):Safariがページを開いて前面に表示され、緑色のリンクからyuanbao.tencent.comが開き、ユーザーがQRコードでサインインして戻り、[I've signed in] を押すと、ダウンロードが続行されました。

  • WeChat Channels: ブラウザーにyuanbao.tencent.comのセッションがない場合です。このページでは、WeChat Channelsの動画はyuanbao.tencent.com(TencentのAIアシスタント)経由で取得するため、そのサイトでWeChatによるサインインが求められることを説明します。サーバーはyuanbaoの getuserinfo を確認してサインインを自動検出するため、ほかに押すものはありません。
  • その他のサイト: ブラウザーのCookieを使っても動画を取得できない場合です(非公開、年齢制限付き、メンバー限定、または「ボットではないことを確認してください」というチェックがある場合)。任意のサイトにサインインしているかを確認する安価な方法はなく、再試行のたびにそのサイトへリクエストを送るため、サインインを示す合図があった場合にのみ再試行します。合図は、I've signed in を押すこと、またはそのサイトに新しいCookie名が現れることです(サインインするとセッションCookieが追加されます。値の変更は対象外です)。最大3回再試行し、Cookieをきっかけに再試行する場合は10秒間隔です。それでも拒否された場合は、アカウントがその動画にアクセスできない可能性が高いと返信します。

使用前に、すべてのWeChatセッションをyuanbaoの getuserinfo で確認します。トークンが古い場合、その応答には更新されたトークンが含まれます。以前のバージョンのように破棄せず、サーバーは更新されたトークンを使用するようになりました。トークンを保持することで、貼り付けた VIDEO_EXTRACT_WECHAT_COOKIE の有効期限切れが減る可能性はありますが、測定はしていません。ブラウザーから取得したセッションはメモリ内に保持され、使用のたびに更新されるため、以降の呼び出しではブラウザーからの読み取りを省略できます。それを使用するには、各呼び出しで userCookies を渡す必要があります。また、ディスクに書き込まれることはありません。

知っておくとよいこと:

  • 「ブラウザー」とはデフォルトのブラウザーを指します。 サインインページを開き、その後Cookieを読み取る場所だからです。yt-dlpがデフォルトのブラウザー(たとえばArc)を読み取れない場合は、読み取り可能なインストール済みブラウザーに切り替えて、そのブラウザーを開きます。
  • Chrome系のブラウザーでは、読み取り時にmacOS Keychainのプロンプトが表示されます(「Chrome Safe Storage」)。「常に許可」を選ぶと、プロンプトは繰り返し表示されなくなります。同じ理由で、サインイン待機中はこれらのブラウザーを5秒ごとではなく15秒ごとに確認します。Safariではプロンプトは不要ですが、サーバーを実行しているアプリにフルディスクアクセスが必要な場合があります。その場合は、返信でその旨が通知されます。
  • Firefoxでは、新しいサインインが反映されるまで時間がかかることがあります。 yt-dlpは cookies.sqlite だけをコピーします。最近の変更がFirefoxで統合されるまで格納される先行書き込みログはコピーしません。
  • エンドツーエンドで動作確認したのはmacOSとSafariの組み合わせのみで、対象はWeChatのみです。ほかのサイト(YouTube、Instagramなど)での実際のサインインは、まだ確認していません。Linux(xdg-settings)とWindows(UserChoice)でのデフォルトブラウザーの検索は、コードパスのみテスト済みです。
常時設定

1 つの jar で、すべての yt-dlp ソースをまとめてカバーできます — ファイル内では Cookie がドメインごとに適用されるため、1 回エクスポートするだけで YouTube、Instagram、Facebook、X、TikTok、Twitch のすべてを認証できます。YouTube 専用の設定ではありません。ほかの環境変数と同様、これらはサーバーの起動時に有効になります。

export VIDEO_EXTRACT_COOKIES_FROM_BROWSER=auto       # recommended: borrow only when blocked

auto は遅延型であり、それが狙いです。 通常のリクエストでは Cookie を一切送信しません。プラットフォームが実際に拒否した場合に限り、サーバーはデフォルトのブラウザー(userCookies が使うものと同じです)の Cookie を使って、そのリクエストだけを再試行し、そこで終了します。ループはありません。認証情報ストアにアクセスする負担が生じるのは、本当にブロックされた場合だけです。そのため、公開動画を視聴するたびに借用中のセッションがローテーションされることもありません。

ほかの 2 つのモードは積極型で、すべてのリクエストに Cookie を付けます。

ブラウザーのストアを直接読み取ります(chrome、safari、edge、brave、…)。

export VIDEO_EXTRACT_COOKIES_FROM_BROWSER=firefox

または、エクスポートした Netscape 形式の jar を指定します。

export VIDEO_EXTRACT_COOKIES_FILE=~/cookies.txt

両方を設定した場合はファイルが優先されます。これらは併用するものではなく、どちらか一方を選ぶ設定です。

OS のキーチェーン確認が表示されます。 Chrome 系のブラウザーはすべて、システムのキーチェーンを使って Cookie ストアを暗号化しています。そのため、最初に読み取るときに承認が必要なダイアログが表示されます(macOS では「Chrome Safe Storage」)。Firefox と Safari では表示されません。Cookie が送信されていない状態でリクエストが拒否された場合、応答ではそのダイアログについて警告しつつ、userCookies の利用について確認するよう提案します。自分で気付くまで放置することはありません。

ログインだけでなく、年齢制限付きやメンバー限定の YouTube、Instagram と Facebook の大部分、X の多くのコンテンツ、Twitch のサブスクライバー限定コンテンツを利用できるようになります。また、匿名取得ではログイン済みの場合よりはるかに早く発生する rate_limited/「ボットではないことを確認するためにログインしてください」 にも対処できます。

使う前に知っておきたいことが 3 つあります。

  • 呼び出し元から Cookie ファイルやブラウザーを指定することはできません。 どちらも指定できるエージェントは、マシン上の任意のファイルを読んだり、任意のセッションを選んだりできてしまいます。できるのは userCookies: true を渡すことだけです。これは、あなたに確認したうえで、その呼び出しに限りデフォルトのブラウザーを使うことを意味します。
  • jar は変更されません。 --cookies FILE は、そのファイルを読み取るだけでなく終了時に書き換えます。このツールは jar を非公開の一時ファイルにコピーし、そのコピーを yt-dlp に渡して、後で削除します。そのため、更新された Cookie は書き戻されません。
  • 現在ログイン中のブラウザーからエクスポートすると、ログアウトする場合があります。 プラットフォームはセッション Cookie をローテーションするため、ログイン中のセッションから取得したコピーは古くなります。特に YouTube で発生しやすい問題です。yt-dlp の推奨は、プライベート/シークレットウィンドウからエクスポートし、ログアウトせずにそのウィンドウを閉じることです。

パスが間違っている場合は、匿名で取得するのではなく、明確にエラーになります。読み取れない jar は設定の不備であり、処理を続行してごまかすものではありません。匿名で取得した結果が返ると、誤った問題を調べることになるためです。

Cookie の値を一切表示せずに、実際に設定した内容を確認できます。

$ video-extract cookies
cookie jar: /Users/you/cookies.txt
  .youtube.com      12 cookies  expires in 23 days
  .instagram.com     4 cookies  expires in 3 days
  .x.com             2 cookies  EXPIRED 5 days ago
  3 domain(s), 18 cookies, 1 with expired cookies

ドメイン、件数、有効期限が表示されます。名前や値は表示されないため、出力を問題報告に貼り付けても安全です。また、よくある間違いも示されます。エクスポーターで Netscape 形式ではなく JSON を選ぶと、一見問題なさそうでいて yt-dlp が読み取れる内容のないファイルが生成されます。その場合、無関係な動画で auth_required が発生し、原因の特定が難しくなることがあります。スクリプトで使う場合は --json を追加してください。

3つの使い方

MCP サーバーが主なインターフェースですが、同じエンジンはほかにも2つの方法で利用できます。

CLI として、エージェントを接続する前に何ができるかを手早く確認できます。

npm run cli -- "https://youtube.com/watch?v=..." --max-frames 10 --out ./output

# just the transcript, no frames
npm run cli -- "<url>" --frames none --out ./output

# one exact frame at 7s, as cheap as this gets
npm run cli -- "<url>" --start 7 --end 7 --frames even --max-frames 1 --no-transcript --out ./output

--out に manifest.json とフレーム画像を書き込み、マニフェストも標準出力に出力します。

この JSON をどこかにパイプで渡したい場合は、ビルド済みのエントリーポイントを直接呼び出してください。npm run は独自のバナー行を標準出力に付加するため、npm run cli の出力はそのままでは有効な JSON ではありません。

npm run build
node dist/cli.js "<url>" --max-frames 10 | jq '.transcript.source'

ライブラリとして、エージェントを介さずにパイプラインを利用できます。

import { analyzeVideo } from '@yanlinglabs/video-extract-mcp/dist/analyze.js';

const manifest = await analyzeVideo('https://youtube.com/watch?v=...', {
  start: 30, end: 90, frames: 'key', maxFrames: 12, outDir: './output',
});
console.log(manifest.transcript?.source);   // 'manual' | 'auto' | 'asr'
console.log(manifest.frames.map((f) => f.image));

analyzeVideo は、想定される失敗では例外をスローしません。DRM ページや無効なリンクの場合は、source.status が 'ok' 以外のマニフェストが返され、内容を読める理由が含まれます。processing.warnings も確認してください。失敗してスキップされた任意のステージは、そこに記録されます。

CLI とライブラリのどちらの経路でも、コンパイル済みの出力を実行することに注意してください。音声モデルとビジョンモデルはコンパイル済みモジュールの隣に解決される別プロセスのワーカーで実行されるため、TypeScript ソースを直接実行すると、これらのワーカーを解決できません。その場合、エラーではなく警告にとどまるため、見落としやすいです。このため、npm run cli は最初にビルドを実行します。

ツール

実処理を行う2つのツールと、1つの検索ツールです。

インターフェースは意図的に小さくしています。以前のバージョンには4つのツールがあり、どれがURLを受け取り、どれがローカルパスを受け取るのかを説明で強調する必要がありました。これは警告をもっと目立たせるべきということではなく、設計に問題があったという兆候です。

resolve_video — 動画を検索し、必要に応じて取得する
resolve_video({
  destinationPath: string,          // required — shared by every item below
  videos: [{                        // one entry per video, at least one
    url:             string,        // required
    returnVideo?:    boolean,       // default false: metadata only, no download
    start?:          number,        // seconds; only with returnVideo: true
    end?:            number,
    comments?:       boolean,       // default false — slow on popular videos
  }],
})

動画が1本の場合(一般的なケース)は、destinationPath に直接出力します。

resolve_video({
  destinationPath: "./out",
  videos: [{ url: "https://youtube.com/watch?v=..." }],
})
// -> ./out/metadata.json (+ the caption file it points at, e.g. auto.en-orig.vtt, when the video has captions)

1回の呼び出しで複数の動画を指定すると、それぞれに専用のサブディレクトリが割り当てられます。配列の順に video-1/、video-2/、... となります。

resolve_video({
  destinationPath: "./out",
  videos: [
    { url: "https://youtube.com/watch?v=..." },
    { url: "https://tiktok.com/@user/video/...", returnVideo: true },
  ],
})
// -> ./out/video-1/metadata.json
// -> ./out/video-2/metadata.json + source.mp4 (returnVideo: true)

デフォルトでは、大きなデータは何もダウンロードしません。タイトル、制作者、長さ、プラットフォームが公開している場合はチャプター一覧、短い説明のプレビューを取得できます。通常、次に何をするか判断するにはこれで十分です。また、範囲指定と組み合わせれば、この一連の処理を効率化できます。

チャプターを確認する → デモが12:04から始まることがわかる → 12:04~20:00だけを分析する → ダウンロード、文字起こし、フレーム処理の90%を省く。

analyze_video — 実際の処理
analyze_video({
  destinationPath: string,                      // required — shared by every item below
  videos: [{                                     // one entry per video, at least one
    pathOrUrl:       string,                     // URL *or* a local file — both work
    start?:          number,                     // seconds
    end?:            number,                     // end === start means one instant
    frames?:         "key" | "even" | "none",    // default "key"
    maxFrames?:      number,                     // default 35
    transcript?:     boolean,                    // default true
    language?:       string,                     // optional override, e.g. "zh"
  }],
})

動画が1本の場合(一般的なケース)は、destinationPath に直接出力します。

analyze_video({
  destinationPath: "./out",
  videos: [{ pathOrUrl: "https://youtube.com/watch?v=..." }],
})
// -> ./out/manifest.json, ./out/transcript.json, frame images

1回の呼び出しで複数の動画を指定すると、それぞれに専用のサブディレクトリが割り当てられます。配列の順に video-1/、video-2/、... となります。1つの項目が失敗しても、ほかの項目には影響しません。

analyze_video({
  destinationPath: "./out",
  videos: [
    { pathOrUrl: "https://youtube.com/watch?v=...", maxFrames: 10 },
    { pathOrUrl: "./local-clip.mp4", frames: "none" },
  ],
})
// -> ./out/video-1/manifest.json, transcript.json, frame images
// -> ./out/video-2/manifest.json, transcript.json, no frame images (frames: "none")
  • "key" は重要度セレクターを実行し、重複を除いた最適なフレームを返します。
  • "even" は範囲内を均等にサンプリングします。maxFrames で密度を指定するため、30秒間に60フレームなら2fpsです。
  • "none" はフレームを一切返しません。文字起こしだけを取得する場合に使います。
  • 特定のフレーム1枚を取得する場合:start: 7, end: 7, frames: "even", maxFrames: 1, transcript: false。

どちらのモードでもフレーム選択は start~end の範囲に限定され、文字起こしの対象も指定した範囲のみです。

get_status — タイムアウト後に結果を取得する
get_status({
  videos: [string, ...]        // required — the same URLs or paths you passed before
})

動画ごとに1つの項目を返します。finished は元の呼び出しで返されるはずだった結果、running は到達したステージ、unknown は状態不明を表します。実際に呼び出しがタイムアウトした場合にのみ使用してください。詳しくは以下を参照してください。

クライアントが待機を中断した呼び出しから結果を取得する

クライアントによっては、ツール呼び出しの待機時間に上限があります。その場合でも、処理は継続します。サーバーは処理を続け、ファイルを書き込みます。待機をやめるのはクライアントだけです。あとから結果を取得するには、動画について問い合わせます。

get_status({ videos: ["https://youtube.com/watch?v=..."] })

analyze_video または resolve_video に渡したものと同じ URL またはファイルパスを指定します。必要であれば、一度に複数指定できます。処理が完了した動画については、最初の呼び出しで返されるはずだった結果がそのまま返ります。処理中の動画については、到達した段階が返ります。

事前に準備しておく必要はありません。それがこの仕組みの要点です。呼び出しのたびに URL が必要なので、常に手元にあります。また、復旧が必要になるのは、設定しておこうと考えた人がいなかったときに限られます。照合は完全一致です。?si= パラメーターが追加または削除されている場合は別の文字列とみなされ、unknown が返ります。どの動画を指しているかを推測するより、わからないと伝えるほうがよいためです。

記録はサーバーのメモリに保存され、VIDEO_EXTRACT_TASK_TTL_MS の期限が切れると失効します。そのため、サーバーを再起動した場合も unknown が返ります。その理由も明確に伝えます。いずれにしても、destinationPath にあるファイルが永続的な結果です。

バックグラウンドタスク

どちらのツールもタスクに対応しています。通常の MCP ツール呼び出しとして実行すると、前述のすべての例は、クライアントがタスクを認識するかどうかにかかわらず、どのクライアントでも説明どおりに動作します。ただし、約 150ms の最低レイテンシーがあります。実際のダウンロードと比べれば気になりませんが、メタデータのみを取得する低コストの呼び出しではゼロではありません。タスクとして呼び出す場合は、MCP クライアントが(実験的な)MCP タスク機能を使って呼び出しにタスクの指定を付けます。この場合、ツールはブロックせず、すぐにハンドルを返し、処理中に進捗を通知します。実際の動画の処理に数分かかることもある analyze_video では、特に重要です。

  • ステータスメッセージは、バッチの現在の状況を示します。現在処理中の項目には "video 2/3: transcribing"、同時実行枠を待っている項目には "queued, 1 ahead" と表示されます。ステータスはクライアントによるポーリング(約 150ms ごと)で確認できるスナップショットであり、段階ごとのリアルタイムな通知ではありません。2 回のポーリングの間に開始して終了した段階は、まとめられて表示されないことがあります。
  • キャンセルの挙動は誠実で、見せかけではありません。また、キャンセルの単位は項目ごとではなくタスクごとです。 タスク内の項目がひとつも実行を開始していない場合は、完全にキャンセルできます。処理は実行されず、何も書き込まれません。いずれかの項目の実行が始まった時点で、どちらのツールでもタスク全体のキャンセルは拒否されます。タスクを黙って消すのではなく、処理を完了して結果を返すというメッセージが表示されます。5 本の動画を処理するバッチで、1 本の処理がすでに始まっていれば、残り 4 本がキューで待機中でもキャンセルは拒否されます。resolve_video はキューに入らないため、実行中の resolve_video タスクのキャンセルは常に拒否されます。
  • ハンドルはメモリ内にのみ保存されます。 タスク完了から VIDEO_EXTRACT_TASK_TTL_MS が経過すると(デフォルトでは 30 分)、ハンドルは失効します。また、サーバープロセスが終了すると、期限にかかわらず無効になります。ハンドルが残っていても、標準入力が閉じるとサーバープロセスは速やかに終了します。すでに destinationPath に書き込まれたファイルには、いずれの場合も影響しません。ツールがファイルを削除することはなく、ハンドルが失効した場合も同様です。
  • 通常の呼び出しは、ひとつだけ注意点がありますが、どこでも使えます。 タスクをサポートするには、実験的なタスク機能を実装した MCP クライアントが必要です。そのようなクライアントがない場合、前述の約 150ms の最低レイテンシーを除き、どちらのツールも上記の説明どおり同期的に動作します。
  • タスクをまったく利用できないクライアントもあります。 Claude デスクトップアプリがその一例で、問い合わせるとそのように回答します。この場合、通常の呼び出しがクライアント独自のタイムアウトに達する可能性がありますが、サーバーは処理を続けます。その後、get_status で結果を取得できます。

進捗の確認

上記の statusMessage は、ポーリングのたびのスナップショットです。便利ですが、情報はまとめられており、タスクが完了すると消えてしまいます。別のターミナルから確認する場合、クライアントの再起動後に確認する場合、またはマシン上のすべてのサーバーが処理している動画を横断して確認する場合など、長期間にわたる用途のために、各サーバーは小さなローカルステータスチャンネルも実行します。これはデフォルトで有効です。唯一の基本ルールは、サーバーが報告するのは観測結果であり、判断ではありません。 このチャンネル内のどの応答にも stale、stuck、healthy といった状態は示されず、完了率を作り出すこともありません。その判断は問い合わせる側に委ねられ、2回ポーリングして比較することで行います。

CLI。 インストールすると、video-extract status [--watch] [--json] [url...] はマシン上のすべての稼働中の video-extract-mcp サーバーを検出します。各 MCP クライアントはそれぞれ独自のサーバープロセスを起動しますが、このコマンドはそのすべてを統合します。やり取りしているサーバーだけではありません。そして、次のように1つのビューに表示します。

https://youtu.be/AbC123xyz  resolving → downloading → transcribing  (45s in stage) · asrWorker pid 4122 · cpu 38.2s · workdir 892 MB
https://youtu.be/DeF456uvw  resolving → downloading → transcribing → frames  (done 2m ago)
https://tiktok.com/@u/video/789  resolving → downloading  (372s in stage) · yt-dlp pid 4210 · cpu 12.4s · workdir 412 MB
https://youtu.be/JkL012rst  queued
server pid 4098 · up 14m · cap 2 · running 2 · queued 1

すべてのフィールドは、表示どおりの情報です。実行された順の生のステージ名、生の経過時間、実際に処理を行っている子プロセスとその累積 CPU 時間、そして処理中の項目に限り、作業ディレクトリの現在までのバイト数です。完了した項目のディレクトリはその後変更されないため、その項目の行には workdir の節はありません。--watch は Ctrl-C を押すまで毎秒表示を再描画します。--json は、すべての稼働中サーバーの情報を統合した同じ内容を、読み取りではなくスクリプト処理向けに、ANSI 制御バイトを含まないプレーンな JSON で出力します。位置引数を指定すると、その URL のみが出力されます(複数指定可能)。稼働中のサーバーがない場合、人間が読める形式では no live video-extract servers と表示され、--json では代わりに [] を出力します。いずれの場合も終了コードは 0 です。

エンドポイント。 各サーバーは localhost のみでアクセスできる GET /status も実行します。デフォルトでは一時ポートにバインドされ、上記の CLI 自体もこのエンドポイントのクライアントにすぎません。その URL は2つの方法でエージェントに伝えられるため、見つけるためにシェルコマンドを実行する必要はありません。1つ目は完了結果の statusUrl フィールド(エンドポイントが無効な場合は null)です。2つ目は、現時点ではハンドルしか持っていないタスクモードの呼び出し元に向けた、ハンドル応答自体の statusMessage で、status: <url> というプレフィックスが付きます。エンドポイントが無効な場合は、null ではなく、このプレフィックス自体が付きません。

curl http://127.0.0.1:PORT/status
curl 'http://127.0.0.1:PORT/status?url=https://youtu.be/AbC123xyz'   # repeatable

VIDEO_EXTRACT_STATUS_PORT を設定すると、ランダムなポートの代わりに指定したポートを使用します。値に 0 を指定すると、エンドポイント全体が無効になります(下記の環境変数テーブルを参照してください)。

遅い処理と停止状態を見分ける。 サーバーが報告するのは観測結果であり、判断ではありません。2回ポーリングして CPU とバイト数を比較し、ダウンロードが遅いのか停止しているのかを見分けます。6分間応答のないダウンロードは、巨大な動画ではよくあることですが、Wi-Fi の調子が悪い場合はバグです。このチャンネルではどちらかを推測しません。数秒間隔で /status を取得する(または video-extract status を実行する)ことで、対象項目の childCpuSeconds と作業ディレクトリのバイト数を比較してください。増えていれば処理中で、変化がなければ実際に停止しています。

処理を停止する。 このチャンネルには cancel サブコマンドはありません。停止するには、ステータス出力に表示された PID に対して通常の kill を実行します。対象によって動作が異なるのは意図的です。項目の子プロセスを強制終了すると、その項目は正しく失敗扱いになりますが、バッチ処理は続行され、タスク自体は完了します。サーバー自身の PID を強制終了すると、すべてが停止します。その時点ですでに destinationPath に書き込まれている内容は、サーバーが正常に終了した場合と同様に残ります。

ダウンロード途中のファイルは削除されます。 ダウンロード中のデータは .part という名前で書き込まれ、すべてのバイトが到着してから名前が変更されます。そのため、プロセスが強制終了されても、完了済みの動画に見えるファイルが残ることはありません。クラッシュ、強制終了、または再起動によって残ったファイルは、次にそのディレクトリを呼び出した際に回収されます。

削除されるのは、このツール自身が作成したファイルだけです。ツール自身のダウンロード途中のファイルと一時ディレクトリに限ります。マニフェスト、文字起こし、フレーム、完成した動画は決して削除されません。また、ブラウザーや独自の yt-dlp 実行で作成された .part ファイルも削除されません。 得られるのは結果であり、作業途中のデータではありません。 パイプラインが生成するデータは、返すデータよりはるかに多くあります。フィルタリング前のすべてのシーン境界候補や、frames: "key" モードでの正規化された再エンコードなどです。これらの処理はスクラッチ用のサブディレクトリで行われ、項目の処理が完了すると削除されます。そのため、destinationPath に保存されるのはマニフェスト、トランスクリプト、レスポンスで指定されたフレームのみ、および videoPath が指す動画ファイル1つです。

サーバーを終了すると、メモリ内のステータス履歴も失われます。設計上、再起動をまたいだ永続化はありません(docs/follow-ups.md を参照)。しかし、重要なデータが失われるわけではありません。いずれの場合も、destinationPath にあるファイルが永続的な記録となります。

「重要なフレーム」とは

各候補フレームは、次の指標に基づいてスコアリングされます。

指標検出するもの
シーン境界ハードカットやショットの切り替わり。境界の 約250~500ms後 にサンプリングするため、トランジションではなく新しいシーンを取得できます
画面上のテキストの新規性テキストが変わったスライドを検出します。位置も考慮するため、常に表示されている字幕バーが「新規」と判定されることはありません
映像品質ほかのフレームと比較する前に、モーションブラーやピンぼけのあるフレームを除外します
埋め込み類似度SigLIP の画像埋め込みを使用するため、見た目が同じ2つのフレームが両方とも選ばれることはありません

選択には、固定の重み付き合計ではなく、多様性を考慮した反復処理(最大周辺関連性)を用います。そのため、1つのフレームを選ぶと、次に選ぶフレームの価値も変わります。返される各フレームには、importance スコアと選択理由が含まれます。

対応ソース

実際にコードパスの動作を確認済みのものは、YouTube、TikTok、Facebook と Reels、X/Twitter、Instagram、Twitch、Vimeo、Reddit、WeChat Channels、および直接指定する .mp4/.m3u8 URL です。そのほか多くのサイトも、yt-dlp の汎用抽出機能を通じて動作します。動作しないサイトもありますが、その場合は例外をスローするのではなく、明確な失敗ステータスを返します。

WeChat Channels(视频号)のサポートについては、特に説明する価値があります。ブラウザー自動化や MITM プロキシを使わず、文書化されたリクエスト手順を通じて、ヘッドレスで解決します。サインイン済みの yuanbao.tencent.com セッションが必要です。ブラウザーから userCookies を呼び出す方法(セッションがない場合は、サインインできるようサイトが開きます)か、VIDEO_EXTRACT_WECHAT_COOKIE 環境変数を使用できます。このプロトコルは、Tencent が配信するフロントエンドと認証済みのプローブから、クリーンルーム方式で導き出しました。よく知られている既存の実装は MIT + Commons Clause であり、商用利用が制限されるため、意図的に参照していません。

知っておきたい設計上の制約

メモリは固定の上限ではなく、同時実行数あたりの使用量です。 音声認識と画像埋め込みはどちらも負荷の高いモデルですが、1回の解析中に同時に動くことはありません。それぞれ専用のワーカープロセスで実行され、次の処理が始まる前に終了します。ピーク使用量は、実行する音声モデルによって異なります。1回の解析あたりの実測値は、Whisper が文字起こしする場合(利用可能な字幕がない CJK 以外の動画すべて)、約 2.0 GB です。Whisper のワーカーだけで約 1.9 GB に達し、26 分の音声では約 2.1 GB まで増えます。SenseVoice を使う場合(中国語、日本語、韓国語、広東語)は 約 1.1 GB、字幕から文字起こしを取得できる場合は 1 GB 未満です(この場合、画像埋め込みの段階が最も重く、約 0.8 GB です)。合計の使用量 ≈ 同時実行数 × この値です。デフォルトの上限は 2 なので、最悪の場合に備えて約 4 GB を見込んでください。VIDEO_EXTRACT_MAX_CONCURRENCY=1 にすると解析は 1 件だけになり、約 2 GB に抑えられます。4 に増やすと約 8 GB になります。

Node のランタイムは 1 つだけです。 Python のサイドカーも、別の言語ランタイムを呼び出すサブプロセスもありません。音声認識には sherpa-onnx-node、画像埋め込みには @huggingface/transformers を使用します。

範囲指定リクエストは実際に機能します。 yt-dlp のソースで、2 時間の動画の 30〜340 秒を指定すると、2 時間分ではなく約 5 分のメディアをダウンロードします。直接 URL と WeChat の場合は、ダウンロード後にローカルでトリミングします。いずれの場合も、取得したクリップは0 秒から始まります。返信でその旨を伝え、オフセットも示します。

処理の劣化は通知されます。 OCR が停止した場合、埋め込みに失敗した場合、または音声認識でエラーが発生した場合でも、処理は続行され、警告が記録されます。空の文字起こしと、動画に音声がないだけの状態は、常に区別できます。

軽量なリクエストは軽量です。 1 フレームだけをリクエストすると、シーン検出、品質フィルタリング、OCR、埋め込み、文字起こし、および動画の再エンコードをスキップします。ソースが 6 秒でも 5 分でも、実測値は約 240 ミリ秒です。

文字起こしのみのリクエストでは動画をダウンロードしません。 字幕付きの動画で frames: "none" を指定すると、メディアはまったく取得されません。字幕だけで質問に回答でき、パイプライン内のほかの処理でファイルを必要としないためです。27 分の YouTube 動画での実測値は、285 MB ではなく 888 KB でした。この場合、参照できるローカルファイルがないため、返信には videoPath が含まれません。start/end の範囲を追加すると、これまでどおりダウンロードします。

ステータス

0.16.0(2026-09-23)時点で、macOS(Apple Silicon)上の実際の動画を使って実環境でテスト済みです。

  • 受け入れテストマトリクスは実際の URL を対象に実行され、実行した 10 行すべてに合格しました(docs/acceptance-matrix.md)。対象は、手動字幕ありと字幕なし(Whisper)の YouTube、TikTok、Facebook、サインインが必要な YouTube 動画(正常に auth_required を返すこと)、直接指定した MP4、Web ページに埋め込まれた動画、WeChat Channels、および中国語の Bilibili 動画(SenseVoice)です。範囲指定ダウンロードのテストは、YouTube が範囲指定の取得を 403 で拒否したため、初回は失敗しました。範囲指定を拒否された場合は動画全体をダウンロードしてローカルでトリミングするようになり、テストに合格します。DRM のテストは実行していません。手元に DRM のページがありませんでした。
  • X、Twitch、Instagram からのダウンロードも、Cookie なしで実環境で動作しました。Vimeo は yt-dlp(2026.08.19)でサインイン済みアカウントが必要になったため、userCookies と Vimeo へのサインインが必要です。Reddit は未テストです。
  • ブラウザー Cookie とサインイン(userCookies)は、サインインページを介した実際の WeChat サインインを含め、Safari でエンドツーエンドのテストを実行しました。
  • 821 件の自動テストに合格しています。これには、実際の MCP クライアントをエンドツーエンドで操作する統合テストも含まれます。CI では Node 22 と 26 を使い、Linux と macOS でテストを実行しています。
  • メモリ使用量と 1 フレーム処理のレイテンシは、推定値ではなく実測値です。

実環境でまだ実行していないテスト対象は、Intel Mac、CI のテストスイート以外での Linux、および Windows です。コードに macOS 固有の要素はないため、ほとんどの部分はほかの環境でも動作するはずですが、依存するネイティブライブラリはプラットフォームごとに異なります(プラットフォームサポートを参照)。

別のプラットフォームで、または上記以外のサイトを対象に npm run matrix を実行した結果は、このプロジェクトへの最も有益な貢献になります。以下を参照してください。

貢献

貢献を心から歓迎しており、参加への明確な道筋もあります。CONTRIBUTING.md に、セットアップ、最初の1時間を無駄にしかねないビルド上の落とし穴、テストの基準、そしてひっそりと壊れる不変条件について、すべて記載されています。要点は次のとおりです。

まず最も価値の高いことから: Apple Silicon の macOS 以外のプラットフォームで、環境変数に実際の URL を設定して npm run matrix を実行し、確認した内容をイシューに投稿してください。

すでに背景が記録されている項目も歓迎します: docs/follow-ups.md には、意図的に延期した項目とその理由がすべて記録されています。実際の映像を使ったセレクターの重みの調整、ファイル末尾の候補エッジ、直接ソースおよび WeChat ソースのバイト範囲取得などが含まれます。これらは漠然とした「初めての方におすすめ」のラベルではありません。それぞれ、試したことと見送った理由が説明されています。

簡単なルール:

  • テストは、コードが壊れていれば失敗することが期待されています。このプロジェクトのレビューで最もよく見つかっている問題は、どちらの場合でも通るテストです。テストを追加する場合は、それが保護する対象を変更し、失敗することを確認してください。
  • Python は使いません。Node ランタイムは1つだけです。
  • 共有型の唯一の信頼できる情報源は src/types.ts です。
  • 動画ごとの解析ステージングの不変条件を保ってください。1本の動画のパイプライン内では、負荷の高いステージ(音声認識、ビジョン埋め込み)を同時に実行せず、必ず順番に実行します。この方針により、同時解析あたりの使用量は、それぞれの合計ではなく約2 GB(Whisper)または約1.1 GB(SenseVoice)に抑えられます。異なる動画については、設計上、最大 VIDEO_EXTRACT_MAX_CONCURRENCY 件の解析を同時に実行します。
npm test          # full suite
npm run typecheck # strict, with noUncheckedIndexedAccess
npm run matrix    # acceptance matrix (honest about skips)

要件

Node≥ 22.12
システムバイナリffmpeg、ffprobe、yt-dlp、Deno(YouTube 用の yt-dlp の JavaScript ランタイム)、tesseract(中国語 OCR 用の chi_sim を含む)。OS ごとの手順:INSTALL.md
モデル約1.5 GB。scripts/fetch-models.sh が取得します — Silero VAD、Whisper small、SenseVoice
プラットフォームmacOS/arm64 で開発。詳しくはプラットフォームのサポートを参照してください

音声認識は言語に応じて振り分けられます。zh、yue、ja、ko は SenseVoice、それ以外は Whisper を使用します。音声に基づく言語検出はありません。インストール済みのライブラリは、実際に何が話されているかにかかわらず常に同じ値を返すためです。言語が分かっている場合は language を指定してください。

プラットフォームのサポート

このコードは設計上、特定のプラットフォームに依存しませんが、実行に使用するネイティブライブラリはプラットフォームに依存しており、それらが制限を定めます。

プラットフォーム状況
macOS、Apple Siliconここで開発し、実環境でテスト済みです。実際の URL を使った受け入れテストマトリックス、および9つのプラットフォームからのダウンロードを実施しています。音声認識ライブラリに同梱されているランタイムは、macOS 15.5 を最小バージョンとして宣言しています。それより古いバージョンではテストしていません。
macOS、Intelonnxruntime-node は1.24以降、Intel Mac 用バイナリを提供していないため、画像埋め込みは WebAssembly のフォールバックで実行されます。結果はほぼ同じですが、数倍遅く、processing.warnings に記録されます。ローカルでの音声認識には macOS 15 が必要です。実際の Intel ハードウェアではまだ実行していません。
macOS 12下記を参照してください。
Linux(glibc)CI で全テストスイートを実行しています。実際の URL を使った実環境での実行はまだありません。動作する見込みです。
Windowsまだ実行していません。エンジンは動作するはずですが、ブラウザーの Cookie が懸念点です。Chrome のアプリバインド暗号化によって、yt-dlp が Cookie を読み取れないことが確認されています。
macOS 12(Monterey)

あるコントリビューターが macOS 12.7.6(Intel)でパイプラインを実行し、必要な手順を記録しました(#5)。そのセットアップでは onnxruntime のバイナリにパッチを適用していましたが、このリリースでは代わりに WebAssembly フォールバックを使用します。これは macOS 12 ではまだ実行されていません。

  • Node 26 ではなく、Node 22 を使用してください。 Node 26 は macOS 12 で起動できません。バイナリが、macOS 12 では公開されていない libc++ シンボルを必要とするためです。
    dyld: Symbol not found: (__ZNSt3__122__libcpp_verbose_abortEPKcz)
      Expected in: /usr/lib/libc++.1.dylib
    
    このパッケージは Node 22.12 以降に対応しているため、最新の Node 22 を使用してください(nvm install 22)。
  • 画像埋め込みにはフォールバックが必要です。 onnxruntime の古い Intel バイナリは、macOS 12 より新しい libc++ を前提にビルドされているため、読み込みに失敗します(Symbol not found: __ZNSt3__18to_charsEPcS0_d)。また、現在のバージョンには Intel バイナリがありません。フォールバックはネイティブライブラリを使用しません。
  • 字幕のない動画ではローカル音声認識を利用できません。sherpa-onnx の Intel ビルドには macOS 15 が必要です。字幕付きの動画は利用できます。
  • OCR は利用できません。 Homebrew には Monterey 用の tesseract ボトルがありません(brew install --force-bottle tesseract を実行すると `--force-bottle` passed but tesseract has no bottle! と表示されます)。ソースからビルドするには、依存関係ツリー全体をコンパイルする必要があります。それでも、フレームは埋め込み、シーン、トランスクリプトウィンドウとともに返され、processing.warnings には ocr unavailable: ... spawn tesseract ENOENT が記録されます。失われるのは、画像に焼き込まれたテキストと WeChat Channels の OCR パスだけです。

ライセンス

MIT — LICENSE を参照してください。


キーワード:MCP サーバー、Model Context Protocol、動画文字起こし、キーフレーム抽出、YouTube トランスクリプト、TikTok ダウンローダー、WeChat Channels 视频号、Whisper、SenseVoice、SigLIP、yt-dlp、シーン検出、AI エージェントツール、動画理解、ローカル ASR、TypeScript、Node.js

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。