merge/vimeo: use HLS instead of dash & clean up (#585)

This commit is contained in:
wukko 2024-06-23 23:39:09 +06:00 committed by GitHub
commit 558b6a9efd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 75 additions and 72 deletions

View file

@ -24,7 +24,7 @@ export default function(r, host, userFormat, isAudioOnly, lang, isAudioMuted, di
else if (r.isGif && toGif) action = "gif"; else if (r.isGif && toGif) action = "gif";
else if (isAudioMuted) action = "muteVideo"; else if (isAudioMuted) action = "muteVideo";
else if (isAudioOnly) action = "audio"; else if (isAudioOnly) action = "audio";
else if (r.isM3U8) action = "singleM3U8"; else if (r.isM3U8) action = "m3u8";
else action = "video"; else action = "video";
if (action === "picker" || action === "audio") { if (action === "picker" || action === "audio") {
@ -48,13 +48,19 @@ export default function(r, host, userFormat, isAudioOnly, lang, isAudioMuted, di
params = { type: "gif" } params = { type: "gif" }
break; break;
case "singleM3U8": case "m3u8":
params = { type: "remux" } params = {
type: Array.isArray(r.urls) ? "render" : "remux"
}
break; break;
case "muteVideo": case "muteVideo":
let muteType = "mute";
if (Array.isArray(r.urls) && !r.isM3U8) {
muteType = "bridge";
}
params = { params = {
type: Array.isArray(r.urls) ? "bridge" : "mute", type: muteType,
u: Array.isArray(r.urls) ? r.urls[0] : r.urls, u: Array.isArray(r.urls) ? r.urls[0] : r.urls,
mute: true mute: true
} }

View file

@ -1,99 +1,88 @@
import { env } from "../../config.js"; import { env } from "../../config.js";
import { cleanString } from '../../sub/utils.js'; import { cleanString } from '../../sub/utils.js';
import HLS from "hls-parser";
const resolutionMatch = { const resolutionMatch = {
"3840": "2160", "3840": 2160,
"2732": "1440", "2732": 1440,
"2560": "1440", "2560": 1440,
"2048": "1080", "2048": 1080,
"1920": "1080", "1920": 1080,
"1366": "720", "1366": 720,
"1280": "720", "1280": 720,
"960": "480", "960": 480,
"640": "360", "640": 360,
"426": "240" "426": 240
}
const qualityMatch = {
"2160": "4K",
"1440": "2K",
"480": "540",
"4K": "2160",
"2K": "1440",
"540": "480"
} }
export default async function(obj) { export default async function(obj) {
let quality = obj.quality === "max" ? "9000" : obj.quality; let quality = obj.quality === "max" ? 9000 : Number(obj.quality);
if (!quality || obj.isAudioOnly) quality = "9000"; if (quality < 240) quality = 240;
if (!quality || obj.isAudioOnly) quality = 9000;
const url = new URL(`https://player.vimeo.com/video/${obj.id}/config`); const url = new URL(`https://player.vimeo.com/video/${obj.id}/config`);
if (obj.password) { if (obj.password) {
url.searchParams.set('h', obj.password); url.searchParams.set('h', obj.password);
} }
let api = await fetch(url) const api = await fetch(url)
.then(r => r.json()) .then(r => r.json())
.catch(() => {}); .catch(() => {});
if (!api) return { error: 'ErrorCouldntFetch' }; if (!api) return { error: 'ErrorCouldntFetch' };
let downloadType = "dash"; const fileMetadata = {
if (!obj.isAudioOnly && JSON.stringify(api).includes('"progressive":[{'))
downloadType = "progressive";
let fileMetadata = {
title: cleanString(api.video.title.trim()), title: cleanString(api.video.title.trim()),
artist: cleanString(api.video.owner.name.trim()), artist: cleanString(api.video.owner.name.trim()),
} }
if (downloadType !== "dash") { if (api.video?.duration > env.durationLimit)
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)
return { error: ['ErrorLengthLimit', env.durationLimit / 60] }; return { error: ['ErrorLengthLimit', env.durationLimit / 60] };
let masterJSONURL = api.request.files.dash.cdns.akfire_interconnect_quic.url; const urlMasterHLS = api.request?.files?.hls?.cdns?.akfire_interconnect_quic?.url;
let masterJSON = await fetch(masterJSONURL).then(r => r.json()).catch(() => {});
if (!masterJSON) return { error: 'ErrorCouldntFetch' }; if (!urlMasterHLS) return { error: 'ErrorCouldntFetch' }
if (!masterJSON.video) return { error: 'ErrorEmptyDownload' };
let masterJSON_Video = masterJSON.video const masterHLS = await fetch(urlMasterHLS)
.sort((a, b) => Number(b.width) - Number(a.width)) .then(r => r.text())
.filter(a => ["dash", "mp42"].includes(a.format)); .catch(() => {});
let bestVideo = masterJSON_Video[0]; if (!masterHLS) return { error: 'ErrorCouldntFetch' };
if (Number(quality) < Number(resolutionMatch[bestVideo.width])) {
bestVideo = masterJSON_Video.find(i => resolutionMatch[i.width] === quality) 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`; if (!bestQuality) bestQuality = variants[0];
const fallbackResolution = bestVideo.height > bestVideo.width ? bestVideo.width : bestVideo.height;
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 { return {
urls: masterM3U8, urls,
isM3U8: true, isM3U8: true,
fileMetadata: fileMetadata, fileMetadata: fileMetadata,
filenameAttributes: { filenameAttributes: {
@ -101,8 +90,8 @@ export default async function(obj) {
id: obj.id, id: obj.id,
title: fileMetadata.title, title: fileMetadata.title,
author: fileMetadata.artist, author: fileMetadata.artist,
resolution: `${bestVideo.width}x${bestVideo.height}`, resolution: `${bestQuality.resolution.width}x${bestQuality.resolution.height}`,
qualityLabel: `${resolutionMatch[bestVideo.width] || fallbackResolution}p`, qualityLabel: `${resolutionMatch[bestQuality.resolution.width]}p`,
extension: "mp4" extension: "mp4"
} }
} }

View file

@ -23,6 +23,10 @@ function transformObject(streamInfo, hlsObject) {
hlsObject.uri = createInternalStream(fullUrl.toString(), streamInfo); hlsObject.uri = createInternalStream(fullUrl.toString(), streamInfo);
if (hlsObject.map) {
hlsObject.map = transformObject(streamInfo, hlsObject.map);
}
return hlsObject; return hlsObject;
} }

View file

@ -92,6 +92,10 @@ export function streamLiveRender(streamInfo, res) {
args = args.concat(ffmpegArgs[format]); args = args.concat(ffmpegArgs[format]);
if (hlsExceptions.includes(streamInfo.service)) {
args.push('-bsf:a', 'aac_adtstoasc')
}
if (streamInfo.metadata) { if (streamInfo.metadata) {
args = args.concat(metadataManager(streamInfo.metadata)) args = args.concat(metadataManager(streamInfo.metadata))
} }