More progress

This commit is contained in:
WardPearce
2025-02-24 18:14:42 +13:00
parent 1f5520dce1
commit 989a8d7504
3 changed files with 45 additions and 106 deletions
+11 -11
View File
@@ -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"
}
+2 -2
View File
@@ -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"
}
}
}
+32 -93
View File
@@ -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;
}
}
});