M3U8 下載失敗怎麼辦?HLS 串流排查
M3U8 下載失敗通常是播放清單沒偵測到、線上工具受到跨網域限制,或串流媒體需要原影片頁面中的存取資訊。本文按 HLS 的實際下載流水線逐步拆解,告訴你失敗發生在哪一環、為什麼、怎麼修。
先理解:HLS 下載流水線
HLS 不是一條 URL 拉一個檔案那麼簡單。它是一條流水線,任何一環出錯,最終表現都是「下載失敗」或「檔案壞掉」。先認清這條線,排查才有方向:
① 偵測 M3U8 ② 解析清單 ③ 下載分片 ④ 解密 ⑤ 合併/轉封裝
↓ ↓ ↓ ↓ ↓
webRequest m3u8-parser Worker Pool WebCrypto FFmpeg WASM
攔截請求 區分 Master/ 並行拉 .ts AES-128-CBC TS → MP4
Media 重試退避 (如需要) 或直接拼接
| 環節 | 失敗表現 | 本文對應章節 |
|---|---|---|
| ① 偵測 | 彈出視窗顯示「未偵測到媒體」 | 偵測不到 M3U8 |
| ② 解析 | 清單下下來了但畫質/分片列表是空的 | 清單解析異常 |
| ③ 下載 | 進度卡住、403、404、CORS | 分片下載失敗 |
| ④ 解密 | 解密報錯或合併後沒聲音/花屏 | 加密流解密失敗 |
| ⑤ 合併 | 檔案生成了但無法播放、音畫不同步 | 合併後無法播放 |
EXT-X-KEY、多音軌)的完整說明,請參閱 影片嗅探 — HLS 串流。想看真實清單長什麼樣、為什麼「樸素下載器」會翻車,可以翻 HLS 深度篇。快速處理
在深入排查前,先跑一遍這套能解決 90% 場景的流程:
- 打開原影片頁面並播放幾秒(很多網站懶載入,不播就不會請求 M3U8)。
- 裝了擴充功能後沒重新整理過頁面的,重新整理一次(擴充功能的
webRequest監聽是頁面載入後才生效的)。 - 用擴充功能重新偵測,選擇目標畫質。
- 選 MP4 輸出,再點一次下載。
如果還是不行,按下面的章節定位。
偵測不到 M3U8
症狀:打開 FlowPick 彈出視窗顯示「未偵測到媒體資源」,或列表裡只有縮圖/封面,沒有 .m3u8 條目。

常見原因
| 原因 | 怎麼確認 | 解決 |
|---|---|---|
| 影片還沒開始載入 | 沒播過就打開擴充功能 | 播放 3-5 秒再打開擴充功能 |
| 裝了擴充功能後沒重新整理頁面 | 裝擴充功能前頁面已載入完畢 | 重新整理頁面重新觸發請求 |
| 使用 MSE 而非原生 HLS | 開發人員工具裡看不到 .m3u8 請求 | 見下方「MSE 網站」小節 |
| 清單在跨網域 iframe 裡 | Network 面板看得到,但擴充功能拿不到 | 直接打開 iframe 來源頁面再偵測 |
| 廣告攔截器等擴充功能衝突 | 暫時停用其他網路類擴充功能後能偵測到 | 加白名單或調整載入順序 |
| 自訂/私有協定 | Network 裡走的是 ws://、blob: 或加密流 | FlowPick 暫不支援,見 已知限制 |
用開發人員工具確認
按 F12 → Network 面板 → 在篩選框輸入 m3u8:

- 看得到請求:表示網站確實用 HLS,問題在擴充功能偵測那一側(多半是沒重新整理頁面或被其他擴充功能攔截)。
- 看不到請求:網站可能用了 MSE(Media Source Extensions),把分片直接餵給
<video>,根本不發.m3u8。這類網站 FlowPick 偵測不到,屬於瀏覽器層級的限制。
MSE 網站怎麼辦
如果 Network 裡看到的是一堆 .m4s / .chunk 請求但沒有清單檔,那它用的是 MSE + 自訂分片邏輯(YouTube 就是典型)。兩條出路:
- 找
.mpd(DASH)入口,改用 DASH 下載器,參考 DASH 沒有音訊排查。 - 找到
<video>標籤的src,如果是blob:開頭,那是 MSE 產生的,無法直接下載——只能等網站自己發原生 HLS/DASH 清單。
webRequest.onBeforeRequest、Content-Type 辨識)和 25+ 種 MIME 類型的完整列表,請參閱 影片嗅探 — 偵測原理。偵測不到時的系統排查流程,見 常見問題排查 — 媒體偵測問題。清單解析異常
症狀:M3U8 偵測到了,但畫質列表是空的,或分片數量異常少(比如只有 1 個),下載出來只有幾 KB。
Master 還是 Media
HLS 清單分兩層。如果你在彈出視窗裡看到的是 Media Playlist(分片清單本身),可能沒有畫質選項可選;反過來如果你拿到的只是 Master Playlist 的 URL,但解析出來的變體都是空的,多半是清單內容不完整。
Master Playlist ← 列出 360p / 720p / 1080p 變體
└── v_720.m3u8 ← Media Playlist,列出幾百個 .ts 分片
└── seg-001.ts, seg-002.ts, ...
自己檢查清單內容
把 M3U8 URL 複製出來,用 curl 看一眼內容(替換成你的 URL):
curl -sL "https://cdn.example.com/index.m3u8"
正常的 Master Playlist 應該看得到 #EXT-X-STREAM-INF 行:
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-STREAM-INF:BANDWIDTH=3000000,RESOLUTION=1280x720
v_720.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=1920x1080
v_1080.m3u8
正常的 Media Playlist 應該看得到 #EXTINF + 分片 URL:
#EXTM3U
#EXT-X-TARGETDURATION:6
#EXT-X-VERSION:3
#EXTINF:6.0,
seg-001.ts
#EXTINF:6.0,
seg-002.ts
#EXTINF:4.2,
seg-003.ts
#EXT-X-ENDLIST
常見異常
| 看到的內容 | 問題 | 處理 |
|---|---|---|
| 整個 HTML 頁面(登入頁/錯誤頁) | URL 被防盜連重新導向 | 見 403 與防盜連 |
只有 #EXTM3U 一行 | 清單被截斷或回應不完整 | 重新打開頁面取得新 URL |
有 #EXT-X-KEY 但沒分片 | 加密流金鑰失敗導致後續不輸出 | 見 加密流解密失敗 |
有 #EXT-X-ENDLIST 但分片數 < 5 | 直播流剛開播或已結束 | 直播流需在結束後下載回放 |
| 分片 URL 是相對路徑 | 正常,解析器會補全基址 | 無需處理 |
CODECS 欄位、不連續性標籤等),這些是「樸素下載器」翻車的高發地。完整解析邏輯見 HLS 深度篇 和 影片嗅探 — Master Playlist vs Media Playlist。分片下載失敗
症狀:進度條走到一半報錯、卡住不動,或主控台大量 403/404/CORS 報錯。
403 與防盜連
分片或清單請求回傳 403 Forbidden,幾乎都是防盜連校驗沒過。常見三類:
| 類型 | 表現 | FlowPick 處理 |
|---|---|---|
| Referer 校驗 | 直接 curl 能下,瀏覽器擴充功能有時 403 | v1.1.1 起擴充功能自動注入原頁面 Referer |
| Cookie / 登入狀態 | 未登入時 403,登入後正常 | 在原影片頁面使用擴充功能即可攜帶 Cookie |
| 短時 Token | URL 裡帶 ?token=xxx&expires=yyy,過幾分鐘失效 | 見下方「過期位址」 |
擴充功能版的防盜連代理(v1.1.1+)
從 v1.1.1 起,FlowPick 內建了防盜連代理預覽與下載:圖片列表、影片封面、懸停預覽、圖片/音訊/文件/字幕下載,都會自動走代理,帶上原頁面的 Referer 和 Cookie,並在儲存前檢查回應——避免把防盜連回傳的 HTML 錯誤頁當成圖片或音訊存下來。
線上工具為什麼常常 403
線上工具跑在 flowpick.com 網域下,瀏覽器不會把目標網站的 Cookie 和原頁面 Referer 帶過去,所以只要來源站做了防盜連,線上工具基本上一定掛。帶防盜連的網站請直接用擴充功能,在原影片頁面操作。
用 curl 重現 403
確認是不是 Referer 問題最快的方法:
# 不帶 Referer,多半 403
curl -I "https://cdn.example.com/seg-001.ts"
# 帶上原頁面 Referer,應該 200
curl -I -H "Referer: https://www.example.com/watch/123" "https://cdn.example.com/seg-001.ts"
第二個能回 200,就坐實是 Referer 校驗。
過期位址(動態 Token)
直播回放和部分平台的點播用短期 Token:
https://cdn.example.com/seg-001.ts?token=ab12cd34&expires=1692300000
expires 是 Unix 時間戳記,過這個時間就 403/404。表現:
- 剛開始下載好好的,下到一半突然全部分片 403。
- 重新打開頁面後又能下,但過一會兒又掛。
處理:
- 偵測到清單後盡快開始下載,別在彈出視窗裡猶豫太久。
- 大檔案被 Token 截斷時,重新整理原頁面,讓擴充功能重新抓一份新清單,繼續未完成的部分(FlowPick 的失敗重試會處理臨時波動,但 Token 過期是硬性失敗,必須換新清單)。
404 分片已刪除
多見於直播回放——直播結束後平台只保留有限時間的回放分片,超期分片直接 404。
- 換個畫質試試(不同畫質可能對應不同分片檔案)。
- 確認回放是否仍在有效期內。
CORS 錯誤
主控台報 has been blocked by CORS policy,基本都是線上工具遇到的。擴充功能有 host_permissions,不受同源策略限制。
| 工具 | CORS 表現 | 處理 |
|---|---|---|
| 瀏覽器擴充功能 | 不會觸發 CORS | — |
| 線上工具 | 清單/分片跨網域拉取被攔 | 改用擴充功能;或確認來源站是否回傳 Access-Control-Allow-Origin |
下載途中網路中斷
臨時網路抖動由重試機制覆蓋。持續失敗時:
- 檢查網路穩定性(特別是代理/VPN 環境,見 常見問題排查 — 代理/VPN 環境)。
- 把並行執行緒數降到 2-3,減少同時連線數(部分 CDN 會拒絕過高並行)。
- 檢查防火牆是否攔截了串流媒體 CDN 網域。
加密流解密失敗
症狀:清單裡有 #EXT-X-KEY,下載能跑完,但合併後沒聲音/花屏/無法播放,或主控台報解密錯誤。
先區分 AES-128 還是 DRM
FlowPick 只支援 AES-128 這種「裸」加密。清單裡看到這些標籤的,通通下不了:
# AES-128 —— 支援
#EXT-X-KEY:METHOD=AES-128,URI="https://cdn.example.com/key.bin",IV=0x...
# SAMPLE-AES / FairPlay —— 不支援(接近 DRM)
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://..."
# Widevine / PlayReady(DASH 裡常見)—— 不支援
<ContentProtection schemeIdUri="..."/>
AES-128 解密失敗的常見原因
| 原因 | 怎麼確認 | 處理 |
|---|---|---|
| 金鑰 URL 403/404 | 主控台報金鑰拉取失敗 | 在原頁面用擴充功能(帶 Cookie/Referer);金鑰也可能帶 Token |
| 金鑰需要登入狀態 | 未登入時金鑰 403,登入後正常 | 先登入再下載 |
| IV 缺失 | 清單裡 #EXT-X-KEY 沒有 IV= | FlowPick 會依規範用分片序號推導 IV,無需手動處理 |
| 金鑰不是 16 位元組 | curl 下來長度不對 | 來源站異常,非 FlowPick 可修 |
用 curl 驗證金鑰
# 看金鑰是否拿得到,注意要帶 Referer/Cookie
curl -sL -H "Referer: https://www.example.com/watch/123" \
"https://cdn.example.com/key.bin" | wc -c
# 正常應該輸出 16
輸出不是 16,金鑰本身有問題;輸出 16 但擴充功能還是解密失敗,多半是擴充功能請求時沒帶上 Cookie/Referer(升級到 v1.1.1+,防盜連代理會自動帶)。
DRM 內容怎麼辦
Widevine、PlayReady、FairPlay、SAMPLE-AES 這些 DRM 保護內容,FlowPick 明確拒絕處理——這是特性不是 bug。聲稱能解 DRM 的下載器要不是在說謊就是違法。法律和技術底線見 串流下載合法性指南。
合併後無法播放
症狀:檔案下下來了,大小也正常,但 VLC/播放器打不開,或只有畫面沒聲音,或音畫不同步。
先用 VLC 測試
VLC 是相容性最好的播放器,先用它排除「播放器問題」:
- VLC 能播、系統播放器不能播 → 編解碼/容器相容問題,換播放器。
- VLC 也播不了 → 檔案本身有問題。
常見原因
| 表現 | 原因 | 處理 |
|---|---|---|
| 檔案只有幾 KB | 下到了清單不是影片 | 重新選擇資源,確認選的是影片不是 .m3u8 |
| 有畫面沒聲音 | 多音軌清單只下了影片軌道 | 見下方「多音軌」 |
| 音畫不同步 | HLS 分片 PTS 不連續 | 切 TS 輸出格式,或用 FFmpeg 重新編碼 |
| 花屏/損壞 | 個別分片下載失敗導致合並不完整 | 重新下載,確保所有分片完整 |
| MP4 打不開但 TS 能播 | 轉封裝時 stsd box 寫錯 | 見下方「TS vs MP4」 |
多音軌清單
真實 HLS 清單(Netflix、Disney+ 那種)常把音訊和影片分開放:
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac-128",NAME="English",DEFAULT=YES,URI="audio/eng_128.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=3000000,...,AUDIO="aac-128"
v_720.m3u8
如果你的下載器只抓 v_720.m3u8 然後拼分片,得到的就會是無聲影片。FlowPick 會自動同時拉音訊軌道並合併,但前提是你選了包含音訊的變體而不是只選影片流。多音軌清單的完整處理見 HLS 深度篇 — EXT-X-MEDIA。
TS vs MP4 輸出格式
遇到 MP4 打不開的情況,先切到 TS 輸出格式試試。原因:
- TS 分片是裸拼接,MPEG-TS 自帶 codec 資訊,幾乎不會錯。
- MP4 轉封裝需要解析
CODECS欄位、寫對stsdbox,遇到非標準 codec 標示會翻車。
TS 能播、MP4 不能播,多半是轉封裝問題,可以先用 TS 救急;要 MP4 的話用 FFmpeg 命令列手動轉一次:
ffmpeg -i output.ts -c copy output.mp4
-c copy 不重新編碼,秒級完成。
大檔案 / 瀏覽器卡死
症狀:下載大檔案(>1GB)時瀏覽器卡頓、崩潰,或下載到一定比例直接中斷。
這是寫入策略問題,不是 HLS 本身的問題。FlowPick 三級寫入策略:
| 策略 | 觸發條件 | 上限 |
|---|---|---|
| File System Access API | Chrome/Edge 86+ | 無硬上限 |
| StreamSaver.js | 支援 Service Worker 的瀏覽器 | 無硬上限 |
| Blob 備援 | 其他情況 | 1.5GB 硬限制 |
Blob 模式下超過 1.5GB 會被直接拒絕。解決方案:
- 用 Chrome 86+ 或 Edge 86+,啟用「選擇儲存目錄」功能。
- Firefox 不支援 FSA,大檔案走 StreamSaver 或 OPFS 暫存儲存(v1.1.1+ 優化)。
- 實在不行,把並行數降到 1-2,減少記憶體佔用。
診斷工具速查
排查時按 F12 打開開發人員工具,下面是幾個最常用的檢查點:
| 檢查 | 面板 | 操作 |
|---|---|---|
| M3U8 有沒有被請求 | Network | 篩選框輸入 m3u8,重新整理頁面播放影片 |
| 分片請求狀態碼 | Network | 篩選框輸入 .ts 或 .m4s,看 Status 欄 |
| 請求帶了什麼標頭 | Network → 選中請求 → Headers | 看 Request Headers 的 Referer/Cookie |
| 解密報錯 | 主控台 | 搜尋 decrypt 或 AES |
| 合併報錯 | 主控台 | 搜尋 ffmpeg 或 wasm |
產生環境診斷報告
複雜問題可以在主控台跑下面這段,產生環境資訊便於回報:
const report = {
ua: navigator.userAgent,
fsa: 'showDirectoryPicker' in window,
sab: typeof SharedArrayBuffer !== 'undefined',
coi: self.crossOriginIsolated,
storage: await navigator.storage?.estimate().catch(() => null),
ts: new Date().toISOString(),
}
console.log(JSON.stringify(report, null, 2))
報告只含瀏覽器環境資訊,不含任何瀏覽記錄或個人資料。
什麼時候真的下不了
FlowPick 適合公開的標準 HLS 播放清單。以下場景是設計上不支援,不是 bug:
- DRM 保護(Widevine / PlayReady / FairPlay / SAMPLE-AES)
- 需要破解登入或付費牆的內容
- 使用 MSE 自訂分片、不發原生 M3U8/MPD 的網站
- 私有協定、WebSocket、WebRTC 傳輸的媒體
碰到這些,正確的行為是報錯而不是默默產出損壞檔案——這也是 FlowPick 的態度。法律和技術底線詳見 串流下載合法性指南。