From 3f49cd1618a82cce9c1a74b591078125ce67e789 Mon Sep 17 00:00:00 2001 From: Vitaly Gashkov Date: Tue, 6 Aug 2024 22:11:34 +0500 Subject: [PATCH] fix: add more filters for hls manifest, fix relative uri resolving, add video track type --- lib/hls.js | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/lib/hls.js b/lib/hls.js index ec9b9ab..1664f63 100644 --- a/lib/hls.js +++ b/lib/hls.js @@ -1,5 +1,6 @@ 'use strict'; +const { dirname } = require('node:path'); const m3u8Parser = require('m3u8-parser'); const { parseBitrate, getQualityLabel } = require('./util'); const { @@ -7,6 +8,9 @@ const { createVideoQualityFilter, createAudioLanguageFilter, createSubtitleLanguageFilter, + createVideoCodecFilter, + createAudioCodecFilter, + createAudioChannelsFilter, } = require('./track'); const parseM3u8 = (manifestString) => { @@ -22,9 +26,9 @@ const fetchPlaylist = async (url) => .then(parseM3u8); const parseUrl = (playlistUri, manifestUri) => { - if (playlistUri.includes('https://')) return playlistUri; - const uri = new URL(manifestUri); - return uri.origin + playlistUri; + let value = playlistUri; + if (!value.startsWith('https://')) value = new URL(value, manifestUri).toString(); + return value; }; const urlsSame = (url1, url2) => { @@ -63,6 +67,7 @@ const getVideoPlaylists = (m3u8, manifestUri) => { const bandwidth = data.attributes.BANDWIDTH; const url = data.resolvedUri || parseUrl(data.uri, manifestUri); const track = { bitrate: parseBitrate(bandwidth), url }; + track.type = 'video'; if (data.attributes.RESOLUTION) { track.resolution = data.attributes.RESOLUTION; track.quality = getQualityLabel(track.resolution); @@ -73,13 +78,20 @@ const getVideoPlaylists = (m3u8, manifestUri) => { }); }; -const segmentsDto = (data = []) => { - const mapSegment = (item) => ({ - url: item.resolvedUri || item.uri, - duration: item.duration, - number: item.number, - presentationTime: item.presentationTime, - }); +const segmentsDto = (data = [], track) => { + const mapSegment = (item) => { + let url = item.resolvedUri || item.uri; + if (!url.startsWith('https://')) { + const baseUrl = dirname(track.url) + '/'; + url = new URL(url, baseUrl).toString(); + } + return { + url, + duration: item.duration, + number: item.number, + presentationTime: item.presentationTime, + }; + }; const segments = data.map(mapSegment); if (data.length && data[0].map?.resolvedUri) segments.unshift({ @@ -96,7 +108,7 @@ const fetchTrackSegments = (tracks) => { return Promise.all( tracks.map(async (track) => { const playlist = await fetchPlaylist(track.url); - track.segments = segmentsDto(playlist.segments); + track.segments = segmentsDto(playlist.segments, track); if (playlist.contentProtection) { track.protection = {}; const fairplayLegacy = playlist.contentProtection['com.apple.fps.1_0']; @@ -130,8 +142,11 @@ const parseManifest = async (manifestString, manifestUri) => { audios, subtitles, withResolution: createResolutionFilter(videos), + withVideoCodecs: createVideoCodecFilter(videos), withVideoQuality: createVideoQualityFilter(videos), + withAudioCodecs: createAudioCodecFilter(audios), withAudioLanguages: createAudioLanguageFilter(audios), + withAudioChannels: createAudioChannelsFilter(audios), withSubtitleLanguages: createSubtitleLanguageFilter(subtitles), }, };