1- import { ClientType , Innertube , Misc , Mixins , Parser , UniversalCache , Utils , YT , YTNodes } from 'youtubei.js'
1+ import { ClientType , Innertube , Misc , Mixins , Parser , Platform , UniversalCache , Utils , YT , YTNodes } from 'youtubei.js'
22import Autolinker from 'autolinker'
33import { IpcChannels , SEARCH_CHAR_LIMIT } from '../../../constants'
44
@@ -20,6 +20,50 @@ const TRACKING_PARAM_NAMES = [
2020 'utm_content' ,
2121]
2222
23+ if ( process . env . SUPPORTS_LOCAL_API ) {
24+ Platform . shim . eval = ( data , env ) => {
25+ return new Promise ( ( resolve ) => {
26+ const properties = [ ]
27+
28+ if ( env . n ) {
29+ properties . push ( `n: exportedVars.nFunction("${ env . n } ")` )
30+ }
31+
32+ if ( env . sig ) {
33+ properties . push ( `sig: exportedVars.sigFunction("${ env . sig } ")` )
34+ }
35+
36+ // Triggers permission errors if we don't remove it (added by YouTube.js), as sessionStorage isn't accessible in sandboxed cross-origin iframes
37+ const modifiedOutput = data . output . replace ( 'const window = Object.assign({}, globalThis);' , '' )
38+
39+ const code = `${ modifiedOutput } \nreturn {${ properties . join ( ', ' ) } }`
40+
41+ // Generate a unique ID, as there may be multiple eval calls going on at the same time (e.g. DASH manifest generation)
42+ const messageId = process . env . IS_ELECTRON || crypto . randomUUID
43+ ? crypto . randomUUID ( )
44+ : `${ Date . now ( ) } -${ Math . floor ( Math . random ( ) * 10000 ) } `
45+
46+ const iframe = document . getElementById ( 'sigFrame' )
47+
48+ /** @param {MessageEvent } event */
49+ const listener = ( event ) => {
50+ if ( event . source === iframe . contentWindow && typeof event . data === 'string' ) {
51+ const data = JSON . parse ( event . data )
52+
53+ if ( data . id === messageId ) {
54+ window . removeEventListener ( 'message' , listener )
55+
56+ resolve ( data . result )
57+ }
58+ }
59+ }
60+
61+ window . addEventListener ( 'message' , listener )
62+ iframe . contentWindow . postMessage ( JSON . stringify ( { id : messageId , code } ) , '*' )
63+ } )
64+ }
65+ }
66+
2367/**
2468 * Creates a lightweight Innertube instance, which is faster to create or
2569 * an instance that can decode the streaming URLs, which is slower to create
@@ -344,12 +388,12 @@ export async function getLocalVideoInfo(id) {
344388 }
345389
346390 if ( info . streaming_data ) {
347- decipherFormats ( info . streaming_data . formats , webInnertube . session . player )
391+ await decipherFormats ( info . streaming_data . formats , webInnertube . session . player )
348392
349393 const firstFormat = info . streaming_data . adaptive_formats [ 0 ]
350394
351395 if ( firstFormat . url || firstFormat . signature_cipher || firstFormat . cipher ) {
352- decipherFormats ( info . streaming_data . adaptive_formats , webInnertube . session . player )
396+ await decipherFormats ( info . streaming_data . adaptive_formats , webInnertube . session . player )
353397 }
354398
355399 if ( info . streaming_data . dash_manifest_url ) {
@@ -405,11 +449,11 @@ export async function getLocalComments(id) {
405449 * @param {Misc.Format[] } formats
406450 * @param {import('youtubei.js').Player } player
407451 */
408- function decipherFormats ( formats , player ) {
452+ async function decipherFormats ( formats , player ) {
409453 for ( const format of formats ) {
410454 // toDash deciphers the format again, so if we overwrite the original URL,
411455 // it breaks because the n param would get deciphered twice and then be incorrect
412- format . freeTubeUrl = format . decipher ( player )
456+ format . freeTubeUrl = await format . decipher ( player )
413457 }
414458}
415459
0 commit comments