如何通过Nodejs下载m3u8扩展名的视频

问题描述 投票:0回答:1
  • 我正在寻找通过nodejs
    https
    模块下载视频,但问题是当我尝试检查视频网址的来源时,视频以
    .m3u8
    扩展名结尾,我发现了每个人都包含的视频列表。视频的 4 秒,当我转到视频 uri 时,我找到了完整的视频,但我不知道如何下载它,因为它是由较小部分构建的,这是列表的示例
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:4.000000,
119GFO427S8MX5QDK9B2O.mp40.ts
#EXTINF:5.566667,
119GFO427S8MX5QDK9B2O.mp41.ts
#EXTINF:4.533333,
119GFO427S8MX5QDK9B2O.mp42.ts
#EXTINF:4.000000,
119GFO427S8MX5QDK9B2O.mp43.ts
#EXTINF:2.066667,
119GFO427S8MX5QDK9B2O.mp44.ts
#EXTINF:4.000000,
119GFO427S8MX5QDK9B2O.mp45.ts
#EXTINF:4.000000,
119GFO427S8MX5QDK9B2O.mp46.ts
#EXTINF:4.000000,
119GFO427S8MX5QDK9B2O.mp47.ts
#EXTINF:4.000000,
119GFO427S8MX5QDK9B2O.mp48.ts
....
  • 这是我的代码示例,当尝试从网址下载时,它只下载上面的 uri 列表,然后我应该一一下载它们,它们不是完整的视频
const https = require("https");
const fs = require("fs");
var req = https.get("https://example.com/video.mp4.m3u8", function(file) {
   fs.createWriteStream("./video.mp4"));
});
req.end();
  • 谁能帮助我
javascript node.js web-scraping video m3u8
1个回答
0
投票

对于每个索引/主 m3u8 文件,每个“质量”都有较小的文件。 HLS 流媒体视频的特点是它需要根据用户的带宽来改变质量。

举个例子:

#EXTM3U
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=950000,BANDWIDTH=1190000,RESOLUTION=640x360,FRAME-RATE=24.000,CODECS="avc1.4d001e,mp4a.40.2",CLOSED-CAPTIONS=NONE
tracks-v4a1/mono.m3u8
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=1270000,BANDWIDTH=1590000,RESOLUTION=852x480,FRAME-RATE=24.000,CODECS="avc1.4d001e,mp4a.40.2",CLOSED-CAPTIONS=NONE
tracks-v3a1/mono.m3u8
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=2840000,BANDWIDTH=3550000,RESOLUTION=1280x720,FRAME-RATE=24.000,CODECS="avc1.4d001f,mp4a.40.2",CLOSED-CAPTIONS=NONE
tracks-v2a1/mono.m3u8
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=3740000,BANDWIDTH=4680000,RESOLUTION=1920x1080,FRAME-RATE=24.000,CODECS="avc1.4d4028,mp4a.40.2",CLOSED-CAPTIONS=NONE
tracks-v1a1/mono.m3u8

您可以看到,对于每个

BANDWIDTH=
值,都有不同的分辨率。然后,为该质量选择一个单独的
m3u8
文件。我认为您想要最高的质量,所以您可以在列表中寻找最高的。或者,您可以全部下载。

以下是一些有用的辅助函数,您可以使用它们来提取所需的基本值:


export function extractTsUrls(m3u8Content: string): string[] {
  return extractLines(m3u8Content, /\.ts/);
}

export function extractM3u8Urls(m3u8Content: string): string[] {
  return extractLines(m3u8Content, /\.m3u8/);
}

export function extractLines(
  m3u8Content: string,
  filePattern: RegExp,
): string[] {
  const lines = m3u8Content.split('\n');
  const urls: string[] = [];
  lines.forEach((line) => {
    if (filePattern.test(line)) {
      urls.push(line);
    }
  });
  return urls;
}

export function extractKeyUrls(m3u8Content: string): string[] {
  const lines = m3u8Content.split('\n');
  const urls: string[] = [];
  lines.forEach((line) => {
    const match = line.match(/URI="([^"]+)"/);
    if (match) {
      urls.push(match[1]);
    }
  });
  return urls;
}

一般下载文件:


export function downloadFile(
  url: string,
  outputPath: string,
): Promise<Buffer> {
  return new Promise((resolve, reject) => {
    axios.get(url, { headers: HEADERS, responseType: 'stream' })
      .then((response: AxiosResponse<Stream>) => {
        const blob = fs.createWriteStream(outputPath);
        response.data.pipe(blob)
          .on('error', (error: Error) => {
            reject(error);
          })
          .on('finish', () => {
            fs.readFile(outputPath, (err, data) => {
              if (err) { reject(err); }
              resolve(data);
            });
          });
      })
      .catch((error) => {
        reject(error);
      });
  });
}

有了这些实用函数,您就可以读取 m3u8 文件,然后解析以获取所有子 m3u8 文件:

  const childUrls = extractM3u8Urls(m3u8FileContent);
  if (childUrls.length > 0) {
    allRequests.push(...childUrls.map(async (childUrl) => {
      const newBaseUrl = baseUrl ? baseUrl : extractBaseUrl(m3u8Url);
      const childRequests = await downloadM3u8(childUrl, newBaseUrl); // <-- this implementation is up to you.
      allRequests.push(...childRequests);
    }));
  }

您可以执行相同的操作来获取加密密钥等。

 const keyUrls = extractKeyUrls(m3u8FileContent);
  if (keyUrls.length > 0) {
    allRequests.push(...keyUrls.map(async (keyUrl) => {
      return downloadFile(urlToDownload, keyOutputPath); <-- just an example
    }));
  }

获取子 m3u8 文件后,您可以执行任何操作来选择您想要的版本(高质量或其他)。如果您想要所有这些,只需进行一些递归并下载所有内容即可。

完成所有操作后,从子 m3u8 文件中提取所有

.ts
相对 URL,附加基本 URL,然后开始下载。

const tsUrls = extractTsUrls(m3u8FileContent); // <-- grab ts files. 
  if (tsUrls.length > 0) {
    allRequests.push(...tsUrls.map(async (tsUrl) => {
      const newTsUrl = path.join(
        ...[
          baseUrl,
          parentPaths,
          tsUrl,
        ].filter((x) => !!x) as string[]); // <-- create the path using base URL
      
      return downloadFile(newTsUrl, tsOutputPath); // <-- example
    }));
  }

完成后,您应该有一个包含 m3u8 文件、加密密钥文件 (

.key
) 和
.ts
视频部分文件的文件夹。 TS 文件本质上只是每个 6 秒左右的视频片段。这样做的原因是,如果用户想跳到视频的中间,浏览器/播放器只需要从该部分下载即可。如果你想将它们全部连接在一起,你将需要某种编码器,例如
ffmpeg
。或者,您也可以只使用
npm serve .
,然后将 VLC 或其他播放器指向 m3u8 文件本地 URL,然后您就可以开始观看内容。

干杯!

© www.soinside.com 2019 - 2024. All rights reserved.