From 989a8d75047b6f974480a70dfe824ca497586c75 Mon Sep 17 00:00:00 2001 From: WardPearce Date: Mon, 24 Feb 2025 18:14:42 +1300 Subject: [PATCH] More progress --- materialious/package-lock.json | 22 +-- materialious/package.json | 4 +- materialious/src/lib/components/Player.svelte | 125 +++++------------- 3 files changed, 45 insertions(+), 106 deletions(-) diff --git a/materialious/package-lock.json b/materialious/package-lock.json index 6e9be708..a8a940a8 100644 --- a/materialious/package-lock.json +++ b/materialious/package-lock.json @@ -41,7 +41,7 @@ "svelte-infinite-loading": "^1.4.0", "svelte-persisted-store": "^0.12.0", "terser": "^5.34.1", - "youtubei.js": "^12.2.0" + "youtubei.js": "^13.1.0" }, "devDependencies": { "@capacitor/assets": "^3.0.5", @@ -7241,17 +7241,17 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -15092,16 +15092,16 @@ } }, "node_modules/youtubei.js": { - "version": "12.2.0", - "resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-12.2.0.tgz", - "integrity": "sha512-G+50qrbJCToMYhu8jbaHiS3Vf+RRul+CcDbz3hEGwHkGPh+zLiWwD6SS+YhYF+2/op4ZU5zDYQJrGqJ+wKh7Gw==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-13.1.0.tgz", + "integrity": "sha512-uL4TyojAYET0c5NGFD7+ScCod/k8Pc/B+D5tLrunFcz1GaBjRMOGRPcNGaRmnhwisegU7ibtw0iUxCN+BZ0ang==", "funding": [ "https://github.com/sponsors/LuanRT" ], "license": "MIT", "dependencies": { "@bufbuild/protobuf": "^2.0.0", - "jintr": "^3.2.0", + "jintr": "^3.2.1", "tslib": "^2.5.0", "undici": "^5.19.1" } diff --git a/materialious/package.json b/materialious/package.json index 468d3a2a..3ede2e8f 100644 --- a/materialious/package.json +++ b/materialious/package.json @@ -74,6 +74,6 @@ "svelte-infinite-loading": "^1.4.0", "svelte-persisted-store": "^0.12.0", "terser": "^5.34.1", - "youtubei.js": "^12.2.0" + "youtubei.js": "^13.1.0" } -} +} \ No newline at end of file diff --git a/materialious/src/lib/components/Player.svelte b/materialious/src/lib/components/Player.svelte index 8163aad6..d159d365 100644 --- a/materialious/src/lib/components/Player.svelte +++ b/materialious/src/lib/components/Player.svelte @@ -7,7 +7,6 @@ import { StatusBar, Style } from '@capacitor/status-bar'; import { NavigationBar } from '@hugotomazi/capacitor-navigation-bar'; import { type Page } from '@sveltejs/kit'; - import GoogleVideo, { Protos } from 'googlevideo'; import 'shaka-player/dist/controls.css'; import shaka from 'shaka-player/dist/shaka-player.ui'; import { SponsorBlock, type Category, type Segment } from 'sponsorblock-api'; @@ -51,6 +50,8 @@ let sponsorBlockElements: Element[] = []; let watchProgressTimeout: NodeJS.Timeout; + const HTTP_IN_HEX = 0x68747470; + let player: shaka.Player; let playerElement: HTMLMediaElement; @@ -121,7 +122,6 @@ url.searchParams.set('ump', '1'); url.searchParams.set('srfvp', '1'); url.searchParams.set('pot', get(poTokenCacheStore)); - request.headers = {}; delete headers.Range; } } @@ -133,107 +133,46 @@ request.uris[0] = url.toString(); }); - networkingEngine.registerResponseFilter(async (type, response) => { - let mediaData = new Uint8Array(0); + networkingEngine.registerResponseFilter(async (type, response, context) => { + if ( + response.data && + response.data.byteLength > 4 && + new DataView( + response.data as ArrayBufferLike & { BYTES_PER_ELEMENT?: undefined } + ).getUint32(0) === HTTP_IN_HEX + ) { + // Interpret the response data as a URL string. + const responseAsString = shaka.util.StringUtils.fromUTF8(response.data); - const handleRedirect = async (redirectData: Protos.SabrRedirect) => { - const redirectRequest = shaka.net.NetworkingEngine.makeRequest( - [redirectData.url!], - player!.getConfiguration().streaming.retryParameters - ); - const requestOperation = player!.getNetworkingEngine()!.request(type, redirectRequest); + const retryParameters = player.getConfiguration().streaming.retryParameters; + + // Make another request for the redirect URL. + const uris = [responseAsString]; + const redirectRequest = shaka.net.NetworkingEngine.makeRequest(uris, retryParameters); + const requestOperation = networkingEngine.request(type, redirectRequest, context); const redirectResponse = await requestOperation.promise; + // Modify the original response to contain the results of the redirect + // response. response.data = redirectResponse.data; response.headers = redirectResponse.headers; response.uri = redirectResponse.uri; - }; + } else { + const url = new URL(response.uri); - const handleMediaData = async (data: Uint8Array) => { - const combinedLength = mediaData.length + data.length; - const tempMediaData = new Uint8Array(combinedLength); - - tempMediaData.set(mediaData); - tempMediaData.set(data, mediaData.length); - - mediaData = tempMediaData; - }; - - if (type === shaka.net.NetworkingEngine.RequestType.SEGMENT) { + // Fix positioning for auto-generated subtitles if ( - response.data && - response.data.byteLength > 4 && - new DataView( - response.data as ArrayBufferLike & { - BYTES_PER_ELEMENT?: never; - } - ).getUint32(0) === 0x68747470 + url.hostname.endsWith('.youtube.com') && + url.pathname === '/api/timedtext' && + url.searchParams.get('caps') === 'asr' && + url.searchParams.get('kind') === 'asr' && + url.searchParams.get('fmt') === 'vtt' ) { - const googUmp = new GoogleVideo.UMP( - new GoogleVideo.ChunkedDataBuffer([new Uint8Array(response.data as ArrayBuffer)]) - ); + const stringBody = new TextDecoder().decode(response.data); + // position:0% for LTR text and position:100% for RTL text + const cleaned = stringBody.replaceAll(/ align:start position:(?:10)?0%$/gm, ''); - let redirect: Protos.SabrRedirect | undefined; - - googUmp.parse((part) => { - try { - const data = part.data.chunks[0]; - switch (part.type) { - case 20: { - const mediaHeader = Protos.MediaHeader.decode(data); - console.info('[MediaHeader]:', mediaHeader); - break; - } - case 21: { - handleMediaData(part.data.split(1).remainingBuffer.chunks[0]); - break; - } - case 43: { - redirect = Protos.SabrRedirect.decode(data); - console.info('[SABRRedirect]:', redirect); - break; - } - case 58: { - const streamProtectionStatus = Protos.StreamProtectionStatus.decode(data); - switch (streamProtectionStatus.status) { - case 1: - console.info('[StreamProtectionStatus]: Ok'); - break; - case 2: - console.error('[StreamProtectionStatus]: Attestation pending'); - break; - case 3: - console.error('[StreamProtectionStatus]: Attestation required'); - break; - default: - break; - } - break; - } - } - } catch (error) { - console.error('An error occurred while processing the part:', error); - } - }); - - if (redirect) return handleRedirect(redirect); - - if (mediaData.length) response.data = mediaData; - } else { - const url = new URL(response.uri); - if ( - url.hostname.endsWith('.youtube.com') && - url.pathname === '/api/timedtext' && - url.searchParams.get('caps') === 'asr' && - url.searchParams.get('kind') === 'asr' && - url.searchParams.get('fmt') === 'vtt' - ) { - const stringBody = new TextDecoder().decode(response.data); - // position:0% for LTR text and position:100% for RTL text - const cleaned = stringBody.replaceAll(/ align:start position:(?:10)?0%$/gm, ''); - - response.data = new TextEncoder().encode(cleaned).buffer as ArrayBuffer; - } + response.data = new TextEncoder().encode(cleaned).buffer as ArrayBuffer; } } });