添加完整的yt-dlp扩展器开发指南文档
## 文档内容 1. yt-dlp扩展器开发完整指南.md - 详细的端到端开发指南 2. yt-dlp扩展器快速参考.md - 精简的快速参考卡片 ## 涵盖内容 - 完整的开发流程和最佳实践 - 通用扩展器代码模板 - Metube集成的详细步骤 - 故障排除和调试技巧 - 部署方案和检查清单 为以后扩展其他网站提供标准化指南。
This commit is contained in:
parent
c580429731
commit
8f17018fe3
2 changed files with 805 additions and 0 deletions
636
yt-dlp扩展器开发完整指南.md
Normal file
636
yt-dlp扩展器开发完整指南.md
Normal 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}¶m2={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 完美集成。记住始终遵循官方标准,进行充分测试,并保持代码的可维护性。
|
||||
169
yt-dlp扩展器快速参考.md
Normal file
169
yt-dlp扩展器快速参考.md
Normal 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 扩展器。
|
||||
Loading…
Reference in a new issue