From c6d0e0bdd51b7a25a2c255c13f7cb38f135ac781 Mon Sep 17 00:00:00 2001 From: wukko Date: Mon, 23 Dec 2024 22:58:16 +0600 Subject: [PATCH] api/youtube: use poToken, visitorData, and web client with cookies and also decipher media whenever needed, but only if cookies are used --- api/src/processing/services/youtube.js | 47 +++++++++++++++++++++----- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/api/src/processing/services/youtube.js b/api/src/processing/services/youtube.js index 6e61efa9..cb18aa06 100644 --- a/api/src/processing/services/youtube.js +++ b/api/src/processing/services/youtube.js @@ -48,7 +48,7 @@ const transformSessionData = (cookie) => { return; const values = { ...cookie.values() }; - const REQUIRED_VALUES = [ 'access_token', 'refresh_token' ]; + const REQUIRED_VALUES = ['access_token', 'refresh_token']; if (REQUIRED_VALUES.some(x => typeof values[x] !== 'string')) { return; @@ -66,12 +66,18 @@ const transformSessionData = (cookie) => { const cloneInnertube = async (customFetch) => { const shouldRefreshPlayer = lastRefreshedAt + PLAYER_REFRESH_PERIOD < new Date(); - const cookie = getCookie('youtube')?.toString(); + + const rawCookie = getCookie('youtube'); + const cookieValues = rawCookie?.values(); + const cookie = rawCookie?.toString(); + if (!innertube || shouldRefreshPlayer) { innertube = await Innertube.create({ fetch: customFetch, - retrieve_player: false, - cookie + retrieve_player: !!cookie, + cookie, + po_token: cookieValues?.po_token, + visitor_data: cookieValues?.visitor_data, }); lastRefreshedAt = +new Date(); } @@ -117,7 +123,7 @@ const cloneInnertube = async (customFetch) => { return yt; } -export default async function(o) { +export default async function (o) { let yt; try { yt = await cloneInnertube( @@ -134,6 +140,8 @@ export default async function(o) { } else throw e; } + const cookie = getCookie('youtube')?.toString(); + let useHLS = o.youtubeHLS; // HLS playlists don't contain the av1 video format, at least with the iOS client @@ -141,9 +149,20 @@ export default async function(o) { useHLS = false; } + let innertubeClient = "ANDROID"; + + if (cookie) { + useHLS = false; + innertubeClient = "WEB"; + } + + if (useHLS) { + innertubeClient = "IOS"; + } + let info; try { - info = await yt.getBasicInfo(o.id, useHLS ? 'IOS' : 'ANDROID'); + info = await yt.getBasicInfo(o.id, innertubeClient); } catch (e) { if (e?.info) { const errorInfo = JSON.parse(e?.info); @@ -168,7 +187,7 @@ export default async function(o) { const playability = info.playability_status; const basicInfo = info.basic_info; - switch(playability.status) { + switch (playability.status) { case "LOGIN_REQUIRED": if (playability.reason.endsWith("bot")) { return { error: "youtube.login" } @@ -243,7 +262,7 @@ export default async function(o) { } else { throw new Error("couldn't fetch the HLS playlist"); } - }).catch(() => {}); + }).catch(() => { }); if (!fetchedHlsManifest) { return { error: "youtube.no_hls_streams" }; @@ -324,7 +343,7 @@ export default async function(o) { } const checkFormat = (format, pCodec) => format.content_length && - (format.mime_type.includes(codecList[pCodec].videoCodec) + (format.mime_type.includes(codecList[pCodec].videoCodec) || format.mime_type.includes(codecList[pCodec].audioCodec)); // sort formats & weed out bad ones @@ -438,6 +457,10 @@ export default async function(o) { urls = audio.uri; } + if (innertubeClient === "WEB" && innertube) { + urls = audio.decipher(innertube.session.player); + } + return { type: "audio", isAudioOnly: true, @@ -464,11 +487,17 @@ export default async function(o) { width: video.width, height: video.height, }); + filenameAttributes.resolution = `${video.width}x${video.height}`; filenameAttributes.extension = codecList[codec].container; video = video.url; audio = audio.url; + + if (innertubeClient === "WEB" && innertube) { + video = video.decipher(innertube.session.player); + audio = audio.decipher(innertube.session.player); + } } filenameAttributes.qualityLabel = `${resolution}p`;