异步下载可切换画质 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 协议》,转载必须注明作者和本文链接
pardon110
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
开发者 @ 社科大
文章
135
粉丝
24
喜欢
103
收藏
56
排名:105
访问:8.9 万
私信
所有博文
社区赞助商