Published on
796

Node.js实现批量下载m3u8视频文件并进行合并

Authors
  • avatar
    Name
    小辉辉
    Twitter

前言

事情的经过是这样的,一朋友在看一个限时提供的网课视频,来问我有没有办法把整个视频给下载下来,这样就可以在后面慢慢看了,我拿到地址了,大致看了下视频的加载方式,他们使用的是m3u8文件加载方案,这个方案之前有接触过,于是便试了试,最终终于把网课视频给下载到本地了。

实现思路

下面就是来讲讲我的实现思路,总体分为三个部分。

m3u8文件加载基本原理

现在大多数的在线视频播放基本采用的是分段加载方案,主要原理为给播放器提供一个m3u8的文件,播放器下载完文件后会对内容进行解析,里面内容我们可以简单理解成一串的播放列表。

这个播放列表由多个视频片段组成,每个视频片段标注了对应的信息,其中最主要的是视频播放时长和每段视频单独的加载地址,一般以ts格式结尾,播放器解析结束后,按照播放进度加载对应的视频片段来完成整个视频的播放。

看的出来,利用上述的m3u8方案可以显著提升视频的播放效率和加载速度,同时可以避免加载当前不必要播放的资源。

Node.js实现批量下载m3u8视频文件

核心代码片段:

// 引入必要的模块
import got from 'got';
import pLimit from 'p-limit';
import {readFileSync, createWriteStream} from "fs";

const prefix = 'xxx'; // 视频文件的前缀
const limit = pLimit(2); // 限制同时视频下载个数,以防被服务器拦截
const file =readFileSync('./index.m3u8', {encoding:'utf8'}); // 读取文件内容

const urls = file.split('\n').filter(d=>d.includes('.ts')).map(url=>{
	return limit(() => got.stream(`${prefix}/${url}`).pipe(createWriteStream(`./movie/${url}`)))
});

// 开始批量下载ts资源
Promise.all(urls).then((v)=>{
  console.log('finish');
}).catch(err=>{
	console.error('视频文件加载出差!', err);
})

上述代码可以实现把已经下载好的index.m3u8文件里的视频文件批量下载到movie文件夹中。

Node.js实现ts文件合并

核心代码片段:

import fs from 'fs'
import path from 'path';

const streamMerge = (sourceFileDirectory, targetFile) => {
  const fileWriteStream = fs.createWriteStream(path.resolve(__dirname, targetFile));

  const scripts =fs.readFileSync('./index.m3u8', {encoding:'utf8'}).split('\n').filter(d=>d.includes('.ts'));
  
  // 调用合并文件
  return streamMergeRecursive(scripts, fileWriteStream, sourceFileDirectory);
}

const streamMergeRecursive = (scripts=[], fileWriteStream, sourceFileDirectory) => {
  // 递归到尾部情况判断
  if (!scripts.length) {
    return fileWriteStream.end("console.log('Stream 合并完成')");
  }

  const currentFile = path.resolve(__dirname, sourceFileDirectory, scripts.shift());
  const currentReadStream = fs.createReadStream(currentFile);

  currentReadStream.pipe(fileWriteStream, { end: false });
  currentReadStream.on('end', function() {
    // 单个文件合并结束,继续下一个
    streamMergeRecursive(scripts, fileWriteStream, sourceFileDirectory);
  });

  currentReadStream.on('error', function(error) {
    console.error(error);
    fileWriteStream.close();
  });
}

streamMerge('./movie', './movie.mp4');

上述代码实现了将movie下的ts片段合并为movie.mp4文件,注意,为了保证合并顺序,这里还用到了m3u8文件里的原始视频片段的排序。

总结

整体流程下来,从最开始理清原理到代码实现花了比较多的时间,但后面运行代码也就耗时2分钟左右。

特别要主要的是视频加载一定要加上个数限制,否则很容易被对方的服务器给封禁,这样一来就得不偿失了。