异步下载可切换画质 m3u8 视频
m3u8格式视频可无缝切换视频清晰度,众多个短视频组成。便于观看,但不利于需要下载该视频的同学。
需求
常逛社区,也看视频。比如慕课,想将视频下载下来,本地观看,但常有如下地址blob:https
开头,常规方法不可达
<video id="video-box-mocoplayer-hls-video_html5_api" class="vjs-tech" preload="auto" autobuffer="" src="blob:https://www.imooc.com/8b40ec52-5b8c-4f9e-b817-04168f7d186d"> </video>
分析
用调试工具查看了一下,发现与m3u8相关。查阅相关资料,在于m3u8这种可切换清晰度的视频,在前端表现是分片传输。换而言之,他有一个类似于索引的文件,每个视频由从多ts结尾的小视频文件组成。也就是在xhr下,每个不超过10秒ts视频文件。索引文件内记录小视频的顺序,链接地址以及时长等相关信息。
思路
将众多ts视频文件下载,然后顺序拼接一个大视频,就可播放了。
m3u8文件内容大致如下,上述小视频文件名被慕课编码了。
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:7
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:6.318667,
00000.ts
#EXTINF:5.966667,
00001.ts
#EXTINF:5.966667,
...
EXTINF:6.000000,
00139.ts
#EXTINF:1.733333,
00140.ts
#EXT-X-ENDLIST
其在xhr显示
细心的你可能发现了,两个地址还是不一样,只有ts文件名相同。没错页面用js动态拼接了前面一大串。
索引信息与xhr请求地址两相印证确定了分片视频真实地址,以及分片数,然后就可用request库进行下载了
编码
const fs = require("fs")
const request = require("request")
vonst path = require("path")
var dirPath = path.join(__dirname, "tsfile");
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath);
console.log("文件夹创建成功");
}
// 并发分片下载
for (let i = 0; i <= 140; i++) {
let fileName = intToString(i, 5) + ".ts";
let url = "https://xxxxxxxxx/v/c04629a3aa0c254ebf76acece97f9b24/" + fileName;
// 建立流拼接
let stream = fs.createWriteStream(path.join(dirPath, fileName));
// 未提供header的情况下,会检测文件后缀名,在请求中设置相应的content-type
request(url).pipe(stream).on("close", function (err) {
console.log("文件[" + fileName + "]下载完毕");
});
}
//整数转字符串,不足的位数用0补齐
function intToString(num, len) {
let str = num.toString();
while (str.length < len) {
str = "0" + str;
}
return str;
}
效果
看输出,request库多个任务下载。其完成顺序与任务分发顺序并非一致。换而言之,它并发执行,极大提高了下载速度。
下一篇使用nodejs的stream库将其合一个整视频。博客:用 Stream 合并多个视频文件
本作品采用《CC 协议》,转载必须注明作者和本文链接