添加完整的yt-dlp扩展器开发指南文档

## 文档内容
1. yt-dlp扩展器开发完整指南.md - 详细的端到端开发指南
2. yt-dlp扩展器快速参考.md - 精简的快速参考卡片

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

为以后扩展其他网站提供标准化指南。
This commit is contained in:
柯孟凯 2025-09-23 14:38:41 +08:00
parent c580429731
commit 8f17018fe3
2 changed files with 805 additions and 0 deletions

View file

@ -0,0 +1,636 @@
# 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 完美集成。记住始终遵循官方标准,进行充分测试,并保持代码的可维护性。

View file

@ -0,0 +1,169 @@
# yt-dlp 扩展器快速参考
基于 tingdao.org 成功案例的快速开发指南
## 🚀 快速开始
### 1. 创建插件结构
```bash
mkdir -p your-plugin/yt_dlp_plugins/extractor
cd your-plugin
```
### 2. 配置文件
**setup.cfg**:
```ini
[metadata]
name = yt-dlp-yoursite-plugin
version = 1.0.0
[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"
requires-python = ">=3.7"
```
### 3. 扩展器模板
```python
# yt_dlp_plugins/extractor/yoursite.py
from yt_dlp.extractor.common import InfoExtractor
from yt_dlp.utils import ExtractorError
class YourSiteIE(InfoExtractor):
IE_NAME = 'yoursite'
_VALID_URL = r'https?://(?:www\.)?yoursite\.com/video/(?P<id>\d+)'
def _real_extract(self, url):
video_id = self._match_id(url)
# API 调用
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'}
)
return {
'id': video_id,
'title': data.get('title'),
'url': data.get('video_url'),
}
```
## 📦 Metube 集成
### 修改 app/main.py
```python
_DEFAULTS = {
# ... 现有配置
'YTDL_PLUGINS_DIR': '',
}
```
### 修改 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
```
## 🧪 测试和部署
### 安装测试
```bash
# 安装插件
pip install -e .
# 测试加载
export PYTHONPATH="/path/to/your-plugin"
yt-dlp --list-extractors | grep yoursite
```
### 功能测试
```bash
# 提取信息
yt-dlp --dump-json "https://yoursite.com/video/123"
# 实际下载
yt-dlp "https://yoursite.com/video/123"
```
### Metube 测试
```bash
# 启动服务器
export PYTHONPATH="/path/to/your-plugin"
pipenv run python3 app/main.py
# 访问 http://localhost:8081 测试
```
## 🔧 常见问题
### 插件未识别
```bash
# 检查环境变量
echo $PYTHONPATH
# 验证插件导入
python -c "import yt_dlp_plugins.extractor.yoursite"
```
### API 调用失败
```python
# 调试 API
import requests
response = requests.post(api_url, data=data, headers=headers)
print(response.status_code, response.text)
```
### Metube 集成问题
```bash
# 检查日志
docker logs metube_container
# 验证插件目录
ls -la /path/to/plugins/
```
## 📋 检查清单
- [ ] URL 正则表达式正确
- [ ] API 调用成功
- [ ] 元数据提取正确
- [ ] 错误处理完善
- [ ] 插件目录结构正确
- [ ] Metube 修改完成
- [ ] 环境变量设置
- [ ] 命令行测试通过
- [ ] Web 界面测试通过
## 🎯 关键成功因素
1. **严格遵循 yt-dlp 官方规范**
2. **正确设置 PYTHONPATH 环境变量**
3. **完整的 API 调研和测试**
4. **优雅的错误处理机制**
5. **充分的集成测试验证**
---
基于 tingdao.org 案例的经验总结,遵循此指南可快速开发任何网站的 yt-dlp 扩展器。