📼

AIに「ダウンロードして」と言うだけ。動画保存ツールを作った。

に公開

TL;DR

なぜ作ったか

動画をローカルに保存したい場面はけっこうある。オフラインで見たい、素材として使いたい、アーカイブしておきたい。

技術者なら yt-dlp を使うのが定番だけど、毎回こうなる。

yt-dlp --write-thumbnail --convert-thumbnails jpg \
  --write-subs --write-auto-subs --sub-langs ja,ja-orig \
  --write-description \
  -f "bestvideo+bestaudio/best" \
  --merge-output-format mp4 \
  -o "%(channel)s/%(title)s/%(title)s.%(ext)s" \
  "https://example.com/watch?v=VIDEO_ID"

覚えられるわけがない。

さらに、動画サイト 以外からも取得したいケースがある。Torrent で配布されているアーカイブ、RTMP で流れているライブ配信、普通のサイトに埋め込まれた動画……それぞれ別のツールが必要で、コマンドも全然違う。

じゃあもう、URL を渡したらバックエンドを自動選択して全部やってくれるツールを作ればいいのでは?

そう思って作ったのが ytdl v2.0 です。

インストール

npm install -g @kanketsu/ytdl

グローバルインストールせずに試したい場合は npx でも実行できます。

npx @kanketsu/ytdl "URL"

yt-dlp と ffmpeg が必要ですが、初回実行時になければ自動でインストールを提案してくれます。

基本的な使い方

一番かんたん:引数なしで実行

ytdl

インタラクティブ UI が起動します。URL を貼って、あとはポチポチ選ぶだけ。

◆  ytdl

◆  URL
│  https://example.com/watch?v=VIDEO_ID

◆  何をダウンロードしますか?
│  ● 動画(最高画質, mp4)
│  ○ 音声のみ(m4a)
│  ○ 情報のみ表示(ダウンロードしない)

◆  画質
│  ● 最高画質(おすすめ)
│  ○ 4K (2160p)
│  ○ 1080p
│  ○ 720p

◆  保存先
│  ~/Downloads

◇  ダウンロードを開始しますか?
│  はい

URL を渡すだけで自動判定

ytdl は URL のプロトコルやパターンを見て、バックエンドを自動選択します。

# 動画サイト(yt-dlp バックエンド)
ytdl "https://example.com/watch?v=VIDEO_ID"

# マグネットリンク(Torrent バックエンド)
ytdl "magnet:?xt=urn:btih:..."

# RTMP ライブ配信(ffmpeg バックエンド)
ytdl "rtmp://live.example.com/stream/key"

# .torrent URL(Torrent バックエンド)
ytdl "https://example.com/file.torrent"

各バックエンドの使い方

# yt-dlp(動画サイト、1000以上対応)
ytdl "URL"                      # 最高画質
ytdl -a "URL"                   # 音声のみ (m4a)
ytdl -q 720 "URL"               # 720p 制限
ytdl -p "playlist URL"          # プレイリスト一括

# Torrent(P2P)
ytdl "magnet:?xt=urn:btih:..."
ytdl "https://example.com/file.torrent"

# ストリーム録画(RTMP/RTSP)
ytdl "rtmp://..."               # 停止まで録画
ytdl --duration 3600 "rtmp://..." # 1時間録画
ytdl "rtsp://camera.example.com/feed"

# サイト解析(yt-dlp で取れない場合)
ytdl --analyze "https://example.com/page-with-video"

# バックエンドを明示指定
ytdl --via torrent "magnet:?xt=..."
ytdl --via stream "rtmp://..."

オプション一覧

フラグ 説明 デフォルト
-a 音声のみ(m4a) off
-q <解像度> 画質指定(360/480/720/1080/1440/2160) 最高画質
-o <ディレクトリ> 保存先 ~/Downloads
-p プレイリストモード off
-b <ブラウザ> クッキー取得元ブラウザ off
-n クッキーなし(デフォルト) on
-i 情報のみ表示 off
-t 文字起こし off
--backend <b> 文字起こしバックエンド (local/api) local
--manuscript <path> 原稿ファイルパス(精度向上) -
--lang <code> 表示言語(ja/en/zh-Hans/es/hi/pt/id) ja
--via <backend> バックエンド指定(ytdlp/torrent/stream/analyzer) 自動
--analyze サイト解析モードを強制 off
--duration <秒> ストリーム録画時間(秒) 停止まで
-- 以降を yt-dlp に直接渡す -

もうちょっと深掘ってみた

ここからは、ytdl v2.0 の内部構造と設計について掘り下げていきます。

アーキテクチャ

v2.0 で最大の変更点は URL ルーターとマルチバックエンド構成です。

v1.x では Node.js → bash の一本道でしたが、v2.0 では lib/router.js が URL を見てバックエンドを振り分けます。各バックエンドは lib/backends/ 配下の独立したモジュールです。

重要な設計判断: bin/ytdl.sh は一切変更しない。yt-dlp バックエンド(lib/backends/ytdlp.js)が ytdl.sh をそのままラップする形にしました。既存の動作を完全に保ちつつ、新機能を追加できます。

URL ルーティングの仕組み

// lib/router.js(抜粋)
export function routeUrl(url) {
  if (url.startsWith('magnet:')) return { backend: 'torrent', reason: 'magnet URI' }
  if (/\.torrent(\?.*)?$/.test(url)) return { backend: 'torrent', reason: '.torrent URL' }

  const protocol = new URL(url).protocol
  if (['rtmp:', 'rtsp:'].includes(protocol)) return { backend: 'stream', reason: `${protocol} stream` }
  if (['http:', 'https:'].includes(protocol)) return { backend: 'ytdlp', reason: 'http/https URL' }

  return { backend: 'ytdlp', reason: 'fallback' }
}

シンプルなパターンマッチング。URL のプロトコルとパターンだけで判定します。

Torrent バックエンド

webtorrent を使った P2P ダウンロードです。webtorrent は optionalDependencies なので、必要なときだけインストールされます。

// lib/backends/torrent.js(概略)
import { createRequire } from 'module'
const require = createRequire(import.meta.url)

// webtorrent が未インストールの場合はインストールを促す
let WebTorrent
try {
  WebTorrent = require('webtorrent')
} catch {
  // ユーザーに「インストールしますか?」を提示
}

ストリーム録画バックエンド

ffmpeg を spawn で直接呼び出してストリームを録画します。--duration で録画時間を指定できます。

# 内部的には ffmpeg が呼ばれる
ffmpeg -i rtmp://... -c copy output.mp4

サイト解析バックエンド

Chrome DevTools Protocol (CDP) でページを開き、ネットワークリクエストの監視 + DOM から <video> タグや OGP タグを探します。Puppeteer を使わず、ws パッケージだけで Chrome を直接制御する軽量アプローチです。ログイン付きダウンロードにも対応し、永続 Chrome プロファイル(~/.ytdl/chrome-profile/)でセッションを保持できます。yt-dlp が失敗したときの自動フォールバックにもなります。

セキュリティ

v1 から引き続き、コマンドインジェクション対策を徹底しています。

// ✅ 安全: 配列で引数を渡す(shell 展開なし)
spawn("bash", [SCRIPT, ...args], { stdio: "inherit" });

// ❌ 危険(ytdl ではやっていない)
exec(`bash ${SCRIPT} ${args.join(" ")}`);

Torrent バックエンドも同様に、マグネットリンクをそのまま webtorrent API に渡すだけで、シェルには渡しません。

npm パッケージとしての構成

{
  "name": "@kanketsu/ytdl",
  "version": "2.0.0",
  "dependencies": {
    "@clack/prompts": "^1.0.1",
    "picocolors": "^1.1.1"
  },
  "optionalDependencies": {
    "webtorrent": "^2.5.0",
    "ws": "^8.19.0"
  }
}

webtorrent と ws(サイト解析用 WebSocket クライアント)は optionalDependencies に。普段は不要、使うときだけインストールされます。

Claude Code プラグインとして使う

v2.0 でプラグインの機能も強化されました。マグネットリンクや RTMP URL を貼っても Claude が適切に処理してくれます。

URL の種別を Claude が判断する必要はありません。ytdl が自動で振り分けます。

インストール

Claude Code で以下を実行。

/plugin marketplace add kanketsu-jp/ytdl
/plugin install ytdl@kanketsu-ytdl

まとめ

実際に試してみよう

# インストール
npm install -g @kanketsu/ytdl

# 動画サイト 動画
ytdl "https://example.com/watch?v=VIDEO_ID"

# インタラクティブモード
ytdl

# 英語で試す
ytdl --lang en

Claude Code ユーザーなら、プラグインも試してみてください。

/plugin marketplace add kanketsu-jp/ytdl
/plugin install ytdl@kanketsu-ytdl

あとは動画の URL(動画サイト でも Torrent でも)を貼って「ダウンロードして」と言うだけです。

※動画ダウンロードについて

参考文献

Discussion