mirror of
https://github.com/wukko/cobalt.git
synced 2024-11-15 04:39:58 +00:00
merge/vimeo: use HLS instead of dash & clean up (#585)
This commit is contained in:
commit
558b6a9efd
4 changed files with 75 additions and 72 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue