diff --git a/package.json b/package.json index af98af22103a0..1ba6d1530188b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "freetube", "productName": "FreeTube", "description": "A private YouTube client", - "version": "0.23.7", + "version": "0.23.8", "license": "AGPL-3.0-or-later", "main": "./dist/main.js", "private": true, diff --git a/src/renderer/helpers/api/local.js b/src/renderer/helpers/api/local.js index 0325a8ec5fee0..660630e4a5bb3 100644 --- a/src/renderer/helpers/api/local.js +++ b/src/renderer/helpers/api/local.js @@ -59,13 +59,49 @@ async function createInnertube({ withPlayer = false, location = undefined, safet client_type: clientType, // use browser fetch - fetch: (input, init) => { - if (input.url?.startsWith('https://www.youtube.com/youtubei/v1/player')) { - init.body = init.body.replace('"videoId":', '"params":"8AEB","videoId":') - } + fetch: !withPlayer + ? (input, init) => fetch(input, init) + : async (input, init) => { + if (input.url?.startsWith('https://www.youtube.com/youtubei/v1/player') && init?.headers?.get('X-Youtube-Client-Name') === '2') { + const response = await fetch(input, init) + + const responseText = await response.text() + + const json = JSON.parse(responseText) + + if (Array.isArray(json.adSlots)) { + let waitSeconds = 0 + + for (const adSlot of json.adSlots) { + if (adSlot.adSlotRenderer?.adSlotMetadata?.triggerEvent === 'SLOT_TRIGGER_EVENT_BEFORE_CONTENT') { + const playerVars = adSlot.adSlotRenderer.fulfillmentContent?.fulfilledLayout?.playerBytesAdLayoutRenderer + ?.renderingContent?.instreamVideoAdRenderer?.playerVars + + if (playerVars) { + const match = playerVars.match(/length_seconds=([\d.]+)/) + + if (match) { + waitSeconds += parseFloat(match[1]) + } + } + } + } + + if (waitSeconds > 0) { + await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000)) + } + } + + // Need to return a new response object, as you can only read the response body once. + return new Response(responseText, { + status: response.status, + statusText: response.statusText, + headers: response.headers + }) + } - return fetch(input, init) - }, + return fetch(input, init) + }, cache, generate_session_locally: !!generateSessionLocally })