Last active
March 25, 2025 15:34
-
-
Save unixfox/32790e4f4381acbfac5c58f44f8787c5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { WebPoSignalOutput } from 'bgutils'; | |
import { BG, buildURL, GOOG_API_KEY, USER_AGENT } from 'bgutils'; | |
import { Innertube, YT, YTNodes, UniversalCache } from 'youtubei.js'; | |
import { JSDOM } from 'jsdom'; | |
const userAgent = USER_AGENT; | |
// @NOTE: Session cache is disabled so we can get a fresh visitor data string. | |
const innertube = await Innertube.create({ | |
user_agent: userAgent, | |
enable_session_cache: false, | |
cache: new UniversalCache(false) | |
}); | |
const visitorData = innertube.session.context.client.visitorData || ''; | |
// #region BotGuard Initialization | |
const dom = new JSDOM('<!DOCTYPE html><html lang="en"><head><title></title></head><body></body></html>', { | |
url: 'https://www.youtube.com/', | |
referrer: 'https://www.youtube.com/', | |
userAgent | |
}); | |
Object.assign(globalThis, { | |
window: dom.window, | |
document: dom.window.document, | |
location: dom.window.location, | |
origin: dom.window.origin | |
}); | |
if (!Reflect.has(globalThis, 'navigator')) { | |
Object.defineProperty(globalThis, 'navigator', { value: dom.window.navigator }); | |
} | |
const challengeResponse = await innertube.getAttestationChallenge('ENGAGEMENT_TYPE_UNBOUND'); | |
if (!challengeResponse.bg_challenge) | |
throw new Error('Could not get challenge'); | |
const interpreterUrl = challengeResponse.bg_challenge.interpreter_url.private_do_not_access_or_else_trusted_resource_url_wrapped_value; | |
const bgScriptResponse = await fetch(`https:${interpreterUrl}`); | |
const interpreterJavascript = await bgScriptResponse.text(); | |
if (interpreterJavascript) { | |
new Function(interpreterJavascript)(); | |
} else throw new Error('Could not load VM'); | |
const botguard = await BG.BotGuardClient.create({ | |
program: challengeResponse.bg_challenge.program, | |
globalName: challengeResponse.bg_challenge.global_name, | |
globalObj: globalThis | |
}); | |
// #endregion | |
// #region WebPO Token Generation | |
const webPoSignalOutput: WebPoSignalOutput = []; | |
const botguardResponse = await botguard.snapshot({ webPoSignalOutput }); | |
const requestKey = 'O43z0dpjhgX20SCx4KAo'; | |
const integrityTokenResponse = await fetch(buildURL('GenerateIT', true), { | |
method: 'POST', | |
headers: { | |
'content-type': 'application/json+protobuf', | |
'x-goog-api-key': GOOG_API_KEY, | |
'x-user-agent': 'grpc-web-javascript/0.1', | |
'user-agent': userAgent | |
}, | |
body: JSON.stringify([ requestKey, botguardResponse ]) | |
}); | |
const response = await integrityTokenResponse.json() as unknown[]; | |
if (typeof response[0] !== 'string') | |
throw new Error('Could not get integrity token'); | |
const integrityTokenBasedMinter = await BG.WebPoMinter.create({ integrityToken: response[0] }, webPoSignalOutput); | |
// #endregion | |
// #region YouTube.js Usage Example | |
const feed = await innertube.getTrending(); | |
const videos = feed.videos | |
.filter((video) => video.type === "Video") | |
.map((value) => ({ value, sort: Math.random() })) | |
.sort((a, b) => a.sort - b.sort) | |
.map(({ value }) => value); | |
const video = videos.find((video) => "id" in video); | |
const videoId = video.id; | |
const contentPoToken = await integrityTokenBasedMinter.mintAsWebsafeString(videoId); | |
const sessionPoToken = await integrityTokenBasedMinter.mintAsWebsafeString(visitorData); | |
const watchEndpoint = new YTNodes.NavigationEndpoint({ watchEndpoint: { videoId } }); | |
const extraRequestArgs = { | |
playbackContext: { | |
contentPlaybackContext: { | |
vis: 0, | |
splay: false, | |
lactMilliseconds: '-1', | |
signatureTimestamp: innertube.session.player?.sts | |
} | |
}, | |
serviceIntegrityDimensions: { | |
poToken: contentPoToken | |
} | |
}; | |
const watchResponse = await watchEndpoint.call(innertube.actions, extraRequestArgs); | |
const videoInfo = new YT.VideoInfo([ watchResponse ], innertube.actions, ''); | |
const audioStreamingURL = `${videoInfo.chooseFormat({ | |
quality: '1080p', | |
type: 'video' | |
}).decipher(innertube.session.player)}&pot=${sessionPoToken}`; | |
console.info('Visitor data:', decodeURIComponent(visitorData)); | |
console.info('Content WebPO Token:', contentPoToken); | |
console.info('Session WebPO Token:', sessionPoToken); | |
console.info('Cold Start WebPO Token:', BG.PoToken.generateColdStartToken(visitorData), '\n'); | |
console.info('Streaming URL:', audioStreamingURL); | |
const respons = await fetch(audioStreamingURL, { method: 'HEAD' }); | |
console.log(`Status Code: ${respons.status}`); | |
// #endregion |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment