M3U8 下载器 - 开发文档
一、项目概述
1.1 项目名称
M3U8 Downloader(暂定名)
1.2 项目简介
基于 N_m3u8DL-RE 开源项目的桌面便携版 m3u8 视频下载工具,提供图形化界面,让用户无需命令行即可轻松下载 HLS 流媒体视频。
1.3 技术栈
| 类别 | 技术选型 | 说明 |
|---|---|---|
| 编程语言 | Python 3.10+ | 开发效率高,生态丰富 |
| GUI 框架 | PyQt5 | 成熟的跨平台 GUI 框架,界面美观 |
| 打包工具 | PyInstaller | 打包为独立可执行的便携版 exe |
| 核心依赖 | N_m3u8DL-RE | 开源 m3u8 下载引擎 |
| 辅助工具 | FFmpeg | 视频处理、合并、转码 |
1.4 项目结构
m3u8down/
├── N_m3u8DL-RE/ # N_m3u8DL-RE 核心文件(已存在)
│ ├── N_m3u8DL-RE.exe # 主程序
│ ├── ffmpeg.exe # FFmpeg
│ ├── ffplay.exe # FFplay
│ ├── ffprobe.exe # FFprobe
│ └── *.dll # FFmpeg 依赖库
├── src/ # 源代码目录(待创建)
│ ├── main.py # 程序入口
│ ├── ui/ # 界面相关
│ │ ├── main_window.py # 主窗口
│ │ ├── task_item.py # 任务列表项组件
│ │ ├── dialogs.py # 对话框(设置、新建任务等)
│ │ └── resources.qrc # 资源文件
│ ├── core/ # 核心逻辑
│ │ ├── downloader.py # 下载引擎(调用 N_m3u8DL-RE)
│ │ ├── task_manager.py # 任务管理器
│ │ ├── config.py # 配置管理
│ │ ├── database.py # 本地数据库操作
│ │ ├── file_manager.py # 文件管理(移动、清理)
│ │ └── utils.py # 工具函数
│ └── assets/ # 静态资源
│ ├── icons/ # 图标
│ └── styles/ # 样式表
├── tmp/ # 临时文件目录(运行时自动创建)
├── data/ # 数据目录
│ ├── downloads.db # SQLite 数据库(已完成任务)
│ └── tasks.json # 运行中任务状态
├── config/ # 配置目录
│ └── config.json # 用户配置
├── build/ # 构建输出
├── dist/ # 打包输出
├── requirements.txt # Python 依赖
├── build.spec # PyInstaller 配置
└── README.md # 项目说明二、功能设计
2.1 核心功能
2.1.0 首次启动 - 保存路径设置
- 启动软件后,首先弹出路径选择对话框
- 用户通过下拉菜单选择可用盘符(自动检测系统所有可用盘符)
- 程序自动在所选盘符下创建
hc文件夹(如D:\hc\) - 如果
hc文件夹已存在则直接使用 - 保存路径配置后,后续启动不再弹出(可在设置中修改)
- 临时文件统一存储在软件根目录的
tmp文件夹
首次启动对话框示例:
┌──────────────────────────────────────────────┐
│ 欢迎使用 M3U8 下载器 [×] │
├──────────────────────────────────────────────┤
│ │
│ 请选择下载文件的保存位置: │
│ │
│ 保存盘符: [D:\ ▼] │
│ │
│ 保存路径: D:\hc\ │
│ 临时目录: <软件根目录>\tmp\ │
│ │
│ ☑ 以后不再提示(可在设置中修改) │
│ │
│ [退出] [确定] │
└──────────────────────────────────────────────┘2.1.1 新建下载任务
- 输入 m3u8 URL 或本地 m3u8 文件路径
任务去重机制:通过 URL 哈希值判断是否已存在相同任务
- 若已存在,弹出提示:"该链接已存在于下载列表中"
- 在界面右下角显示轻量提醒通知(3 秒后自动消失)
- 解析 URL,显示可用的视频/音频/字幕轨道
- 选择画质、音轨、字幕
- 设置保存路径、文件名
- 高级选项(线程数、重试次数、代理等)
2.1.2 任务管理
- 任务列表展示(类似迅雷/IDM 等下载工具)
- 任务状态:等待中、下载中、已暂停、已完成、失败
- 支持操作:开始、暂停、继续、删除、重试
- 显示进度条、下载速度、已下载大小、剩余时间
- 并发控制:系统默认最大 3 个任务同时下载,多余任务进入等待队列
2.1.3 文件保存规则
下载完成后,文件按照以下规则保存:
<盘符>\hc\<下载链接的哈希值>\<下载完成的时秒分>.mp4示例:
D:\hc\a1b2c3d4\143025.mp4- 链接哈希值:对下载 URL 进行哈希计算(如 MD5 前 8 位),确保唯一性
- 时秒分命名:使用下载完成的时间(时+秒+分,6 位数字),避免重名
- 下载完成后,将文件信息写入本地数据库
- 下载完成后,自动清理临时目录中的对应文件夹和文件
2.1.4 任务状态持久化
- 正在下载的任务:实时写入运行目录的
tasks.json文件 - 程序意外关闭后,重启可恢复未完成的任务状态
- 已完成的任务信息存储在本地数据库中
2.1.4.1 异常恢复机制
软件启动时执行以下操作:
- 清理残留进程:检测并终止所有 N_m3u8DL-RE 相关进程
- 清空临时目录:删除
tmp文件夹内所有内容(避免残留损坏文件) - 读取任务状态:从
tasks.json加载未完成的任务列表 - 自动恢复下载:将加载的任务重新加入下载队列,按并发限制依次启动
- 恢复的任务保持原有进度信息(实际进度需重新下载)
2.1.5 本地数据库
- 使用 SQLite 轻量级数据库存储已完成任务记录
- 数据库文件位置:
<软件根目录>\data\downloads.db 存储字段:
id: 任务唯一标识url: 下载链接url_hash: 链接哈希值file_path: 最终保存路径file_name: 文件名(时秒分.mp4)file_size: 文件大小download_time: 下载完成时间status: 状态(已完成)
2.1.6 下载控制
- 限速设置
- 下载完成后操作(删除临时文件、混流等)
2.1.4 设置中心
- 通用设置:下载目录、临时目录、线程数
- 网络设置:代理、请求头、超时时间
- 下载设置:重试次数、并发数、限速
- 高级设置:解密 Key、自定义参数
2.2 界面布局
┌─────────────────────────────────────────────────────────────┐
│ 菜单栏 [文件] [任务] [工具] [帮助] │
├─────────────────────────────────────────────────────────────┤
│ 工具栏 [新建] [开始] [暂停] [删除] [设置] │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 任务列表 │ │
│ │ ┌───┬──────────┬────────┬────────┬──────┬────────┐ │ │
│ │ │ │ 文件名 │ 大小 │ 进度 │ 速度 │ 状态 │ │ │
│ │ ├───┼──────────┼────────┼────────┼──────┼────────┤ │ │
│ │ │▓▓ │ video.mp4 │ 100MB │ ████░░ │ 5MB/s│ 下载中 │ │ │
│ │ │▓▓ │ audio.mp3 │ 50MB │ ██████ │ 3MB/s│ 下载中 │ │ │
│ │ │ │ doc.pdf │ 10MB │ ░░░░░░ │ 0 │ 等待中 │ │ │
│ │ └───┴──────────┴────────┴────────┴──────┴────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌───────────┐ │
│ │ 提醒通知框 │ │
│ │ 链接已存在 │ │
│ └───────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 状态栏 任务数: 3 | 下载中: 2 | 总速度: 8MB/s | 磁盘剩余: XX │
└─────────────────────────────────────────────────────────────┘2.3 新建任务对话框
┌──────────────────────────────────────────────┐
│ 新建下载任务 [×] │
├──────────────────────────────────────────────┤
│ URL/文件路径: │
│ ┌──────────────────────────────────────┐ │
│ │ https://example.com/video.m3u8 │ │
│ └──────────────────────────────────────┘ │
│ │
│ 保存目录: [C:\Downloads\] [浏览...] │
│ 文件名: [video] │
│ │
│ ─── 轨道选择 ─── │
│ 视频: [1080p ▼] 音频: [中文 ▼] 字幕: [无 ▼]│
│ │
│ ─── 高级选项 ─── │
│ ☑ 下载完成后合并 │
│ 线程数: [8] 重试次数: [3] │
│ 代理: [无 ▼] │
│ │
│ [取消] [确定] │
└──────────────────────────────────────────────┘三、技术实现
3.1 N_m3u8DL-RE 调用方式
通过子进程调用 N_m3u8DL-RE.exe,解析其命令行输出获取进度信息。
核心命令构建示例:
# 基础命令
cmd = [
"N_m3u8DL-RE.exe",
m3u8_url,
"--save-dir", save_dir,
"--save-name", file_name,
"--thread-count", str(thread_count),
"--download-retry-count", str(retry_count),
]
# 可选参数
if proxy:
cmd.extend(["--custom-proxy", proxy])
if headers:
for h in headers:
cmd.extend(["-H", h])
if select_video:
cmd.extend(["-sv", select_video])进度解析:
N_m3u8DL-RE 输出格式示例:
Parsing URL: https://example.com/video.m3u8
Found 3 video tracks, 2 audio tracks, 1 subtitle track
Downloading: 45.2% (125/277) Speed: 5.2MB/s ETA: 00:02:30通过正则表达式解析进度、速度、ETA 等信息。
3.2 任务管理器设计
class TaskStatus(Enum):
WAITING = "waiting"
DOWNLOADING = "downloading"
PAUSED = "paused"
COMPLETED = "completed"
FAILED = "failed"
class DownloadTask:
id: str # 任务唯一标识
url: str # 下载链接
save_dir: str # 保存目录
save_name: str # 文件名
status: TaskStatus # 状态
progress: float # 进度 0-100
speed: str # 速度
eta: str # 剩余时间
total_size: str # 总大小
downloaded_size: str # 已下载大小
process: subprocess.Popen # 子进程对象
options: dict # 下载选项3.3 配置管理
使用 JSON 文件存储用户配置:
{
"first_run": false,
"save_dir": "D:\\hc",
"tmp_dir": "<软件根目录>\\tmp",
"thread_count": 8,
"retry_count": 3,
"timeout": 100,
"proxy": "",
"headers": [],
"max_concurrent": 3,
"speed_limit": "",
"auto_merge": true,
"delete_temp": true,
"language": "zh-CN"
}配置说明:
first_run: 是否首次运行(true=弹出路径选择,false=直接进入主界面)save_dir: 下载保存目录(自动设置为盘符:\hc)tmp_dir: 临时文件目录(固定为<软件根目录>\tmp)max_concurrent: 最大并发下载数(默认 3)
3.4 本地数据库设计
使用 SQLite 存储已完成任务记录:
CREATE TABLE IF NOT EXISTS downloads (
id TEXT PRIMARY KEY, -- 任务唯一标识(UUID)
url TEXT NOT NULL, -- 下载链接
url_hash TEXT NOT NULL, -- URL 的 MD5 哈希值(前 8 位)
file_path TEXT NOT NULL, -- 最终保存路径
file_name TEXT NOT NULL, -- 文件名(时秒分.mp4)
file_size INTEGER, -- 文件大小(字节)
download_time TEXT NOT NULL, -- 下载完成时间(ISO 格式)
status TEXT DEFAULT 'completed' -- 状态
);
CREATE INDEX IF NOT EXISTS idx_url_hash ON downloads(url_hash);
CREATE INDEX IF NOT EXISTS idx_download_time ON downloads(download_time);3.5 运行中任务状态(tasks.json)
{
"tasks": [
{
"id": "uuid-xxx",
"url": "https://example.com/video.m3u8",
"url_hash": "a1b2c3d4",
"save_name": "video",
"status": "downloading",
"progress": 45.2,
"speed": "5.2MB/s",
"eta": "00:02:30",
"start_time": "2026-06-01T14:30:00",
"tmp_dir": "<软件根目录>\\tmp\\a1b2c3d4"
}
]
}3.6 文件保存流程
1. N_m3u8DL-RE 下载完成 → 临时文件在 <tmp>\<url_hash>\ 目录
2. 计算下载完成时间 → 格式化为 HHmmss(时秒分)
3. 创建目标目录 → <save_dir>\<url_hash>\
4. 移动文件 → <save_dir>\<url_hash>\<HHmmss>.mp4
5. 写入数据库 → 记录任务信息
6. 清理临时目录 → 删除 <tmp>\<url_hash>\ 及其内容
7. 更新 tasks.json → 移除已完成任务3.7 程序启动流程
1. 程序启动
↓
2. 加载配置文件 (config.json)
↓
3. 判断是否首次运行 (first_run == true)
├─ 是 → 弹出 FirstRunDialog → 用户选择盘符 → 创建 hc 文件夹 → 保存配置
└─ 否 → 继续
↓
4. 清理残留 N_m3u8DL-RE 进程
↓
5. 清空 tmp 文件夹
↓
6. 初始化数据库 (downloads.db)
↓
7. 读取 tasks.json 加载未完成的任务
↓
8. 将加载的任务加入下载队列
↓
9. 启动主窗口,显示任务列表
↓
10. 按并发限制(默认 3)依次启动下载任务3.8 异常恢复流程
1. 检测到 tasks.json 存在未完成的任务
↓
2. 遍历任务列表,过滤状态为 downloading/paused/waiting 的任务
↓
3. 重置任务状态为 waiting
↓
4. 将任务加入 TaskManager 的等待队列
↓
5. TaskManager.check_queue() 自动调度,按并发限制启动任务3.9 关键类设计
MainWindow (主窗口)
├── TaskListWidget (任务列表)
│ └── TaskItemWidget (单个任务项)
├── Toolbar (工具栏)
├── StatusBar (状态栏)
├── NotificationWidget (右下角提醒通知)
└── MenuBar (菜单栏)
Dialogs
├── FirstRunDialog (首次运行路径选择)
├── NewTaskDialog (新建任务)
├── SettingsDialog (设置)
├── TaskDetailDialog (任务详情)
└── SelectTrackDialog (轨道选择)
Core
├── DownloadEngine (下载引擎)
│ ├── start_task()
│ ├── pause_task()
│ ├── resume_task()
│ ├── cancel_task()
│ └── parse_output()
├── TaskManager (任务管理器)
│ ├── add_task()
│ ├── remove_task()
│ ├── get_tasks()
│ ├── get_active_count()
│ ├── check_queue() # 检查等待队列,自动启动新任务
│ ├── save_running_tasks() # 保存运行中任务到 tasks.json
│ └── is_duplicate_url() # 检查 URL 是否已存在
├── ConfigManager (配置管理)
│ ├── load_config()
│ ├── save_config()
│ └── get/set 各项配置
├── DatabaseManager (数据库管理)
│ ├── init_database() # 初始化数据库和表
│ ├── add_record() # 添加已完成任务记录
│ ├── query_by_hash() # 按哈希值查询
│ └── get_all_records() # 获取所有记录
├── FileManager (文件管理)
│ ├── generate_url_hash() # 生成 URL 哈希值
│ ├── generate_filename() # 生成时秒分文件名
│ ├── move_to_final_path() # 移动文件到最终保存路径
│ ├── cleanup_temp() # 清理临时目录
│ └── ensure_dirs() # 确保所需目录存在
└── ProcessManager (进程管理)
├── kill_n_m3u8dl_processes() # 清理所有 N_m3u8DL-RE 进程
└── is_process_running() # 检查进程是否运行四、开发计划
阶段一:基础框架搭建
- [ ] 创建项目结构
- [ ] 搭建 PyQt5 主窗口框架
- [ ] 实现基础菜单栏、工具栏、状态栏
- [ ] 实现配置管理模块
阶段二:核心功能开发
- [ ] 实现 N_m3u8DL-RE 调用封装
- [ ] 实现进度解析
- [ ] 实现任务管理器
- [ ] 实现新建任务对话框
阶段三:界面完善
- [ ] 实现任务列表组件
- [ ] 实现任务操作(开始/暂停/删除)
- [ ] 实现设置对话框
- [ ] 添加样式美化
阶段四:高级功能
- [ ] 轨道选择功能
- [ ] 限速功能
- [ ] 代理设置
- [ ] 批量下载
阶段五:测试与打包
- [ ] 功能测试
- [ ] 边界情况处理
- [ ] PyInstaller 打包
- [ ] 便携版测试
五、注意事项
5.1 便携版要求
- 所有文件相对于程序目录
- 配置存储在程序目录下的 config 文件夹
- 无需写入注册表或系统目录
- 可直接拷贝整个文件夹到其他电脑使用
5.2 N_m3u8DL-RE 集成
- N_m3u8DL-RE.exe 放在程序目录下的
N_m3u8DL-RE文件夹 - 通过相对路径调用
- 打包时需确保 N_m3u8DL-RE 相关文件被包含
5.3 跨平台考虑
- 当前仅支持 Windows(因 N_m3u8DL-RE 为 Windows 版本)
- 路径处理使用
os.path或pathlib - 命令行参数注意 Windows 转义
5.4 错误处理
- 网络异常处理
- N_m3u8DL-RE 进程异常捕获
- 磁盘空间不足检测
- 文件权限问题处理
六、依赖清单
Python 依赖
PyQt5>=5.15.0
requests>=2.28.0外部依赖(已提供)
- N_m3u8DL-RE.exe
- ffmpeg.exe / ffplay.exe / ffprobe.exe
- 相关 DLL 文件
七、参考资源
- N_m3u8DL-RE GitHub: https://github.com/nilaoda/N_m3u8DL-RE
- PyQt5 文档: https://www.riverbankcomputing.com/static/Docs/PyQt5/
- PyInstaller 文档: https://pyinstaller.org/
评论