From 4feb93612a9f6e1080f0d6d173a7990bb7c4bbcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D1=81=D0=B5=D0=B2=D0=BE=D0=BB=D0=BE=D0=B4=20=D0=9E?= =?UTF-8?q?=2E?= Date: Sat, 4 Jan 2025 15:28:25 +0300 Subject: [PATCH] api: add in yt downloader quality --- api/src/processing/match-action.js | 6 +- api/src/processing/services/youtube.js | 108 +++++++++++++++++++++---- 2 files changed, 96 insertions(+), 18 deletions(-) diff --git a/api/src/processing/match-action.js b/api/src/processing/match-action.js index d1a0995b..7562579e 100644 --- a/api/src/processing/match-action.js +++ b/api/src/processing/match-action.js @@ -59,9 +59,6 @@ export default function({ r, host, audioFormat, isAudioOnly, isAudioMuted, disab type: Array.isArray(r.urls) ? "merge" : "remux", isHLS: true, } - if (r.type === 'redirect') { - responseType = "redirect"; - } break; case "muteVideo": @@ -119,6 +116,7 @@ export default function({ r, host, audioFormat, isAudioOnly, isAudioMuted, disab case "youtube": params = { type: r.type }; + responseType = "redirect"; break; case "reddit": @@ -215,5 +213,7 @@ export default function({ r, host, audioFormat, isAudioOnly, isAudioMuted, disab params.type = "proxy"; } + //console.log(responseType, {...defaultParams, ...params}); + return createResponse(responseType, {...defaultParams, ...params}) } diff --git a/api/src/processing/services/youtube.js b/api/src/processing/services/youtube.js index 0c976561..4b7fa419 100644 --- a/api/src/processing/services/youtube.js +++ b/api/src/processing/services/youtube.js @@ -1,7 +1,56 @@ import youtubedl from 'youtube-dl-exec'; import { env } from '../../config.js'; +import * as fs from 'fs'; + +const videoQualities = [144, 240, 360, 480, 720, 1080, 1440, 2160, 4320]; + +const codecList = { + h264: { + videoCodec: "avc1", + audioCodec: "mp4a", + container: "mp4" + }, + av1: { + videoCodec: "av01", + audioCodec: "opus", + container: "webm" + }, + vp9: { + videoCodec: "vp9", + audioCodec: "opus", + container: "webm" + } +}; + +const hlsCodecList = { + h264: { + videoCodec: "avc1", + audioCodec: "mp4a", + container: "mp4" + }, + vp9: { + videoCodec: "vp09", + audioCodec: "mp4a", + container: "webm" + } +}; export default async function(o) { + let useHLS = o.youtubeHLS; + + if (useHLS && o.format === "av1") { + useHLS = false; + } + + const quality = o.quality === "max" ? 9000 : Number(o.quality); + + const normalizeQuality = res => { + const shortestSide = res.height > res.width ? res.width : res.height; + return videoQualities.find(qual => qual >= shortestSide); + }; + + let codec = o.format || "h264"; + try { const output = await youtubedl(`https://www.youtube.com/watch?v=${o.id}`, { dumpSingleJson: true, @@ -13,6 +62,7 @@ export default async function(o) { ], downloader: 'ffmpeg', hlsUseMpegts: true, + //getUrl: true, }); const { is_live, duration, formats, title } = output; @@ -29,25 +79,53 @@ export default async function(o) { return { error: "youtube.no_matching_format" }; } - const qualityDict = formats.reduce((acc, format) => { - const qualityLabel = format.height ? `${format.height}p` : "unknown"; - acc[qualityLabel] = { - url: format.url, - extension: format.ext, - resolution: format.height ? `${format.height}p` : "unknown", - youtubeFormat: format.ext - }; - return acc; - }, {}); + const matchHlsCodec = codecs => ( + codecs.includes(hlsCodecList[codec]?.videoCodec || codecList.h264.videoCodec) + ); + + const variants = formats.map(format => ({ + url: format.url, + codecs: format.vcodec, + resolution: { width: format.width, height: format.height } + })); + + const best = variants.find(i => matchHlsCodec(i.codecs)); + + const preferred = variants.find(i => + matchHlsCodec(i.codecs) && normalizeQuality(i.resolution) === quality + ); + + let selected = preferred || best; + + if (!selected) { + codec = "h264"; + selected = variants.find(i => matchHlsCodec(i.codecs)); + } + + if (!selected) { + return { error: "youtube.no_matching_format" }; + } + + // download + // youtubedl(selected.url, { + // output: `${title}.${hlsCodecList[codec]?.container || 'mp4'}`, + // format: 'best' + // }); return { - type: "redirect", - urls: qualityDict, + type: "video", + urls: [selected.url], filenameAttributes: { - title, + service: 'youtube', + title: title, + id: o.id, + extension: hlsCodecList[codec]?.container || 'mp4' }, - fileMetadata: {}, - isHLS: true, + fileMetadata: { + codecs: selected.codecs, + resolution: selected.resolution + }, + isHLS: false, }; } catch (e) { console.error(e);