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