mirror of
https://github.com/LuanRT/googlevideo.git
synced 2026-06-13 08:42:31 +00:00
chore: Update examples
This commit is contained in:
61
examples/downloader/package-lock.json
generated
61
examples/downloader/package-lock.json
generated
@@ -15,7 +15,7 @@
|
||||
"googlevideo": "file:../..",
|
||||
"jsdom": "^25.0.1",
|
||||
"shaka-player": "^4.11.2",
|
||||
"youtubei.js": "^15.0.0"
|
||||
"youtubei.js": "^16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cli-progress": "^3.11.6",
|
||||
@@ -25,7 +25,7 @@
|
||||
}
|
||||
},
|
||||
"../..": {
|
||||
"version": "4.0.1",
|
||||
"version": "4.0.4",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
@@ -99,18 +99,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
||||
@@ -520,18 +508,6 @@
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||
},
|
||||
"node_modules/jintr": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/jintr/-/jintr-3.3.1.tgz",
|
||||
"integrity": "sha512-nnOzyhf0SLpbWuZ270Omwbj5LcXUkTcZkVnK8/veJXtSZOiATM5gMZMdmzN75FmTyj+NVgrGaPdH12zIJ24oIA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jsdom": {
|
||||
"version": "25.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz",
|
||||
@@ -581,6 +557,15 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/meriyah": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/meriyah/-/meriyah-6.1.4.tgz",
|
||||
"integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
@@ -742,11 +727,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
|
||||
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
|
||||
@@ -760,15 +740,6 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "6.21.3",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
|
||||
"integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
@@ -878,18 +849,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/youtubei.js": {
|
||||
"version": "15.0.0",
|
||||
"resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-15.0.0.tgz",
|
||||
"integrity": "sha512-giPZREn+q0z8Jr45NUcJUXE7QA2+UD2jx5FR+ULdnexvtHg5uQZr9Am8aYcECPKzbBNe6ksBD1yT4SKNbhpRqA==",
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-16.0.0.tgz",
|
||||
"integrity": "sha512-aMx+ulnrxzsgVsxTr7gbBVnIjti2NQUlMwCoo1/MzICCJS3iMLOPUFdo7bSpwskL6ljzQ/LxmmB4WQC3FtkBlA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^2.0.0",
|
||||
"jintr": "^3.3.1",
|
||||
"tslib": "^2.5.0",
|
||||
"undici": "^6.21.3"
|
||||
"meriyah": "^6.1.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"googlevideo": "file:../..",
|
||||
"jsdom": "^25.0.1",
|
||||
"shaka-player": "^4.11.2",
|
||||
"youtubei.js": "^15.0.0"
|
||||
"youtubei.js": "^16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cli-progress": "^3.11.6",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createWriteStream, type WriteStream } from 'node:fs';
|
||||
import cliProgress from 'cli-progress';
|
||||
import { Constants, Innertube, type IPlayerResponse, UniversalCache, YTNodes } from 'youtubei.js';
|
||||
import { Constants, Innertube, type IPlayerResponse, Platform, UniversalCache, YTNodes } from 'youtubei.js';
|
||||
import type { Types } from 'youtubei.js';
|
||||
|
||||
import { generateWebPoToken } from './webpo-helper.js';
|
||||
import type { SabrFormat } from 'googlevideo/shared-types';
|
||||
@@ -23,6 +24,22 @@ export interface StreamResults {
|
||||
videoTitle: string;
|
||||
}
|
||||
|
||||
Platform.shim.eval = async (data: Types.BuildScriptResult, env: Record<string, Types.VMPrimative>) => {
|
||||
const properties = [];
|
||||
|
||||
if (env.n) {
|
||||
properties.push(`n: exportedVars.nFunction("${env.n}")`);
|
||||
}
|
||||
|
||||
if (env.sig) {
|
||||
properties.push(`sig: exportedVars.sigFunction("${env.sig}")`);
|
||||
}
|
||||
|
||||
const code = `${data.output}\nreturn { ${properties.join(', ')} }`;
|
||||
|
||||
return new Function(code)();
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches video details and streaming information from YouTube.
|
||||
*/
|
||||
@@ -36,7 +53,7 @@ export async function makePlayerRequest(innertube: Innertube, videoId: string, r
|
||||
vis: 0,
|
||||
splay: false,
|
||||
lactMilliseconds: '-1',
|
||||
signatureTimestamp: innertube.session.player?.sts
|
||||
signatureTimestamp: innertube.session.player?.signature_timestamp
|
||||
}
|
||||
},
|
||||
contentCheckOk: true,
|
||||
@@ -150,7 +167,7 @@ export async function createSabrStream(
|
||||
streamResults: StreamResults;
|
||||
}> {
|
||||
const innertube = await Innertube.create({ cache: new UniversalCache(true) });
|
||||
const webPoTokenResult = await generateWebPoToken(innertube.session.context.client.visitorData || '');
|
||||
const webPoTokenResult = await generateWebPoToken(videoId);
|
||||
|
||||
// Get video metadata.
|
||||
const playerResponse = await makePlayerRequest(innertube, videoId);
|
||||
@@ -165,7 +182,7 @@ export async function createSabrStream(
|
||||
`);
|
||||
|
||||
// Now get the streaming information.
|
||||
const serverAbrStreamingUrl = innertube.session.player?.decipher(playerResponse.streaming_data?.server_abr_streaming_url);
|
||||
const serverAbrStreamingUrl = await innertube.session.player?.decipher(playerResponse.streaming_data?.server_abr_streaming_url);
|
||||
const videoPlaybackUstreamerConfig = playerResponse.player_config?.media_common_config.media_ustreamer_request_config?.video_playback_ustreamer_config;
|
||||
|
||||
if (!videoPlaybackUstreamerConfig) throw new Error('ustreamerConfig not found');
|
||||
@@ -188,7 +205,7 @@ export async function createSabrStream(
|
||||
serverAbrStream.on('reloadPlayerResponse', async (reloadPlaybackContext) => {
|
||||
const playerResponse = await makePlayerRequest(innertube, videoId, reloadPlaybackContext);
|
||||
|
||||
const serverAbrStreamingUrl = innertube.session.player?.decipher(playerResponse.streaming_data?.server_abr_streaming_url);
|
||||
const serverAbrStreamingUrl = await innertube.session.player?.decipher(playerResponse.streaming_data?.server_abr_streaming_url);
|
||||
const videoPlaybackUstreamerConfig = playerResponse.player_config?.media_common_config.media_ustreamer_request_config?.video_playback_ustreamer_config;
|
||||
|
||||
if (serverAbrStreamingUrl && videoPlaybackUstreamerConfig) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { BG, type BgConfig } from 'bgutils-js';
|
||||
import { JSDOM } from 'jsdom';
|
||||
|
||||
export async function generateWebPoToken(visitorData: string) {
|
||||
export async function generateWebPoToken(contentBinding: string) {
|
||||
const requestKey = 'O43z0dpjhgX20SCx4KAo';
|
||||
|
||||
if (!visitorData)
|
||||
if (!contentBinding)
|
||||
throw new Error('Could not get visitor data');
|
||||
|
||||
const dom = new JSDOM();
|
||||
@@ -17,7 +17,7 @@ export async function generateWebPoToken(visitorData: string) {
|
||||
const bgConfig: BgConfig = {
|
||||
fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => fetch(input, init),
|
||||
globalObj: globalThis,
|
||||
identifier: visitorData,
|
||||
identifier: contentBinding,
|
||||
requestKey
|
||||
};
|
||||
|
||||
@@ -38,10 +38,10 @@ export async function generateWebPoToken(visitorData: string) {
|
||||
bgConfig
|
||||
});
|
||||
|
||||
const placeholderPoToken = BG.PoToken.generatePlaceholder(visitorData);
|
||||
const placeholderPoToken = BG.PoToken.generatePlaceholder(contentBinding);
|
||||
|
||||
return {
|
||||
visitorData,
|
||||
visitorData: contentBinding,
|
||||
placeholderPoToken,
|
||||
poToken: poTokenResult.poToken,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Innertube, { Constants, type Context, UniversalCache, YT } from 'youtubei.js';
|
||||
import type { Types } from 'youtubei.js';
|
||||
import Innertube, { Constants, type Context, Platform, UniversalCache, YT } from 'youtubei.js';
|
||||
import { UmpReader, CompositeBuffer } from 'googlevideo/ump';
|
||||
|
||||
import {
|
||||
@@ -26,6 +27,22 @@ type ClientConfig = {
|
||||
|
||||
type UmpPartHandler = (part: Part) => void;
|
||||
|
||||
Platform.shim.eval = async (data: Types.BuildScriptResult, env: Record<string, Types.VMPrimative>) => {
|
||||
const properties = [];
|
||||
|
||||
if (env.n) {
|
||||
properties.push(`n: exportedVars.nFunction("${env.n}")`);
|
||||
}
|
||||
|
||||
if (env.sig) {
|
||||
properties.push(`sig: exportedVars.sigFunction("${env.sig}")`);
|
||||
}
|
||||
|
||||
const code = `${data.output}\nreturn { ${properties.join(', ')} }`;
|
||||
|
||||
return new Function(code)();
|
||||
};
|
||||
|
||||
const enableCompression = true;
|
||||
|
||||
/**
|
||||
@@ -87,7 +104,7 @@ async function prepareOnesieRequest(args: OnesieRequestArgs) {
|
||||
vis: 0,
|
||||
splay: false,
|
||||
lactMilliseconds: '-1',
|
||||
signatureTimestamp: innertube.session.player?.sts
|
||||
signatureTimestamp: innertube.session.player?.signature_timestamp
|
||||
}
|
||||
},
|
||||
videoId
|
||||
@@ -302,7 +319,7 @@ const innertube = await Innertube.create({ cache: new UniversalCache(true), retr
|
||||
const videoInfo = await getBasicInfo(innertube, 'JAs6WyK-Kr0');
|
||||
console.log('Basic info:', videoInfo);
|
||||
console.log('Deciphered audio URL:');
|
||||
console.log(videoInfo.chooseFormat({
|
||||
console.log(await videoInfo.chooseFormat({
|
||||
format: 'mp4',
|
||||
quality: 'best',
|
||||
type: 'audio'
|
||||
|
||||
55
examples/onesie-request/package-lock.json
generated
55
examples/onesie-request/package-lock.json
generated
@@ -10,11 +10,11 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"googlevideo": "file:../..",
|
||||
"youtubei.js": "^15.0.0"
|
||||
"youtubei.js": "^16.0.0"
|
||||
}
|
||||
},
|
||||
"../..": {
|
||||
"version": "4.0.1",
|
||||
"version": "4.0.4",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
@@ -42,61 +42,30 @@
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.2.3.tgz",
|
||||
"integrity": "sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/googlevideo": {
|
||||
"resolved": "../..",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/jintr": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/jintr/-/jintr-3.3.1.tgz",
|
||||
"integrity": "sha512-nnOzyhf0SLpbWuZ270Omwbj5LcXUkTcZkVnK8/veJXtSZOiATM5gMZMdmzN75FmTyj+NVgrGaPdH12zIJ24oIA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "6.21.3",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
|
||||
"integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
|
||||
"license": "MIT",
|
||||
"node_modules/meriyah": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/meriyah/-/meriyah-6.1.4.tgz",
|
||||
"integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/youtubei.js": {
|
||||
"version": "15.0.0",
|
||||
"resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-15.0.0.tgz",
|
||||
"integrity": "sha512-giPZREn+q0z8Jr45NUcJUXE7QA2+UD2jx5FR+ULdnexvtHg5uQZr9Am8aYcECPKzbBNe6ksBD1yT4SKNbhpRqA==",
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-16.0.0.tgz",
|
||||
"integrity": "sha512-aMx+ulnrxzsgVsxTr7gbBVnIjti2NQUlMwCoo1/MzICCJS3iMLOPUFdo7bSpwskL6ljzQ/LxmmB4WQC3FtkBlA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^2.0.0",
|
||||
"jintr": "^3.3.1",
|
||||
"tslib": "^2.5.0",
|
||||
"undici": "^6.21.3"
|
||||
"meriyah": "^6.1.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,6 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"googlevideo": "file:../..",
|
||||
"youtubei.js": "^15.0.0"
|
||||
"youtubei.js": "^16.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
49
examples/sabr-shaka-example/package-lock.json
generated
49
examples/sabr-shaka-example/package-lock.json
generated
@@ -12,7 +12,7 @@
|
||||
"bgutils-js": "^3.2.0",
|
||||
"googlevideo": "^4.0.4",
|
||||
"shaka-player": "^4.16.2",
|
||||
"youtubei.js": "^15.1.1"
|
||||
"youtubei.js": "^16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.4.5",
|
||||
@@ -482,18 +482,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bgutils-js": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bgutils-js/-/bgutils-js-3.2.0.tgz",
|
||||
@@ -575,16 +563,13 @@
|
||||
"@bufbuild/protobuf": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jintr": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/jintr/-/jintr-3.3.1.tgz",
|
||||
"integrity": "sha512-nnOzyhf0SLpbWuZ270Omwbj5LcXUkTcZkVnK8/veJXtSZOiATM5gMZMdmzN75FmTyj+NVgrGaPdH12zIJ24oIA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.8.0"
|
||||
"node_modules/meriyah": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/meriyah/-/meriyah-6.1.4.tgz",
|
||||
"integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
@@ -1031,15 +1016,6 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "6.21.3",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
|
||||
"integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.1.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz",
|
||||
@@ -1131,17 +1107,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/youtubei.js": {
|
||||
"version": "15.1.1",
|
||||
"resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-15.1.1.tgz",
|
||||
"integrity": "sha512-fuEDj9Ky6cAQg93BrRVCbr+GTYNZQAIFZrx/a3oDRuGc3Mf5bS0dQfoYwwgjtSV7sgAKQEEdGtzRdBzOc8g72Q==",
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-16.0.0.tgz",
|
||||
"integrity": "sha512-aMx+ulnrxzsgVsxTr7gbBVnIjti2NQUlMwCoo1/MzICCJS3iMLOPUFdo7bSpwskL6ljzQ/LxmmB4WQC3FtkBlA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^2.0.0",
|
||||
"jintr": "^3.3.1",
|
||||
"undici": "^6.21.3"
|
||||
"meriyah": "^6.1.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"bgutils-js": "^3.2.0",
|
||||
"googlevideo": "^4.0.4",
|
||||
"shaka-player": "^4.16.2",
|
||||
"youtubei.js": "^15.1.1"
|
||||
"youtubei.js": "^16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.4.5",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import shaka from 'shaka-player/dist/shaka-player.ui.js';
|
||||
import { Innertube, UniversalCache, YT, Utils, Constants } from 'youtubei.js';
|
||||
import type { Types } from 'youtubei.js/web';
|
||||
import { Constants, Innertube, Platform, UniversalCache, Utils, YT } from 'youtubei.js/web';
|
||||
import { SabrStreamingAdapter } from 'googlevideo/sabr-streaming-adapter';
|
||||
import { buildSabrFormat } from 'googlevideo/utils';
|
||||
import { ShakaPlayerAdapter } from './ShakaPlayerAdapter.js';
|
||||
@@ -16,11 +17,27 @@ const statusElement = document.getElementById('status') as HTMLDivElement;
|
||||
let player: shaka.Player;
|
||||
let sabrAdapter: SabrStreamingAdapter;
|
||||
let innertube: Innertube;
|
||||
let sessionPoTokenContentBinding: string | undefined;
|
||||
let sessionPoTokenCreationLock = false;
|
||||
let sessionPoToken: string | undefined;
|
||||
let playbackWebPoTokenContentBinding: string | undefined;
|
||||
let playbackWebPoTokenCreationLock = false;
|
||||
let playbackWebPoToken: string | undefined;
|
||||
let coldStartToken: string | undefined;
|
||||
|
||||
Platform.shim.eval = async (data: Types.BuildScriptResult, env: Record<string, Types.VMPrimative>) => {
|
||||
const properties = [];
|
||||
|
||||
if (env.n) {
|
||||
properties.push(`n: exportedVars.nFunction("${env.n}")`);
|
||||
}
|
||||
|
||||
if (env.sig) {
|
||||
properties.push(`sig: exportedVars.sigFunction("${env.sig}")`);
|
||||
}
|
||||
|
||||
const code = `${data.output}\nreturn { ${properties.join(', ')} }`;
|
||||
|
||||
return new Function(code)();
|
||||
};
|
||||
|
||||
async function main() {
|
||||
shaka.polyfill.installAll();
|
||||
|
||||
@@ -38,8 +55,6 @@ async function main() {
|
||||
|
||||
botguardService.init().then(() => console.info('[Main]', 'BotGuard client initialized'));
|
||||
|
||||
sessionPoTokenContentBinding = innertube.session.context.client.visitorData;
|
||||
|
||||
console.log('[Main] Innertube initialized');
|
||||
|
||||
// Now init the player.
|
||||
@@ -77,8 +92,7 @@ async function main() {
|
||||
volumeContainer[0].addEventListener('mousewheel', (event) => {
|
||||
event.preventDefault();
|
||||
const delta = Math.sign((event as any).deltaY);
|
||||
const newVolume = Math.max(0, Math.min(1, videoElement.volume - delta * 0.05));
|
||||
videoElement.volume = newVolume;
|
||||
videoElement.volume = Math.max(0, Math.min(1, videoElement.volume - delta * 0.05));
|
||||
});
|
||||
|
||||
console.log('[Main] Shaka Player initialized');
|
||||
@@ -94,6 +108,9 @@ async function loadVideo(videoId: string) {
|
||||
alert('Please enter a video ID.');
|
||||
return;
|
||||
}
|
||||
|
||||
playbackWebPoToken = undefined;
|
||||
playbackWebPoTokenContentBinding = videoId;
|
||||
|
||||
statusElement.textContent = `Loading video: ${videoId}...`;
|
||||
console.log('[Player]', `Loading video: ${videoId}`);
|
||||
@@ -116,7 +133,7 @@ async function loadVideo(videoId: string) {
|
||||
pyv: true
|
||||
},
|
||||
contentPlaybackContext: {
|
||||
signatureTimestamp: innertube.session.player?.sts
|
||||
signatureTimestamp: innertube.session.player?.signature_timestamp
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -144,18 +161,18 @@ async function loadVideo(videoId: string) {
|
||||
});
|
||||
|
||||
sabrAdapter.onMintPoToken(async () => {
|
||||
if (!sessionPoToken) {
|
||||
if (!playbackWebPoToken) {
|
||||
// For live streams, we must block and wait for the PO token as it's sometimes required for playback to start.
|
||||
// For VODs, we can mint the token in the background to avoid delaying playback, as it's not immediately required.
|
||||
// While BotGuard is pretty darn fast, it still makes a difference in user experience (from my own testing).
|
||||
if (isLive) {
|
||||
await mintSessionPoToken();
|
||||
await mintContentWebPO();
|
||||
} else {
|
||||
mintSessionPoToken().then();
|
||||
mintContentWebPO().then();
|
||||
}
|
||||
}
|
||||
|
||||
return sessionPoToken || coldStartToken || '';
|
||||
return playbackWebPoToken || coldStartToken || '';
|
||||
});
|
||||
|
||||
sabrAdapter.onReloadPlayerResponse(async (reloadContext) => {
|
||||
@@ -170,21 +187,21 @@ async function loadVideo(videoId: string) {
|
||||
pyv: true
|
||||
},
|
||||
contentPlaybackContext: {
|
||||
signatureTimestamp: innertube.session.player?.sts
|
||||
signatureTimestamp: innertube.session.player?.signature_timestamp
|
||||
},
|
||||
reloadPlaybackContext: reloadContext
|
||||
}
|
||||
});
|
||||
|
||||
const parsedInfo = new YT.VideoInfo([ reloadedInfo ], innertube.actions, cpn);
|
||||
sabrAdapter.setStreamingURL(innertube.session.player!.decipher(parsedInfo.streaming_data?.server_abr_streaming_url));
|
||||
sabrAdapter.setStreamingURL(await innertube.session.player!.decipher(parsedInfo.streaming_data?.server_abr_streaming_url));
|
||||
sabrAdapter.setUstreamerConfig(videoInfo.player_config?.media_common_config.media_ustreamer_request_config?.video_playback_ustreamer_config);
|
||||
});
|
||||
|
||||
sabrAdapter.attach(player);
|
||||
|
||||
if (videoInfo.streaming_data && !isPostLiveDVR && !isLive) {
|
||||
sabrAdapter.setStreamingURL(innertube.session.player!.decipher(videoInfo.streaming_data?.server_abr_streaming_url));
|
||||
sabrAdapter.setStreamingURL(await innertube.session.player!.decipher(videoInfo.streaming_data?.server_abr_streaming_url));
|
||||
sabrAdapter.setUstreamerConfig(videoInfo.player_config?.media_common_config.media_ustreamer_request_config?.video_playback_ustreamer_config);
|
||||
sabrAdapter.setServerAbrFormats(videoInfo.streaming_data.adaptive_formats.map(buildSabrFormat));
|
||||
}
|
||||
@@ -219,24 +236,24 @@ async function loadVideo(videoId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
async function mintSessionPoToken() {
|
||||
if (!sessionPoTokenContentBinding || sessionPoTokenCreationLock) return;
|
||||
async function mintContentWebPO() {
|
||||
if (!playbackWebPoTokenContentBinding || playbackWebPoTokenCreationLock) return;
|
||||
|
||||
sessionPoTokenCreationLock = true;
|
||||
playbackWebPoTokenCreationLock = true;
|
||||
try {
|
||||
coldStartToken = botguardService.mintColdStartToken(sessionPoTokenContentBinding);
|
||||
console.info('[Player]', `Cold start token created (Content binding: ${decodeURIComponent(sessionPoTokenContentBinding)})`);
|
||||
coldStartToken = botguardService.mintColdStartToken(playbackWebPoTokenContentBinding);
|
||||
console.info('[Player]', `Cold start token created (Content binding: ${decodeURIComponent(playbackWebPoTokenContentBinding)})`);
|
||||
|
||||
if (!botguardService.isInitialized()) await botguardService.reinit();
|
||||
|
||||
if (botguardService.integrityTokenBasedMinter) {
|
||||
sessionPoToken = await botguardService.integrityTokenBasedMinter.mintAsWebsafeString(decodeURIComponent(sessionPoTokenContentBinding));
|
||||
console.info('[Player]', `Session PO token created (Content binding: ${decodeURIComponent(sessionPoTokenContentBinding)})`);
|
||||
playbackWebPoToken = await botguardService.integrityTokenBasedMinter.mintAsWebsafeString(decodeURIComponent(playbackWebPoTokenContentBinding));
|
||||
console.info('[Player]', `WebPO token created (Content binding: ${decodeURIComponent(playbackWebPoTokenContentBinding)})`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Player]', 'Error minting session PO token', err);
|
||||
console.error('[Player]', 'Error minting WebPO token', err);
|
||||
} finally {
|
||||
sessionPoTokenCreationLock = false;
|
||||
playbackWebPoTokenCreationLock = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user