metube/yt-dlp扩展器开发完整指南.md
柯孟凯 8f17018fe3 添加完整的yt-dlp扩展器开发指南文档
## 文档内容
1. yt-dlp扩展器开发完整指南.md - 详细的端到端开发指南
2. yt-dlp扩展器快速参考.md - 精简的快速参考卡片

## 涵盖内容
- 完整的开发流程和最佳实践
- 通用扩展器代码模板
- Metube集成的详细步骤
- 故障排除和调试技巧
- 部署方案和检查清单

为以后扩展其他网站提供标准化指南。
2025-09-23 14:38:41 +08:00

636 lines
No EOL
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# yt-dlp 扩展器开发完整指南
本文档基于 tingdao.org 扩展器的成功开发经验,提供了一套完整的 yt-dlp 扩展器开发和 Metube 集成方案。
## 📋 目录
1. [项目概述](#项目概述)
2. [技术架构](#技术架构)
3. [开发流程](#开发流程)
4. [实现细节](#实现细节)
5. [Metube 集成](#metube-集成)
6. [部署方案](#部署方案)
7. [故障排除](#故障排除)
8. [通用模板](#通用模板)
## 🎯 项目概述
### 功能目标
- 为 yt-dlp 添加新网站支持
- 与 Metube Web 界面无缝集成
- 支持单个视频和播放列表下载
- 完整的元数据提取和错误处理
### 成功案例tingdao.org
- **网站类型**: 中文基督教音频内容网站
- **API 架构**: REST API with POST 请求
- **内容格式**: MP3 音频文件
- **特殊功能**: 主要和备用音频源、播放列表支持
## 🏗️ 技术架构
### 核心组件
```
yt-dlp 扩展器
├── 插件包 (yt_dlp_plugins)
│ └── extractor/
│ └── 自定义扩展器.py
├── 配置文件 (setup.cfg, pyproject.toml)
├── Metube 集成修改
└── 环境配置
```
### 技术栈
- **Python 3.7+**
- **yt-dlp 2025.09.05+**
- **Metube** (aiohttp + Socket.IO)
- **正则表达式** (URL 匹配)
- **JSON API 处理**
## 🚀 开发流程
### 阶段 1: 网站分析和 API 调研
#### 1.1 网站结构分析
```bash
# 使用浏览器开发者工具分析
# 1. URL 模式识别
# 2. 网络请求监控
# 3. API 端点发现
# 4. 数据结构分析
```
#### 1.2 API 接口调研
```python
# 测试 API 调用
import requests
# 发现正确的 API 端点
api_url = "https://example.com/api/endpoint"
data = {'param': 'value'}
response = requests.post(api_url, data=data)
```
#### 1.3 数据结构映射
```json
// 分析 API 响应结构
{
"status": 1,
"list": {
"mediaList": [...],
"authorMsg": {...}
}
}
```
### 阶段 2: 插件开发
#### 2.1 项目结构创建
```bash
# 按照 yt-dlp 官方规范创建目录
mkdir -p your-plugin/yt_dlp_plugins/extractor
```
#### 2.2 配置文件设置
**setup.cfg**:
```ini
[metadata]
name = yt-dlp-yoursite-plugin
version = 1.0.0
description = yt-dlp extractor plugin for yoursite.com
author = Your Name
license = Public Domain
[options]
packages = find_namespace:
python_requires = >=3.7
```
**pyproject.toml**:
```toml
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project]
name = "yt-dlp-yoursite-plugin"
version = "1.0.0"
description = "yt-dlp extractor plugin for yoursite.com"
authors = [{name = "Your Name"}]
license = {text = "Public Domain"}
requires-python = ">=3.7"
```
#### 2.3 扩展器代码实现
**关键实现要点**:
1. **类命名规范**: 必须以 `IE` 结尾
2. **URL 匹配**: 使用正则表达式精确匹配
3. **API 调用**: 正确的请求头和参数
4. **数据提取**: 完整的元数据处理
5. **错误处理**: 优雅的异常处理
6. **格式支持**: 主要和备用源处理
### 阶段 3: Metube 集成
#### 3.1 修改 Metube 配置支持
**app/main.py** 添加插件目录配置:
```python
_DEFAULTS = {
# ... 其他配置
'YTDL_PLUGINS_DIR': '',
# ...
}
```
#### 3.2 修改下载引擎支持插件
**app/ytdl.py** 添加插件加载逻辑:
```python
# 在 YoutubeDL 参数中添加插件目录
if (self.manager and
hasattr(self.manager, 'config') and
hasattr(self.manager.config, 'YTDL_PLUGINS_DIR') and
self.manager.config.YTDL_PLUGINS_DIR):
import glob
plugin_dirs = glob.glob(f"{self.manager.config.YTDL_PLUGINS_DIR}/*")
plugin_dirs = [d for d in plugin_dirs if os.path.isdir(d)]
if plugin_dirs:
params['plugin_dirs'] = plugin_dirs
```
### 阶段 4: 测试和验证
#### 4.1 插件安装测试
```bash
# 安装插件包
cd your-plugin
pip install -e .
# 验证插件加载
export PYTHONPATH="/path/to/your-plugin"
yt-dlp --list-extractors | grep yoursite
```
#### 4.2 功能测试
```bash
# 命令行测试
yt-dlp "https://yoursite.com/video/123" --dump-json
# 实际下载测试
yt-dlp "https://yoursite.com/video/123"
```
#### 4.3 Metube 集成测试
```bash
# 启动 Metube 服务器
export PYTHONPATH="/path/to/your-plugin"
pipenv run python3 app/main.py
# 浏览器测试 Web 界面
# http://localhost:8081
```
## 📝 实现细节
### 通用扩展器模板
```python
"""
YourSite.com extractor for yt-dlp
This extractor supports downloading content from yoursite.com
Author: Your Name
License: Public Domain
"""
from datetime import datetime
from yt_dlp.extractor.common import InfoExtractor
from yt_dlp.utils import ExtractorError, int_or_none, try_get
class YourSiteIE(InfoExtractor):
"""Extractor for yoursite.com content"""
IE_NAME = 'yoursite'
IE_DESC = 'yoursite.com content'
_VALID_URL = r'https?://(?:www\.)?yoursite\.com/video/(?P<id>\d+)'
_TESTS = [{
'url': 'https://www.yoursite.com/video/123',
'info_dict': {
'id': '123',
'title': 'Test Video',
'ext': 'mp4',
# 添加更多测试数据
},
'params': {
'skip_download': True,
}
}]
def _real_extract(self, url):
"""Main extraction method"""
video_id = self._match_id(url)
# 调用 API 获取视频信息
video_data = self._download_json(
'https://api.yoursite.com/video',
video_id,
data=f'id={video_id}'.encode(),
headers={
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (compatible; yt-dlp)',
'Accept': 'application/json',
},
note='Downloading video metadata',
errnote='Failed to download video metadata'
)
# 检查 API 响应
if video_data.get('status') != 'success':
raise ExtractorError(
f'API returned error: {video_data.get("message", "Unknown error")}',
expected=True
)
# 提取视频信息
video_info = video_data.get('data', {})
# 构建格式列表
formats = []
# 主要视频源
if video_info.get('video_url'):
formats.append({
'url': video_info['video_url'],
'format_id': 'primary',
'quality': 1,
})
# 备用视频源
if video_info.get('backup_url'):
formats.append({
'url': video_info['backup_url'],
'format_id': 'backup',
'quality': 0,
})
if not formats:
raise ExtractorError('No video URLs found', expected=True)
# 解析时间戳
timestamp = self._parse_timestamp(video_info.get('upload_time'))
return {
'id': video_id,
'title': video_info.get('title', '').strip(),
'timestamp': timestamp,
'uploader': video_info.get('uploader'),
'formats': formats,
}
def _parse_timestamp(self, time_str):
"""解析时间戳"""
if not time_str:
return None
try:
# 根据实际格式调整
dt = datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S')
return int(dt.timestamp())
except (ValueError, TypeError) as e:
self.report_warning(f'Failed to parse timestamp "{time_str}": {e}')
return None
```
### API 调用最佳实践
#### 1. 请求头配置
```python
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'application/json, text/plain, */*',
'Referer': 'https://yoursite.com/',
'Origin': 'https://yoursite.com',
}
```
#### 2. 参数处理
```python
# POST 数据编码
data = f'param1={value1}&param2={value2}'.encode()
# GET 参数
params = {'param1': value1, 'param2': value2}
```
#### 3. 错误处理
```python
# API 状态检查
if api_response.get('status') != 1:
raise ExtractorError(
f'API error: {api_response.get("msg", "Unknown error")}',
expected=True
)
# 数据验证
if not media_list:
raise ExtractorError('No media found', expected=True)
```
### 格式处理策略
#### 1. 多源支持
```python
formats = []
# 主要源
if primary_url:
formats.append({
'url': primary_url,
'format_id': 'primary',
'quality': 1,
'ext': 'mp4',
})
# 备用源
if backup_url and backup_url != primary_url:
formats.append({
'url': backup_url,
'format_id': 'backup',
'quality': 0,
'ext': 'mp4',
})
```
#### 2. 音频专用处理
```python
formats.append({
'url': audio_url,
'ext': 'mp3',
'acodec': 'mp3',
'vcodec': 'none',
'abr': 128,
'format_id': 'audio',
})
```
## 🔧 Metube 集成
### 服务器启动配置
#### 环境变量方式
```bash
export PYTHONPATH="/path/to/your-plugin"
pipenv run python3 app/main.py
```
#### Docker 方式
```yaml
# docker-compose.yml
services:
metube:
image: alexta69/metube
environment:
- YTDL_PLUGINS_DIR=/app/plugins
volumes:
- "./downloads:/downloads"
- "./plugins:/app/plugins"
```
### 修改清单
#### 1. 配置系统修改 (app/main.py)
```python
# 添加插件目录配置项
_DEFAULTS = {
# ... 现有配置
'YTDL_PLUGINS_DIR': '',
}
```
#### 2. 下载引擎修改 (app/ytdl.py)
```python
# 在 _download 方法中添加插件支持
if (self.manager and
hasattr(self.manager, 'config') and
hasattr(self.manager.config, 'YTDL_PLUGINS_DIR') and
self.manager.config.YTDL_PLUGINS_DIR):
# 插件目录处理逻辑
```
#### 3. 信息提取修改 (app/ytdl.py)
```python
# 在 __extract_info 方法中添加插件支持
if (hasattr(self.config, 'YTDL_PLUGINS_DIR') and
self.config.YTDL_PLUGINS_DIR):
# 插件目录处理逻辑
```
## 📦 部署方案
### 方案 1: 开发环境部署
```bash
# 1. 创建插件包
mkdir -p your-plugin/yt_dlp_plugins/extractor
# 2. 安装插件
cd your-plugin
pip install -e .
# 3. 设置环境变量
export PYTHONPATH="/path/to/your-plugin"
# 4. 启动 Metube
pipenv run python3 app/main.py
```
### 方案 2: 生产环境部署
```bash
# 1. 构建插件包
python setup.py sdist bdist_wheel
# 2. 安装到系统
pip install dist/yt-dlp-yoursite-plugin-1.0.0.tar.gz
# 3. 配置 Metube
# 设置相应的环境变量或配置文件
```
### 方案 3: Docker 容器化部署
```dockerfile
# Dockerfile 扩展
FROM alexta69/metube
# 复制插件
COPY plugins/ /app/plugins/
# 设置环境变量
ENV PYTHONPATH="/app/plugins"
```
## 🛠️ 故障排除
### 常见问题和解决方案
#### 1. 插件未被识别
**症状**: `Unsupported URL``Falling back on generic information extractor`
**解决方法**:
```bash
# 检查插件安装
pip list | grep yoursite
# 检查环境变量
echo $PYTHONPATH
# 验证插件目录结构
ls -la your-plugin/yt_dlp_plugins/extractor/
# 测试直接安装
pipenv run python -c "import yt_dlp_plugins.extractor.yoursite"
```
#### 2. API 调用失败
**症状**: `Failed to download metadata` 或 HTTP 错误
**解决方法**:
```python
# 调试 API 调用
import requests
response = requests.post(api_url, data=data, headers=headers)
print(response.status_code, response.text)
# 检查请求头
# 检查参数格式
# 验证网站 API 变化
```
#### 3. 元数据提取错误
**症状**: 标题为空或时间戳解析失败
**解决方法**:
```python
# 调试数据结构
print(json.dumps(api_response, indent=2, ensure_ascii=False))
# 检查字段映射
# 验证数据类型转换
```
#### 4. Metube 集成问题
**症状**: Web 界面下载失败或进程错误
**解决方法**:
```bash
# 检查 Metube 日志
docker logs metube_container
# 验证插件目录映射
# 检查环境变量设置
# 测试进程间通信
```
### 调试技巧
#### 1. 详细日志输出
```bash
# 启用详细模式
yt-dlp --verbose "https://yoursite.com/video/123"
# 启用调试模式
yt-dlp --debug "https://yoursite.com/video/123"
```
#### 2. JSON 输出调试
```bash
# 仅提取元数据
yt-dlp --dump-json "https://yoursite.com/video/123"
# 检查插件加载
yt-dlp --verbose --list-extractors | grep yoursite
```
#### 3. Python 调试
```python
# 直接测试扩展器
from yt_dlp_plugins.extractor.yoursite import YourSiteIE
extractor = YourSiteIE()
result = extractor.extract("https://yoursite.com/video/123")
```
## 📋 开发检查清单
### 开发阶段
- [ ] 网站 API 调研完成
- [ ] URL 正则表达式测试通过
- [ ] 数据结构映射正确
- [ ] 错误处理完善
- [ ] 测试用例编写
### 集成阶段
- [ ] 插件目录结构正确
- [ ] 配置文件格式正确
- [ ] Metube 修改完成
- [ ] 环境变量设置正确
### 测试阶段
- [ ] 命令行测试通过
- [ ] Metube Web 界面测试通过
- [ ] 元数据提取正确
- [ ] 下载功能正常
- [ ] 错误处理验证
### 部署阶段
- [ ] 生产环境配置
- [ ] 性能测试
- [ ] 监控设置
- [ ] 文档更新
## 🔮 最佳实践总结
### 1. 开发原则
- **遵循官方规范**: 严格按照 yt-dlp 插件开发标准
- **完整测试覆盖**: 包含单元测试和集成测试
- **优雅错误处理**: 提供清晰的错误信息
- **性能优化**: 避免不必要的网络请求
### 2. 代码质量
- **清晰命名**: 使用描述性的变量和函数名
- **完整注释**: 解释复杂的业务逻辑
- **模块化设计**: 将功能拆分为可复用的方法
- **安全考虑**: 验证输入数据和 API 响应
### 3. 维护策略
- **版本控制**: 使用语义化版本号
- **变更记录**: 维护详细的 CHANGELOG
- **监控告警**: 设置 API 变化监控
- **用户反馈**: 建立问题反馈渠道
## 📚 参考资源
### 官方文档
- [yt-dlp Plugin Development](https://github.com/yt-dlp/yt-dlp/wiki/Plugin-Development)
- [yt-dlp Sample Plugins](https://github.com/yt-dlp/yt-dlp-sample-plugins)
- [yt-dlp Developer Instructions](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#developer-instructions)
### 技术资源
- [Python setuptools 文档](https://setuptools.pypa.io/)
- [正则表达式测试工具](https://regex101.com/)
- [JSON 格式化工具](https://jsonformatter.org/)
### 相关项目
- [Metube 项目](https://github.com/alexta69/metube)
- [yt-dlp 主项目](https://github.com/yt-dlp/yt-dlp)
---
通过本指南,您可以为任何网站开发 yt-dlp 扩展器并与 Metube 完美集成。记住始终遵循官方标准,进行充分测试,并保持代码的可维护性。