diff --git a/src/modules/processing/matchActionDecider.js b/src/modules/processing/matchActionDecider.js index f7ed3da9..74f0f8c7 100644 --- a/src/modules/processing/matchActionDecider.js +++ b/src/modules/processing/matchActionDecider.js @@ -24,7 +24,7 @@ export default function(r, host, userFormat, isAudioOnly, lang, isAudioMuted, di else if (r.isGif && toGif) action = "gif"; else if (isAudioMuted) action = "muteVideo"; else if (isAudioOnly) action = "audio"; - else if (r.isM3U8) action = "singleM3U8"; + else if (r.isM3U8) action = "m3u8"; else action = "video"; if (action === "picker" || action === "audio") { @@ -48,13 +48,19 @@ export default function(r, host, userFormat, isAudioOnly, lang, isAudioMuted, di params = { type: "gif" } break; - case "singleM3U8": - params = { type: "remux" } + case "m3u8": + params = { + type: Array.isArray(r.urls) ? "render" : "remux" + } break; case "muteVideo": + let muteType = "mute"; + if (Array.isArray(r.urls) && !r.isM3U8) { + muteType = "bridge"; + } params = { - type: Array.isArray(r.urls) ? "bridge" : "mute", + type: muteType, u: Array.isArray(r.urls) ? r.urls[0] : r.urls, mute: true } diff --git a/src/modules/processing/services/vimeo.js b/src/modules/processing/services/vimeo.js index 69a36eca..d79d2e92 100644 --- a/src/modules/processing/services/vimeo.js +++ b/src/modules/processing/services/vimeo.js @@ -1,99 +1,88 @@ import { env } from "../../config.js"; import { cleanString } from '../../sub/utils.js'; +import HLS from "hls-parser"; + const resolutionMatch = { - "3840": "2160", - "2732": "1440", - "2560": "1440", - "2048": "1080", - "1920": "1080", - "1366": "720", - "1280": "720", - "960": "480", - "640": "360", - "426": "240" -} - -const qualityMatch = { - "2160": "4K", - "1440": "2K", - "480": "540", - - "4K": "2160", - "2K": "1440", - "540": "480" + "3840": 2160, + "2732": 1440, + "2560": 1440, + "2048": 1080, + "1920": 1080, + "1366": 720, + "1280": 720, + "960": 480, + "640": 360, + "426": 240 } export default async function(obj) { - let quality = obj.quality === "max" ? "9000" : obj.quality; - if (!quality || obj.isAudioOnly) quality = "9000"; + let quality = obj.quality === "max" ? 9000 : Number(obj.quality); + if (quality < 240) quality = 240; + if (!quality || obj.isAudioOnly) quality = 9000; const url = new URL(`https://player.vimeo.com/video/${obj.id}/config`); if (obj.password) { url.searchParams.set('h', obj.password); } - let api = await fetch(url) + const api = await fetch(url) .then(r => r.json()) .catch(() => {}); + if (!api) return { error: 'ErrorCouldntFetch' }; - let downloadType = "dash"; - - if (!obj.isAudioOnly && JSON.stringify(api).includes('"progressive":[{')) - downloadType = "progressive"; - - let fileMetadata = { + const fileMetadata = { title: cleanString(api.video.title.trim()), artist: cleanString(api.video.owner.name.trim()), } - if (downloadType !== "dash") { - if (qualityMatch[quality]) quality = qualityMatch[quality]; - let all = api.request.files.progressive.sort((a, b) => Number(b.width) - Number(a.width)); - let best = all[0]; - - let bestQuality = all[0].quality.split('p')[0]; - if (qualityMatch[bestQuality]) { - bestQuality = qualityMatch[bestQuality] - } - - if (Number(quality) < Number(bestQuality)) { - best = all.find(i => i.quality.split('p')[0] === quality); - } - - if (!best) return { error: 'ErrorEmptyDownload' }; - - return { - urls: best.url, - audioFilename: `vimeo_${obj.id}_audio`, - filename: `vimeo_${obj.id}_${best.width}x${best.height}.mp4` - } - } - - if (api.video.duration > env.durationLimit) + if (api.video?.duration > env.durationLimit) return { error: ['ErrorLengthLimit', env.durationLimit / 60] }; - let masterJSONURL = api.request.files.dash.cdns.akfire_interconnect_quic.url; - let masterJSON = await fetch(masterJSONURL).then(r => r.json()).catch(() => {}); + const urlMasterHLS = api.request?.files?.hls?.cdns?.akfire_interconnect_quic?.url; - if (!masterJSON) return { error: 'ErrorCouldntFetch' }; - if (!masterJSON.video) return { error: 'ErrorEmptyDownload' }; + if (!urlMasterHLS) return { error: 'ErrorCouldntFetch' } - let masterJSON_Video = masterJSON.video - .sort((a, b) => Number(b.width) - Number(a.width)) - .filter(a => ["dash", "mp42"].includes(a.format)); + const masterHLS = await fetch(urlMasterHLS) + .then(r => r.text()) + .catch(() => {}); - let bestVideo = masterJSON_Video[0]; - if (Number(quality) < Number(resolutionMatch[bestVideo.width])) { - bestVideo = masterJSON_Video.find(i => resolutionMatch[i.width] === quality) + if (!masterHLS) return { error: 'ErrorCouldntFetch' }; + + const variants = HLS.parse(masterHLS)?.variants?.sort( + (a, b) => Number(b.bandwidth) - Number(a.bandwidth) + ); + if (!variants || variants.length === 0) return { error: 'ErrorEmptyDownload' }; + + let bestQuality; + + if (quality < resolutionMatch[variants[0]?.resolution?.width]) { + bestQuality = variants.find(v => + (quality === resolutionMatch[v.resolution.width]) + ); } - let masterM3U8 = `${masterJSONURL.split("/sep/")[0]}/sep/video/${bestVideo.id}/master.m3u8`; - const fallbackResolution = bestVideo.height > bestVideo.width ? bestVideo.width : bestVideo.height; + if (!bestQuality) bestQuality = variants[0]; + + const expandLink = (path) => { + return new URL(path, urlMasterHLS).toString(); + }; + + let urls = expandLink(bestQuality.uri); + + const audioPath = bestQuality?.audio[0]?.uri; + if (audioPath) { + urls = [ + urls, + expandLink(audioPath) + ] + } else if (obj.isAudioOnly) { + return { error: 'ErrorEmptyDownload' }; + } return { - urls: masterM3U8, + urls, isM3U8: true, fileMetadata: fileMetadata, filenameAttributes: { @@ -101,8 +90,8 @@ export default async function(obj) { id: obj.id, title: fileMetadata.title, author: fileMetadata.artist, - resolution: `${bestVideo.width}x${bestVideo.height}`, - qualityLabel: `${resolutionMatch[bestVideo.width] || fallbackResolution}p`, + resolution: `${bestQuality.resolution.width}x${bestQuality.resolution.height}`, + qualityLabel: `${resolutionMatch[bestQuality.resolution.width]}p`, extension: "mp4" } } diff --git a/src/modules/stream/internal-hls.js b/src/modules/stream/internal-hls.js index 9fa37e6e..3ed7e17b 100644 --- a/src/modules/stream/internal-hls.js +++ b/src/modules/stream/internal-hls.js @@ -23,6 +23,10 @@ function transformObject(streamInfo, hlsObject) { hlsObject.uri = createInternalStream(fullUrl.toString(), streamInfo); + if (hlsObject.map) { + hlsObject.map = transformObject(streamInfo, hlsObject.map); + } + return hlsObject; } diff --git a/src/modules/stream/types.js b/src/modules/stream/types.js index 000b7f7f..af4aa2e5 100644 --- a/src/modules/stream/types.js +++ b/src/modules/stream/types.js @@ -92,6 +92,10 @@ export function streamLiveRender(streamInfo, res) { args = args.concat(ffmpegArgs[format]); + if (hlsExceptions.includes(streamInfo.service)) { + args.push('-bsf:a', 'aac_adtstoasc') + } + if (streamInfo.metadata) { args = args.concat(metadataManager(streamInfo.metadata)) }