简介这是一款面向流媒体视频下载需求的 m3u8 在线下载与提取工具适合需要将 HLS 流媒体保存为本地视频的普通用户、视频运营人员及前端开发者。工具围绕 M3U8 清单解析、TS 分片多线程下载和自动合并设计同时提供网页端交互界面部署后可通过浏览器输入链接完成视频抓取。压缩包共 47 个文件大小 1.68MB涵盖 15 个 JavaScript 脚本含解析、下载、播放与解密模块、7 个 HTML 页面、4 个 CSS 样式、2 个文本说明、字体图标、多张界面预览图以及 PHP、配置与安全验证文件目录结构接近一个完整可运行的网站项目。目前已有 5939 人浏览学习。借助此包可快速搭建 m3u8 下载服务既能用于日常视频离线缓存也可作为学习 m3u8 协议、前端播放器集成和下载流程设计的参考案例。1. 为什么需要 m3u8 下载工具你看到的视频其实是一堆 ts 碎片A同学想离线缓存一门录播课右键另存为灰掉录屏又卡又糊最后在开发者工具的 Network 面板里翻出几十条index.m3u8和几百个.ts请求才明白自己看的不是一个完整的视频文件而是一份播放清单加一堆分片。这就是 HLS 流媒体协议的工作方式浏览器边下边播绝不把分片拼成一个文件。m3u8 下载工具解决的就是这个诉求——把索引、分片、密钥按顺序拉回本地再合并、转码成一个能随意拷贝的 mp4。这篇笔记从协议原理讲到具体操作再列我在真实下载里踩过的坑新手能照步骤跑通熟手能直接看参数和边界条件。2. 先看懂 m3e8 再动手索引结构、加密分支与工具选型逻辑2.1 m3u8 到底记录了什么从播放列表到分片清单m3u8 本质上是一个 UTF-8 编码的文本文件里面按顺序写着视频分片的地址和播放时长。打开任何一个典型文件内容都很直白#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:10 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:10.0, https://example.com/segment/0001.ts #EXTINF:10.0, https://example.com/segment/0002.ts #EXTINF:8.5, https://example.com/segment/0003.ts #EXT-X-ENDLIST#EXTINF后面跟的是分片时长单位是秒下一行就是这个分片的下载地址。整份文件就是一张“播放清单”。下载工具读取这份清单后会依次请求每一个分片按顺序把二进制的 ts 数据拼接起来。这就像手里拿着一张零件清单把每个零件依次拧在一起最终得到一台整机。还要注意#EXT-X-ENDLIST这一行。有它表示这是点播VOD流分片数量固定下载可以收敛没有它说明是直播流分片会持续追加工具需要额外做“跟随拉取”的逻辑。在线下载工具界面里如果让你选“点播 / 直播”多数情况下选点播就能正常结束任务。2.2 加密与非加密流EXT-X-KEY 是你绕不开的分支点很多 m3u8 文件里会多出这样几行#EXT-X-KEY:METHODAES-128,URIhttps://example.com/key/enc.key,IV0x1234567890abcdef1234567890abcdef看到EXT-X-KEY就说明分片被加密了。浏览器播放时会在内存里解密所以你能正常看但直接下载下来的 ts 文件是无法播放的乱码。要合并出一个能用的 mp4下载工具必须先请求这个URI指向的密钥文件再配合IV初始向量对每个分片做 AES-128 解密然后才能进入合并环节。METHOD还有可能写成SAMPLE-AES常见于部分 DRM 保护的流处理逻辑比AES-128更复杂。遇到SAMPLE-AES在线工具通常不支持完整解密因为涉及版权保护和更底层的解码器操作不要抱太大期望。绝大多数录播、课程视频走的是AES-128这也是工具能处理的最主流分支。2.3 在线工具 vs ffmpeg 命令行我为什么留下这个方案有人会问直接用 ffmpeg 不就行了一条命令ffmpeg -i https://example.com/index.m3u8 -c copy out.mp4确实能跑通但现实往往没那么简单。很多场景下m3u8 地址需要带 Referer、User-Agent、Cookie 才能访问ffmpeg 虽然能用-headers传参遇到重定向、鉴权中间页、自定义请求头嵌套时命令行会变得冗长且极易写错排错成本不低。我保留在线工具的原因是它把“下载分片 解密 合并 转码”封装成了可视化流程密钥注入、请求头配置都有独立字段失败了能直接看到在哪一步断掉。尤其是手上只有浏览器、不方便装命令行环境的时候在线工具是成本最低的路径。当然ffmpeg 在批量处理和可控性上更强我在后面第 6 章会讲两者如何配合。3. 在线下载 m3u8 视频的完整操作从开发者工具取地址到生成 MP43.1 第一步在 Network 面板里拿到真实 m3u8 地址打开视频播放页面按 F12 进入开发者工具切到 Network 面板刷新页面让视频重新加载然后在过滤框里输入m3u8。正常情况下能看到一条或几条名称类似index.m3u8、playlist.m3u8的记录点击它右侧 Preview 标签页会直接展示分片列表。拿地址时有个细节部分站点会把 m3u8 地址包在blob:里直接blob:https://...这种是无法下载的。这时候要看发起的请求找到真正返回 m3u8 文本的那个 HTTP 请求复制它的完整 URL。常见做法是右键这条请求选择 Copy → Copy Link Address得到的是带签名参数的完整地址这类地址通常带?tokenxxxexpiresxxx后缀复制时一个字符都不能少少一个参数就可能 403。3.2 第二步把 URL、请求头和密钥填进下载表单拿到地址后回到在线工具页面填入三样东西m3u8 地址、请求头信息、输出格式。请求头是最容易漏掉的一步。工具界面上一般有一个“自定义请求头”或“Headers”输入区格式类似 JSON{ Referer: https://example.com/video/1, User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0, Cookie: sessionidabc123 }这三个字段里Referer决定了服务器是否认为你是从正常页面发起的请求User-Agent告诉服务器你用的是浏览器而不是脚本Cookie则用于维持登录态。很多视频站的鉴权逻辑是三者同时校验只要缺一个服务器就会返回 403 或者直接拒绝分片请求。填写时不要用自己的浏览器 UA 简写尽量从开发者工具里完整复制。因为有些服务器会检查浏览器指纹UA 不对也会被拒。3.3 第三步等待合并转码理解 TS→MP4 的转换逻辑点击开始后工具会先拉取 m3u8 索引然后逐个下载分片。下载完所有 ts 后工具开始做合并。这里要说明的是ts 文件本身是 MPEG-TS 封装里面可能包含 H.264 视频流和 AAC 音频流合并操作只是把这堆二进制块按顺序拼成一个大的 ts 文件然后通过转封装remux把视频轨和音频轨重新封装进 MP4 容器而不做重新编码。转封装速度远快于重新编码因为像素数据没有发生变化只是换了“包装盒”。所以工具显示“合并中”时CPU 占用率不会特别高硬盘读写才是主要瓶颈。如果工具在合并时做了转码选项耗时就会显著增加。日常下载录播课时选“直接合并 / 复制流”就能得到可播放的 mp4没必要做重复编码。3.4 关键参数说明表参数/选项作用建议取值m3u8 URL分片索引地址完整复制保留签名参数Referer告诉服务器来源页面与播放页 URL 保持一致User-Agent模拟浏览器身份从开发者工具完整复制Cookie维持登录态按站点实际情况填写输出格式合并后的封装格式一般选 mp4分片并发数同时下载多少个 ts510过高容易触发限流密钥处理自动/手动注入密钥默认自动失败时手动填分片并发数是一个容易忽略的选项。并发太高服务器会认为你在 CC 攻击轻则限速重则封 IP并发太低几百个分片要等很久。我一般设 8兼顾速度和安全性。4. 加密 m3u8 的 Decrypt 流程密钥、IV 与自定义请求头4.1 判定加密类型看到 METHODAES-128 后要做什么先打开 m3u8 文件搜索EXT-X-KEY字段。前面已经讲过METHODAES-128是加密流的主流形态。它意味着每个分片在 HTTP 传输过程中是被加密的密文但加密算法本身是标准 AES 块密码只要有密钥和 IV就能恢复原始内容。工具通常会在解析索引时自动发现EXT-X-KEY并尝试请求密钥文件。如果密钥地址是相对路径比如/key/enc.key工具会自动拼上 m3u8 所在域名。这一步手动做的时候容易漏密钥 URL 拼接错误是解密失败的头号原因。在线工具的日志区一般会打印“Found KEY: https://...”你确认一下这个地址能不能在浏览器里直接打开。如果打不开说明密钥接口同样需要鉴权请求头。4.2 注入密钥与 IV常见做法与参数写法有的工具界面上有“密钥 / Key”和“IV”两个手动输入框。当自动获取失败或者密钥地址会过期时手动注入是唯一出路。IV 常见格式是0x开头的十六进制串长度 32 位16 字节和 AES 块大小严格对应。// 一个典型的手动密钥配置对象用于填入在线工具的密钥输入区 const keyConfig { method: AES-128, keyUri: https://example.com/keys/enc.key, iv: 0x9f7e6d5c4b3a29180716253443526170 };这段配置的逻辑是method指定解密算法keyUri告诉工具去哪里拿密钥本体iv是与第一个分片对应的初始向量。iv取不到时很多工具默认会用分片的media sequence自动推导 IV这与标准规范一致——部分实现会忽略 IV 字段按序列号算 IV。填完这组参数后工具会先下载 key 文件通常 16 字节再用 AES-CBC 模式逐个解密 ts。命令行下对应的 ffmpeg 参数是-decryption_key但在线工具里只需要把keyUri和iv填对即可。4.3 加密流下载失败时优先检查请求头而不是密钥这是我实际下载中反复遇到的一个情况密钥地址明明能在浏览器里打开但工具解密失败日志却显示“decrypt error”。排查时第一反应是去改密钥值折腾半天没效果。后来我注意到工具请求密钥时用的是一组与分片不同的请求头而密钥接口恰好校验了Referer或Cookie导致密钥返回的不是 200 而是 403 页面。工具拿到 403 页面仍尝试按 16 字节解析自然解密失败。所以我的排查顺序是先看工具日志里密钥请求的 HTTP 状态码再看返回内容长度是不是 16 字节最后才是密钥内容本身。如果你看到“decrypt error”并且日志里没有明确写密钥获取失败先确认工具是否把自定义请求头同时应用到了密钥请求上。部分工具默认只把请求头用于分片请求密钥请求走的是空 Headers这是最容易翻车的地方。5. 避坑排查下载 m3u8 时我遇到的五个实际问题5.1 分片进度卡在 99%超时重试机制与线程数设置现象进度条走到 99% 后长时间不动日志显示大量分片重试最后任务失败。原因尾部几个分片体积较大下载耗时超过了工具默认的单分片超时时间或者服务器对高频请求做了临时限流。99% 这个位置特别容易让人误以为是合并阶段卡住实际上还在下载最后一个分片。解决把并发数从 8 降到 4同时把单分片超时时间从默认的 10 秒调大到 30 秒。工具设置里找到“超时”或“Timeout”选项改成 30 再重试。如果仍然失败复制失败分片的完整 URL粘贴到浏览器地址栏手动下载一次确认是不是这个分片本身已经损坏。5.2 合并后没有声音音频轨可能是另一条 m3u8现象mp4 正常生成画面流畅但全程无声。原因部分播放器站点把视频轨和音频轨拆成两条独立的 m3u8 流。视频 m3u8 里只有纯视频分片音频在另一个audio.m3u8中。只下载视频索引再合并产物自然没有音轨。解决回到 Network 面板过滤m3u8看是不是同时存在.ts请求指向不同域名或不同目录。如果看到两组分片一组是video/路径、一组是audio/路径说明有分离流。下载时需要同时填入视频 m3u8 和音频 m3u8工具会把两条流合并进同一 mp4。部分在线工具支持“附加音轨 URL”字段找不到就手动下载两条流再用ffmpeg -i video.mp4 -i audio.m4a -c copy final.mp4合并。5.3 报 403 ForbiddenReferer 和 User-Agent 不匹配现象分片请求返回 403但浏览器里同一个 URL 可以正常播放。原因服务器校验的是请求头组合而不是单个字段。有一种典型情况是Referer 填对了但 User-Agent 是空的或者是 Python 的默认 UA两者不匹配导致服务端拒绝。解决严格从浏览器复制 UA完整粘贴到工具里。检查 UA 里是否包含多个空格换行有些浏览器扩展会导致 UA 被改写。另一个容易漏的点是Accept头部分站点要求Accept: */*在线工具默认可能不带这字段手动补上即可。5.4 下载完成但时长只有几秒你抓到了播放列表的“分页”而非完整索引现象下载过程正常合并出的 mp4 播放时长只有 58 秒但原视频是一小时。原因有些站点做分页加载首屏只给前几条分片后续分片通过 JS 异步追加或请求一个带start_seq的扩展 m3u8。你复制的是首屏索引里面只有演示用的几条分片。解决在 Network 面板里把过滤条件改成m3u8刷新页面后不要立即暂停等直播流跑十几秒观察是否出现第二个 m3u8 请求且它的#EXTINF列表数量明显更多。取最后一条。另外手动打开复制到的 m3u8数一数#EXTINF出现次数如果只有不到 10 条基本可以判定是分页列表。5.5 输出是 .ts 而不是 .mp4搞清合并与转码的差异现象任务显示“合并完成”但输出文件后缀是.ts一些播放器不认。原因工具默认只用“复制流”模式做拼接不进行转封装。ts 封装和 mp4 封装的流格式不完全兼容工具判断目标格式时可能选择了“保持原样”。解决在输出格式下拉框里明确选择 mp4。如果工具没有 mp4 选项可以用一行 ffmpeg 补救ffmpeg -i input.ts -c copy -movflags faststart output.mp4-c copy不做重新编码只变换容器速度很快-movflags faststart会把 moov 元数据移到文件头部保证在网页播放器里拖进度条不卡顿。没有这一步有些播放器要等整个文件下载完才能拖进度条。6. 进阶用法批量下载多个 m3u8 并自动校验完整性的脚本思路6.1 用脚本管理多个任务的下载流程单个视频用在线工具够用但遇到课程合集动辄几十集手动粘贴 URL 会把人逼疯。我的习惯是在本地跑一个 Python 脚本把 m3u8 地址统一交给 ffmpeg在线工具只在需要手动注入密钥或特殊请求头时才介入。脚本逻辑很简单import subprocess import json tasks [ {name: chapter01, url: https://example.com/01/index.m3u8}, {name: chapter02, url: https://example.com/02/index.m3u8} ] headers Referer: https://example.com/course\nUser-Agent: Mozilla/5.0 for task in tasks: cmd [ ffmpeg, -y, -headers, headers, -i, task[url], -c, copy, f{task[name]}.mp4 ] result subprocess.run(cmd, capture_outputTrue, textTrue) print(f{task[name]} finished: {result.returncode})这段脚本把每个任务的头信息统一注入循环调用 ffmpeg 下载合并。-y是当场覆盖同名文件-c copy跳过重新编码。跑完一遍后检查returncode如果是0说明 ffmpeg 正常退出非零则需要去看stderr里的具体报错。6.2 校验输出文件时长对比、ts 数量与 ffprobe下载完并不等于下载对时长差十秒这种问题脚本发现不了。我的验证习惯是下载前先统计 m3u8 里#EXTINF的总时长下载后用 ffprobe 读取实际时长两者差值在 1 秒内视为正常。# 读取本地 mp4 的实际时长单位秒 ffprobe -v quiet -show_entries formatduration -of json output.mp4这个命令输出的 JSON 里能拿到duration字段。把它和从 m3u8 计算出的理论时长做对比差异超过 1.5 秒就标记为重试。另一点是检查文件内是否包含音频轨ffprobe -show_streams里出现codec_typeaudio才完整。我曾在批量下载后只抽检了画面结果一半文件没有音轨全部重新来过。从那以后我每次跑完批量任务都会强制把所有输出文件过一遍 ffprobe把时长偏差大于 1.5 秒或缺少音频轨的文件挑出来重新下。这一改再也没有交付过半成品。希望这套排查思路能帮到你——拿到工具只是第一步知道它每一步在做什么、失败时从哪查起才是能稳定复现的落地能力。本文还有配套的精品资源点击获取