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

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

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

14 KiB
Raw Blame History

yt-dlp 扩展器开发完整指南

本文档基于 tingdao.org 扩展器的成功开发经验,提供了一套完整的 yt-dlp 扩展器开发和 Metube 集成方案。

📋 目录

  1. 项目概述
  2. 技术架构
  3. 开发流程
  4. 实现细节
  5. 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 网站结构分析

# 使用浏览器开发者工具分析
# 1. URL 模式识别
# 2. 网络请求监控
# 3. API 端点发现
# 4. 数据结构分析

1.2 API 接口调研

# 测试 API 调用
import requests

# 发现正确的 API 端点
api_url = "https://example.com/api/endpoint"
data = {'param': 'value'}
response = requests.post(api_url, data=data)

1.3 数据结构映射

// 分析 API 响应结构
{
  "status": 1,
  "list": {
    "mediaList": [...],
    "authorMsg": {...}
  }
}

阶段 2: 插件开发

2.1 项目结构创建

# 按照 yt-dlp 官方规范创建目录
mkdir -p your-plugin/yt_dlp_plugins/extractor

2.2 配置文件设置

setup.cfg:

[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:

[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 添加插件目录配置:

_DEFAULTS = {
    # ... 其他配置
    'YTDL_PLUGINS_DIR': '',
    # ...
}

3.2 修改下载引擎支持插件

app/ytdl.py 添加插件加载逻辑:

# 在 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 插件安装测试

# 安装插件包
cd your-plugin
pip install -e .

# 验证插件加载
export PYTHONPATH="/path/to/your-plugin"
yt-dlp --list-extractors | grep yoursite

4.2 功能测试

# 命令行测试
yt-dlp "https://yoursite.com/video/123" --dump-json

# 实际下载测试
yt-dlp "https://yoursite.com/video/123"

4.3 Metube 集成测试

# 启动 Metube 服务器
export PYTHONPATH="/path/to/your-plugin"
pipenv run python3 app/main.py

# 浏览器测试 Web 界面
# http://localhost:8081

📝 实现细节

通用扩展器模板

"""
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. 请求头配置

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. 参数处理

# POST 数据编码
data = f'param1={value1}&param2={value2}'.encode()

# GET 参数
params = {'param1': value1, 'param2': value2}

3. 错误处理

# 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. 多源支持

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. 音频专用处理

formats.append({
    'url': audio_url,
    'ext': 'mp3',
    'acodec': 'mp3',
    'vcodec': 'none',
    'abr': 128,
    'format_id': 'audio',
})

🔧 Metube 集成

服务器启动配置

环境变量方式

export PYTHONPATH="/path/to/your-plugin"
pipenv run python3 app/main.py

Docker 方式

# docker-compose.yml
services:
  metube:
    image: alexta69/metube
    environment:
      - YTDL_PLUGINS_DIR=/app/plugins
    volumes:
      - "./downloads:/downloads"
      - "./plugins:/app/plugins"

修改清单

1. 配置系统修改 (app/main.py)

# 添加插件目录配置项
_DEFAULTS = {
    # ... 现有配置
    'YTDL_PLUGINS_DIR': '',
}

2. 下载引擎修改 (app/ytdl.py)

# 在 _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)

# 在 __extract_info 方法中添加插件支持
if (hasattr(self.config, 'YTDL_PLUGINS_DIR') and
    self.config.YTDL_PLUGINS_DIR):
    # 插件目录处理逻辑

📦 部署方案

方案 1: 开发环境部署

# 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: 生产环境部署

# 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 扩展
FROM alexta69/metube

# 复制插件
COPY plugins/ /app/plugins/

# 设置环境变量
ENV PYTHONPATH="/app/plugins"

🛠️ 故障排除

常见问题和解决方案

1. 插件未被识别

症状: Unsupported URLFalling back on generic information extractor

解决方法:

# 检查插件安装
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 错误

解决方法:

# 调试 API 调用
import requests
response = requests.post(api_url, data=data, headers=headers)
print(response.status_code, response.text)

# 检查请求头
# 检查参数格式
# 验证网站 API 变化

3. 元数据提取错误

症状: 标题为空或时间戳解析失败

解决方法:

# 调试数据结构
print(json.dumps(api_response, indent=2, ensure_ascii=False))

# 检查字段映射
# 验证数据类型转换

4. Metube 集成问题

症状: Web 界面下载失败或进程错误

解决方法:

# 检查 Metube 日志
docker logs metube_container

# 验证插件目录映射
# 检查环境变量设置
# 测试进程间通信

调试技巧

1. 详细日志输出

# 启用详细模式
yt-dlp --verbose "https://yoursite.com/video/123"

# 启用调试模式
yt-dlp --debug "https://yoursite.com/video/123"

2. JSON 输出调试

# 仅提取元数据
yt-dlp --dump-json "https://yoursite.com/video/123"

# 检查插件加载
yt-dlp --verbose --list-extractors | grep yoursite

3. 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 扩展器并与 Metube 完美集成。记住始终遵循官方标准,进行充分测试,并保持代码的可维护性。