项目定位
m3u8下载器 是一个桌面端视频下载工作台。它不是单纯命令行脚本,而是把 M3U8 直链下载、批量任务、请求头配置、片源搜索和 FFmpeg 合并整合到一个 PySide6 GUI 里。
项目主入口是 main.py,核心下载逻辑在 m3u8_downloader.py。搜索源按文件拆开,比如 search.py、search_555.py、search_mqtv.py、search_ncat.py、search_mofa.py。
整体链路
用户输入 m3u8 URL / 通过片源搜索拿到 m3u8
|
v
M3U8Parser.parse_m3u8
|
+--> 识别 master playlist
+--> 递归进入子播放列表
+--> 解析 EXTINF 片段
+--> 解析 EXT-X-KEY AES-128 信息
|
v
M3U8Downloader._download_segments
|
+--> ThreadPoolExecutor 并发下载 TS
+--> 失败指数退避重试
+--> AESDecryptor 解密片段
|
v
FFmpegMerger.merge_segments
|
+--> ffmpeg concat stream copy
+--> 找不到 ffmpeg 时使用备用 TS 合并
这个链路做得比较完整:能处理 master playlist,能处理相对路径 TS,能处理 #EXT-X-KEY,还能把进度通过 callback 回传给 GUI。
GUI 工作台
桌面层用 PySide6 写,MainWindow 里维护:
download_tasks:当前任务组件列表。custom_headers:用户自定义请求头。active_downloads:当前活跃下载数。download_queue:等待队列。
界面支持单链接和批量模式。批量模式下一行一个 M3U8 链接,创建任务后根据并发设置自动启动。每个任务是独立的 DownloadTaskWidget,下载完成后发信号给主窗口,主窗口再处理队列里的下一个任务。
这个设计比“点一下就阻塞主界面”的脚本舒服很多:GUI 只负责组织任务和展示状态,真正下载在线程/任务里跑。
M3U8 解析
M3U8Parser 做了几件关键事。
第一是请求头可配置。默认 UA、Accept、Accept-Language 足够处理很多普通 CDN,但用户也可以给特殊站点补 Referer、Origin 或其它 header。
第二是 base URL 处理。M3U8 里经常出现相对路径:
#EXTINF:6.0,
segment00001.ts
解析器会根据原始 M3U8 地址推导基础路径,再用 urljoin 得到完整 TS 地址。
第三是 master playlist 处理。如果发现 #EXT-X-STREAM-INF,说明当前文件不是最终分片列表,而是清晰度索引。项目会取子播放列表继续递归解析。
第四是加密信息。遇到:
#EXT-X-KEY:METHOD=AES-128,URI="key.key",IV=0x...
解析器会记录 method、uri 和 iv,后面下载每个分片时交给 AESDecryptor。
AES-128 解密
AESDecryptor 用 cryptography 做 CBC 解密。它会缓存 key,避免每个分片都重新请求 key 地址。
解密流程大概是:
- 请求
EXT-X-KEY里的 key URI。 - 如果 M3U8 没有显式 IV,就用分片 index 生成 16 字节 IV。
AES-CBC解密 TS bytes。- 去掉 PKCS7 padding。
- 写入临时 TS 文件。
这块的关键点不是算法本身,而是请求头复用。很多 key 地址和 TS 地址一样需要 Referer/UA,所以解密器会使用下载器当前 session 的 headers。
多线程下载
下载器使用 ThreadPoolExecutor 并发下载分片,默认线程数在配置里是 16,GUI 里可以调。
单个分片下载失败会按指数退避重试:
attempt 1 -> 立即重试
attempt 2 -> 等 2 秒
attempt 3 -> 等 4 秒
...
所有分片结果按 index 放回列表,最后只拿成功的文件合并。如果成功数太少,下载器会给出警告,而不是假装任务正常完成。
进度由 ProgressCallback 管理,它维护 completed_segments、failed_segments、total_segments 和起始时间。GUI 收到的数据里能拿到百分比、速度和 ETA。
FFmpeg 合并
项目专门抽了 FFmpegMerger,启动时按顺序找:
- 项目内置
ffmpeg/ffmpeg.exe或ffmpeg/ffmpeg。 - 系统 PATH。
- Windows、macOS、Linux 常见安装路径。
找得到就用 concat demuxer:
ffmpeg -f concat -safe 0 -i concat.txt -c copy -y output.mp4
这个方案能正确处理 TS 时间戳和 PAT/PMT 信息,音画同步会比手动二进制拼接稳很多。
找不到 ffmpeg 时,项目会走备用方案:第一个 TS 完整写入,后续 TS 跳过前 10 个 188 字节 TS 包,再写入输出文件。它不如 ffmpeg 完美,但比直接 copy /b 或简单拼接更稳。
站点搜索源
项目不只接受用户手动粘贴 M3U8,还内置了多个搜索源:
AiGua:搜索后解析详情页和播放接口,收集剧集 M3U8。VideoSearcher555:解析播放页里的 player 数据,处理加密播放地址。MQTVSearcher:搜索接口有 token 和加密响应,项目里做了 encode/decode。NCatSearcher:从搜索结果和播放页里提取 M3U8/MP4。MofaSearcher:搜索结果本身就能直接给出播放路由。
这些搜索源都有一个共同模式:
搜索关键词
-> 搜索页 / 搜索 API
-> 详情页
-> 剧集列表
-> 播放页
-> M3U8 URL
不同站点的差异主要在请求头、参数、token、加密响应和播放页字段上。GUI 层不用理解这些细节,只要拿到最终 M3U8 列表即可。
配置和工具函数
config.py 里放默认线程数、重试次数、超时、窗口大小、默认 UA、临时目录后缀和状态文案。
utils.py 里是比较实用的工具:
- URL 和 M3U8 URL 校验。
- 文件名清理。
- 自动补
.mp4。 - 文件大小和时间格式化。
- 临时目录创建/清理。
- 避免文件名冲突。
- 输出路径权限检查。
- 从 URL 推断任务标题。
这些小函数让 GUI 和下载器都能保持干净,不用到处散落路径处理逻辑。
我觉得这个项目有意思的点
这个项目的价值不只在“能下载 M3U8”,而是在把下载相关的边角问题都收进一个桌面工具里:
- 用户可以手动补请求头,适配 Referer/Origin 检查。
- 搜索源可以直接批量生成 M3U8 任务。
- 下载器知道 master playlist、AES-128、分片重试和进度统计。
- 合并优先用 ffmpeg,保证输出 MP4 的稳定性。
- GUI 有队列和并发控制,不会一下把所有任务同时打满。
后续如果继续打磨,我会优先把下载核心、搜索源和 GUI 进一步解耦:搜索源统一成接口,下载任务做持久化,日志从 print 换成结构化 logging,再给每个站点搜索源补无网络 fixture 测试。这样它就会从“能用的桌面工具”变成“更容易持续维护的下载工作台”。