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
....
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();
对于每个索引/主 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,然后您就可以开始观看内容。
干杯!