Skip to main content
Glama
README.md
# 多媒體下載與轉檔 MCP Server

> **版本:v4.0.0** | 更新日期:2026-05-19

一個由 Claude AI Agent 透過 MCP(Model Context Protocol)控制的自動化媒體處理系統。

---

## 版本紀錄

### v4.0.0(2026-05-19)— 架構全面升級

- **P0**:修復 yt-dlp 硬編碼路徑(動態偵測 venv / PATH),精簡工具 13 → 8 個
- **P1**:統一回傳結構(error_code 標準化),SSL 驗證改為 fallback 模式,合併重複下載邏輯
- **P2**:MCP Context 進度回報、下載歷史紀錄(自動跳過已下載)、YouTube Playlist 支援
- **P3**:新增 3 個 MCP Resources、6 個 Prompt 操作流程、字幕下載、`query_formats` 格式查詢工具

### v3.0.0(2026-05-19)— 專案整理與自動修復

- 新增 `start_mcp.sh` 啟動包裝腳本,每次啟動自動更新 yt-dlp 並清除快取
- 歸檔過時檔案,整理 `docs/` 目錄

### v2.1.0(2026-03-03)— 修復 YouTube JavaScript Runtime

- 加入 `--js-runtimes node:...` 和 `--remote-components ejs:github`

### v2.0.0(2025-12-31)— MCP Server 大幅升級

- MCP Server 增加至 13 個工具,新增 HLS / Podcast / 白名單功能

### v1.0.0(2025-12-10)— 初始版本

- 基礎 YouTube 下載與 MP3 轉換,Claude Desktop MCP 整合

---

## 功能特色

- **批量下載**:支援 YouTube 單影片、Playlist、頻道 URL 同時下載多個
- **下載進度**:Agent 可即時收到每個 URL 的下載進度通知
- **歷史紀錄**:自動跳過已下載的 URL(檔案存在才跳過)
- **字幕下載**:下載影片時同步取得 SRT 字幕
- **格式查詢**:下載前查詢可用畫質和字幕語言
- **HLS 串流下載**:下載 `.m3u8` 串流並自動轉為 MP4
- **Podcast 下載**:支援 RSS Feed 解析和直接音檔連結
- **格式轉換**:使用 FFmpeg 轉換為 MP3,可選擇同步進行 EBU R128 響度正規化
- **圖片下載**:下載圖片並轉換為 JPG
- **網路白名單**:限制下載來源,防止誤操作
- **自動修復**:每次啟動自動更新 yt-dlp、補裝套件、清除快取
- **MCP Resources**:白名單狀態、yt-dlp 版本、下載歷史可直接查閱(不佔工具位)
- **MCP Prompts**:6 個操作流程卡片,引導 Agent 最佳操作路徑

---

## 系統需求

- **Python 3.10+**
- **yt-dlp**:YouTube 及各平台影音下載
- **FFmpeg**:音視頻轉檔
- **Node.js**:YouTube n-challenge 解密(建議安裝)

### macOS 安裝

```bash
brew install yt-dlp ffmpeg node
```

---

## 安裝步驟

```bash
# 1. 克隆專案
git clone <your-repo-url>
cd DownloadVideoPythonProject

# 2. 建立虛擬環境
python -m venv .venv
source .venv/bin/activate

# 3. 安裝 Python 套件
pip install -r requirements.txt

# 4. 測試啟動
python server.py
```

---

## 使用方法

### Claude Desktop 整合(推薦)

編輯 Claude Desktop 配置檔:

**macOS**:`~/Library/Application Support/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "media-downloader": {
      "command": "/bin/bash",
      "args": [
        "/你的專案路徑/DownloadVideoPythonProject/start_mcp.sh"
      ]
    }
  }
}
```

> 使用 `start_mcp.sh` 啟動:每次啟動自動更新 yt-dlp、補裝缺少套件、清除快取,解決 yt-dlp 因 YouTube 反爬蟲更新而失效的問題。

設定完成後**重啟 Claude Desktop**,即可透過對話使用所有功能。

### CLI 工具

```bash
# 下載單一音檔
python download_cli.py "https://www.youtube.com/watch?v=xxxxx"

# 下載並指定輸出目錄
python download_cli.py "url" -o ./my_downloads

# 批量下載
python download_cli.py "url1" "url2" "url3"

# 查看完整說明
python download_cli.py --help
```

---

## 對話使用範例

以下為在 Claude Desktop 中與 Agent 對話的實際用法。

### 下載 YouTube 影片音檔

```
下載這個 YouTube 影片的音檔:
https://www.youtube.com/watch?v=xxxxx
```

Agent 會自動呼叫 `download_media`,下載過程中回報進度,完成後告知檔案位置。

### 下載整個 Playlist

```
下載這個 YouTube 播放清單的所有音檔,存到 ./music:
https://www.youtube.com/playlist?list=PLxxxxx
```

`download_media` 支援 playlist URL,yt-dlp 自動展開逐一下載。

### 下載影片並附帶繁中字幕

```
下載這部影片,要有繁體中文字幕:
https://www.youtube.com/watch?v=xxxxx
```

Agent 會先呼叫 `query_formats` 確認字幕語言,再以 `subtitles=True, sub_lang="zh-TW"` 下載,字幕 `.srt` 儲存在影片同目錄。

### 查詢可用格式

```
查詢這個影片有哪些可用畫質和字幕語言:
https://www.youtube.com/watch?v=xxxxx
```

呼叫 `query_formats`,回傳格式清單(解析度、副檔名)和可用字幕語言代碼。

### 下載 Podcast

```
下載這個 Podcast 的最新一集:
https://feeds.example.com/podcast.rss
```

```
下載第 3 集(index=2):
https://feeds.example.com/podcast.rss  episode_index=2
```

### 下載 HLS 串流

```
幫我下載這個 m3u8 串流,輸出為 ./downloads/video.mp4:
https://example.com/stream/index.m3u8
```

### 管理白名單

```
列出目前的白名單規則
```

```
新增 *.example.com 到白名單
```

```
移除 example.com
```

### 查看下載歷史

```
顯示最近的下載紀錄
```

Agent 會自動讀取 `data://download-history` Resource,回傳最近 20 筆紀錄。

### 診斷 MCP 狀態

```
幫我診斷一下 MCP 是否正常,yt-dlp 版本是否最新
```

在輸入框輸入 `/` 選取 `check_mcp_health`,或直接在對話中說「用 check_mcp_health 診斷」,Agent 會依序讀取版本、白名單、下載紀錄並給出建議。

---

## MCP 工具一覽(8 個)

| 工具 | 說明 |
|------|------|
| `download_media` | yt-dlp 下載影音,支援 playlist、字幕、進度回報、歷史跳過 |
| `convert_to_mp3` | FFmpeg 轉換音視頻為 MP3(可加 `normalize=True` 做 EBU R128 響度正規化) |
| `download_and_convert_image` | 下載圖片並轉換為 JPG |
| `podcast_downloader` | Podcast 下載(RSS Feed 或直接音檔連結) |
| `download_hls_tool` | HLS (.m3u8) 串流下載並轉為 MP4 |
| `direct_download_audio` | 純 HTTP 下載音檔(不依賴 yt-dlp) |
| `whitelist_manage` | 白名單查詢、新增、移除、啟用/停用 |
| `query_formats` | 查詢 URL 可用的影片格式與字幕語言 |

> **規劃中(尚未實作)**:音量調整工具 `adjust_audio`(對既有音檔調整音量)。設計細節見 [P4 任務文件](docs/shared/adjust_audio_volume.md)。

## MCP Resources(3 個,不佔工具位)

Resources 是唯讀資料,需透過 Claude Desktop UI **手動附加**到對話中才能讓 Agent 讀取。
Agent **不會**主動偵測並讀取 Resource,自然語言也無法直接觸發(yt-dlp 版本、下載歷史都沒有對應工具)。

| URI | 說明 |
|-----|------|
| `config://whitelist` | 白名單規則和啟用狀態 |
| `status://ytdlp-version` | yt-dlp 版本號和執行路徑 |
| `data://download-history` | 最近 20 筆下載紀錄 |

### 使用步驟(Claude Desktop)

1. 在對話輸入框左下角點擊 **`+`** 按鈕
2. 選擇 **「Add from MCP」** 或 **「MCP Resources」**
3. 找到 `media-downloader` 伺服器,選取要附加的 Resource
4. Resource 會顯示為對話中的附件,Agent 即可讀取其內容
5. 輸入問題,例如:

```
根據剛才附加的 Resource,yt-dlp 目前版本是多少?
```

```
根據附加的下載歷史,最近下載了哪些檔案?
```

> **注意**:Claude Desktop UI 的 Resource 入口位置隨版本不同可能有差異,若找不到 `+` 選單,可改用下方「直接指定 URI」的方式。

### 備用方式:直接在對話中指定 URI

若 UI 無法附加,可明確告訴 Agent 要讀取哪個 Resource:

```
請使用 MCP resource 讀取 status://ytdlp-version,告訴我 yt-dlp 版本
```

```
請用 MCP resource 讀取 data://download-history,列出最近的下載紀錄
```

---

## MCP Prompts(6 個)

Prompts 是預設的操作流程說明,有兩種使用方式:

### 方式一:透過 Claude Desktop UI(推薦)

**`+` 按鈕:**
1. 點擊對話輸入框左下角的 **`+`** 按鈕
2. 選擇 **「MCP Prompts」** 或對應的 `media-downloader` 項目
3. 點選 Prompt 名稱後自動帶入流程說明

**`/` 斜線指令:**
1. 在對話輸入框直接輸入 **`/`**
2. 從彈出清單找到 Prompt 名稱並點選

> 首次設定或修改 Prompt 後需**重啟 Claude Desktop** 才會出現在清單中。

### 方式二:在對話中直接呼叫

```
使用 batch_download_youtube 流程,下載這幾個 URL:
https://www.youtube.com/watch?v=aaa
```

```
用 check_mcp_health 診斷一下目前 MCP 狀態
```

Agent 先取得 Prompt 的步驟說明,再依序呼叫對應工具。

| Prompt | 適用情境 |
|--------|---------|
| `batch_download_youtube` | YouTube 批量下載音檔的完整流程 |
| `download_with_subtitles` | 下載影片同時取得字幕 |
| `download_podcast_series` | Podcast RSS 整季下載 |
| `download_hls_stream` | HLS 串流下載 |
| `manage_whitelist` | 白名單完整設定流程(含常用規則建議) |
| `check_mcp_health` | 診斷 MCP 狀態(版本 / 白名單 / 歷史紀錄) |

---

## 專案結構

```
DownloadVideoPythonProject/
├── server.py                    # MCP 伺服器主程式(8 Tools + 3 Resources + 6 Prompts)
├── start_mcp.sh                 # 啟動包裝腳本(自動更新 yt-dlp)
├── download_cli.py              # CLI 下載工具
├── requirements.txt             # Python 套件依賴
├── whitelist.json               # 白名單設定檔
├── claude_desktop_config.example.json  # Claude Desktop 設定範例
├── tools/
│   ├── podcast_downloader.py    # Podcast 下載
│   └── hls_downloader/          # HLS 串流下載模組
├── utils/
│   ├── audio_downloader.py      # 音檔直接下載(SSL fallback)
│   ├── download_history.py      # 下載歷史紀錄管理
│   ├── path_resolver.py         # yt-dlp / Node.js 路徑偵測、YouTube player client、檔名清洗參數
│   ├── response.py              # 統一回傳結構(success_response / error_response)
│   ├── whitelist_validator.py   # 白名單驗證
│   ├── rss_parser.py            # RSS Feed 解析
│   ├── loudnorm.py              # 兩趟式 EBU R128 響度正規化
│   └── sanitizer.py             # 檔名清洗、Unicode 正規化與路徑還原
├── docs/
│   ├── shared/                  # 需求文件(P0–P3 全部完成)
│   ├── guides/                  # 使用指南(白名單、HLS)
│   ├── spec/                    # 功能規格書
│   ├── issues/                  # 問題紀錄
│   └── archive/                 # 歸檔(過時文件與腳本)
├── downloads/                   # 下載檔案目錄
└── images/                      # 圖片儲存目錄
```

---

## 常見問題

### yt-dlp 下載失敗(YTDLP_FAILED)

重啟 Claude Desktop,`start_mcp.sh` 會自動執行 `pip install --upgrade yt-dlp` 和 `yt-dlp --rm-cache-dir`。

手動更新:

```bash
source .venv/bin/activate
pip install --upgrade yt-dlp
yt-dlp --rm-cache-dir
```

### YouTube 下載失敗:`n challenge solving failed` / 只抓得到縮圖

**現象**:

```
WARNING: [youtube] xxxxx: n challenge solving failed: Some formats may be missing.
WARNING: Only images are available for download. use --list-formats to see them
ERROR: [youtube] xxxxx: Requested format is not available.
```

**原因**:YouTube 對播放連結加了一層「n 參數」JS 加密挑戰,yt-dlp 需要本機安裝 JS runtime(Node.js 或 Deno)才能解出真正可用的影音網址。若機器上沒有可用的 JS runtime,yt-dlp 只能拿到縮圖,導致指定的 `mp4`/`m4a` 格式全部找不到。

**排除步驟**(2026-07-13 實測有效):

1. 確認 yt-dlp 是最新版:`pip install -U yt-dlp`(若已是最新版,此步驟不會解決問題,需繼續下一步)
2. 安裝 Deno 作為 JS challenge solver:

```bash
brew install deno
```

3. 安裝完成後 **重新開一個終端機視窗**(讓新 shell 讀到含 `deno` 的 PATH),yt-dlp 不需要加任何參數就會自動偵測並使用 Deno:

```
[youtube] [jsc:deno] Solving JS challenges using deno
```

看到這行代表 n challenge 已成功解開,下載會恢復正常。

### 檔案明明存在,轉檔卻說找不到

**現象**:`ls` 列得出檔案,但照著螢幕把檔名打一次就失敗:

```
ls: 睡前故事 EP143 《我從哪裡來?》 生命教育|兒童性教育|家庭生活.mp4: No such file or directory
```

**原因**:影片標題夾帶 NBSP(U+00A0)等不可見空白,寫進檔名後與半形空格完全無法分辨。用 `repr()` 即可現形:

```bash
python3 -c "import os; [print(repr(f)) for f in os.listdir('.')]"
```

**處理**:2026-09-15 起下載時已自動清洗,新檔案不會再有此問題(**需重啟 MCP server**)。此日期之前下載的舊檔案檔名仍帶不可見字元,但 `convert_to_mp3` 與 media-processor 技能腳本已能自動比對還原,可直接沿用。完整分析見 [ISSUE-004](docs/issues/ISSUE-004-Invisible-Whitespace-In-Filenames.md)。

### 轉檔後音檔長度大幅縮短(大寫副檔名 `.MP3`)

**現象**:對 `.MP3`(大寫)檔案轉檔後,原始檔案內容大量消失,但工具回報成功:

```
轉檔前: 2760832 bytes, 600.0 秒
轉檔後:   32262 bytes,   6.9 秒
```

**原因**:macOS 檔案系統不分大小寫,`song.MP3` 與 `song.mp3` 是同一個檔案,舊版以字串比較無法判斷,導致 FFmpeg 同時讀寫同一檔案。

**處理**:2026-09-15 起已改用 `os.path.samefile()` 判斷,不會再發生(**需重啟 MCP server**)。**已受損的檔案無法復原**,若先前曾對大寫副檔名的 MP3 轉檔,請檢查長度並從原始來源重新取得。完整分析見 [ISSUE-005](docs/issues/ISSUE-005-Case-Insensitive-Path-Comparison-Data-Loss.md)。

### Claude Desktop 無法連接 MCP Server

1. 確認 `claude_desktop_config.json` 中的路徑正確(絕對路徑)
2. 按 ⌘Q 完全退出後重開 Claude Desktop
3. 查看日誌:

```bash
tail -f ~/Library/Logs/Claude/mcp-server-media-downloader.log
```

### 白名單驗證失敗(WHITELIST_DENIED)

在 Claude Desktop 輸入:

```
列出白名單規則,並把 youtube.com 和 *.googlevideo.com 加入
```

詳細說明:[白名單指南](docs/guides/WHITELIST_GUIDE.md)

### HLS 下載相關問題

參閱:[HLS 設定指南](docs/guides/HLS_SETUP_GUIDE.md)

### 重複下載同一個 URL

`download_media` 會自動查詢歷史紀錄,已下載且檔案存在時回傳 `status: skipped`,不重複下載。若需強制重新下載,請先刪除 `download_history.json`。

---

## 安全注意事項

- 僅下載有版權或授權的內容
- 建議啟用白名單功能,限制下載來源
- `download_history.json` 已排除於 git 追蹤之外

---

## 授權

MIT License