M3U8嗅探下载
思路与核心代码
基本思路
M3U8视频格式是一种基于HTTP Live Streaming(HLS)协议的视频文件格式。它是苹果公司开发的,目前广泛应用于iOS、macOS和tvOS等系统中。与传统的视频格式不同,M3U8视频格式将整个视频分成多个小片段进行传输,这些小片段可以根据网络情况自动调节其质量和大小。这种方式使得M3U8视频格式非常适合在网络环境不稳定或带宽不足的情况下播放视频。
示例-1就是一个m3u8文件的内容截取,一个个的ts文件就是一个个小的视频片段,所以我们拿到m3u8文件并解析内容之后,将一个个ts文件合并到一起就是完整的视频。浏览器 chrome.webRequest
API 可以观察和分析流量,以及拦截、屏蔽或修改运行中的请求。我们可以用它来嗅探网页中的m3u8文件地址。
示例 - 1:m3u8文件内容
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:8
#EXT-X-DISCONTINUITY
#EXTINF:4.000000,
cf645dad0e2000000.ts
#EXTINF:4.000000,
cf645dad0e2000001.ts
#EXTINF:4.000000,
cf645dad0e2000002.ts
#EXTINF:4.000000,
cf645dad0e2000003.ts
#EXT-X-ENDLIST
示例 - 2 :嗅探文件请求
var caches = {};
chrome.webRequest.onCompleted.addListener(function(details){
let rul = details.url;
let type = details.type;
let documentId = details.documentId;
if(type == 'xmlhttprequest'){
let suffix = rul.substr(rul.lastIndexOf('\.'));
if(suffix == '.m3u8'){
if(!caches[documentId]){
caches[documentId] = {list:[]};
}
caches[documentId].list.push(rul);
}
}
}, {urls: ["<all_urls>"]}, []);
- 使用
chrome.webRequest
API监听网络请求完成事件,当请求的是m3u8文件时记录请求路径。
示例 - 3 :解析m3u8并缓存ts
function cacheFile(tabId,index,url) {
fetch(url)
.then(response => {
return response.text()
})
.then(data => {
let lines = data.trim().split("\n");
let files = lines.filter(line => line.trim().endsWith('.ts')); // 需要缓存的文件列表
let baseUrl = url.substring(0,url.lastIndexOf('\/')); // 缓存视频的基本路径
let fileSize = 0; // 已经缓存的文件数量
files.forEach((fileName,x)=>{
fetch(baseUrl+"/"+fileName)
.then(response => {
return response.blob();
})
.then(blob => {
let reader = new FileReader();
reader.onloadend = function() {
let key = tabId+""+index+""+x;
chrome.storage.local.set({ [key] : reader.result }).then(() => {
fileSize = fileSize+1;
console.log("进度:"+fileSize+"/"+files.length);
if(fileSize==files.length){ // 全部缓存之后,通知合并下载
chrome.tabs.sendMessage(tabId,{ command: 'download',
tabId:tabId, index:index, fileSize:fileSize });
}else{ // 更新进度
chrome.tabs.sendMessage(tabId,{ command: 'update-cache-progress', tabId:tabId,
progress:fileSize, fileSize:files.length });
}
});
};
reader.readAsDataURL(blob);
})
.catch((err) => {
console.error("缓存异常:["+baseUrl+"/"+fileName+"]"+err);
});
});
});
}
示例 - 4 :环形进度条
<svg id="progressSVG" width="80" height="80">
<circle cx="40" cy="40" r="30" fill="none" stroke="#ddd" stroke-width="5" />
<circle class="progress" cx="40" cy="40" r="30" fill="none"
stroke="#0f0"
stroke-width="5"
stroke-dasharray="188.5"
stroke-dashoffset="190.9"/>
<text x="50%" y="50%" text-anchor="middle" dy=".3em" font-size="12">0%</text>
</svg>
- 两个空心圆,第二个空心圆通过stroke、stroke-width、stroke-dasharray、stroke-dashoffset定义了轮廓,通过更新stroke-dashoffset使其绿色轮廓覆盖第一个空心圆,就实现了一个简单的环形进度条。