@@ -109,6 +109,7 @@ async function createInnertube({ withPlayer = false, location = undefined, safet
109109 user_agent : navigator . userAgent ,
110110
111111 retrieve_player : ! ! withPlayer ,
112+ player_id : '9f4cc5e4' ,
112113 location : location ,
113114 enable_safety_mode : ! ! safetyMode ,
114115 client_type : clientType ,
@@ -412,15 +413,11 @@ export async function getLocalVideoInfo(id) {
412413 }
413414
414415 if ( info . streaming_data . dash_manifest_url ) {
415- let url = info . streaming_data . dash_manifest_url
416-
417- if ( url . includes ( '?' ) ) {
418- url += `&pot=${ encodeURIComponent ( contentPoToken ) } &mpd_version=7`
419- } else {
420- url += `${ url . endsWith ( '/' ) ? '' : '/' } pot/${ encodeURIComponent ( contentPoToken ) } /mpd_version/7`
421- }
422-
423- info . streaming_data . dash_manifest_url = url
416+ info . streaming_data . dash_manifest_url = await decipherDashManifestUrl (
417+ info . streaming_data . dash_manifest_url ,
418+ webInnertube . session . player ,
419+ contentPoToken
420+ )
424421 }
425422 }
426423
@@ -472,6 +469,49 @@ async function decipherFormats(formats, player) {
472469 }
473470}
474471
472+ /**
473+ * @param {string } url
474+ * @param {import('youtubei.js').Player } player
475+ * @param {string } poToken
476+ */
477+ async function decipherDashManifestUrl ( url , player , poToken ) {
478+ const urlObject = new URL ( url )
479+
480+ if ( urlObject . searchParams . size > 0 ) {
481+ urlObject . searchParams . set ( 'pot' , poToken )
482+ urlObject . searchParams . set ( 'mpd_version' , '7' )
483+
484+ return await player . decipher ( urlObject . toString ( ) )
485+ }
486+
487+ // Convert path params to query params
488+ const pathParts = urlObject . pathname
489+ . replace ( '/api/manifest/dash' , '' )
490+ . split ( '/' )
491+ . filter ( part => part . length > 0 )
492+
493+ urlObject . pathname = '/api/manifest/dash'
494+
495+ for ( let i = 0 ; i + 1 < pathParts . length ; i += 2 ) {
496+ urlObject . searchParams . set ( pathParts [ i ] , decodeURIComponent ( pathParts [ i + 1 ] ) )
497+ }
498+
499+ // decipher
500+ const deciphered = await player . decipher ( urlObject . toString ( ) )
501+
502+ // convert query parameters back to path parameters
503+ const decipheredUrlObject = new URL ( deciphered )
504+
505+ for ( const [ key , value ] of decipheredUrlObject . searchParams ) {
506+ decipheredUrlObject . pathname += `/${ key } /${ encodeURIComponent ( value ) } `
507+ }
508+
509+ decipheredUrlObject . search = ''
510+ decipheredUrlObject . pathname += `/pot/${ encodeURIComponent ( poToken ) } /mpd_version/7`
511+
512+ return decipheredUrlObject . toString ( )
513+ }
514+
475515export async function getLocalChannelId ( url ) {
476516 try {
477517 const innertube = await createInnertube ( )
0 commit comments