diff --git a/src/Innertube.ts b/src/Innertube.ts index e924cab1..396ae52f 100644 --- a/src/Innertube.ts +++ b/src/Innertube.ts @@ -70,12 +70,16 @@ class Innertube { /** * Retrieves video info. */ - async getInfo(video_id: string) { + async getInfo(video_id: string | undefined) { throwIfMissing({ video_id }); const cpn = generateRandomString(16); - const initial_info = this.actions.getVideoInfo(video_id, cpn); + const initial_info = await this.actions.execute('/player', { + client: 'ANDROID', + videoId: 'mFWf4Tb5m6Y' + }); + const continuation = this.actions.next({ video_id }); const response = await Promise.all([ initial_info, continuation ]); @@ -85,11 +89,15 @@ class Innertube { /** * Retrieves basic video info. */ - async getBasicInfo(video_id: string) { + async getBasicInfo(video_id: string | undefined) { throwIfMissing({ video_id }); const cpn = generateRandomString(16); - const response = await this.actions.getVideoInfo(video_id, cpn); + + const response = await this.actions.execute('/player', { + client: 'ANDROID', + videoId: 'mFWf4Tb5m6Y' + }); return new VideoInfo([ response ], this.actions, this.session.player, cpn); } @@ -238,11 +246,11 @@ class Innertube { * * If you wish to retrieve the video info too, have a look at {@link getBasicInfo} or {@link getInfo}. */ - async download(video_id: string, options?: DownloadOptions) { + async download(video_id: string | undefined, options?: DownloadOptions) { throwIfMissing({ video_id }); const info = await this.getBasicInfo(video_id); return info.download(options); } } -export default Innertube; +export default Innertube; \ No newline at end of file diff --git a/src/core/Player.ts b/src/core/Player.ts index f742a770..06e0b047 100644 --- a/src/core/Player.ts +++ b/src/core/Player.ts @@ -4,19 +4,23 @@ import { getRandomUserAgent, getStringBetweenStrings, PlayerError } from '../uti import Constants from '../utils/Constants'; import UniversalCache from '../utils/Cache'; -import NToken from '../deciphers/NToken'; -import Signature from '../deciphers/Signature'; + +// Import NToken from '../deciphers/NToken'; +// Import Signature from '../deciphers/Signature'; export default class Player { - #ntoken; - #signature; + // #ntoken; + // #signature; + #signature_timestamp; #player_id; - constructor(signature: Signature, ntoken: NToken, signature_timestamp: number, player_id: string) { - this.#ntoken = ntoken; - this.#signature = signature; + constructor(signature_timestamp: number, player_id: string) { + // This.#ntoken = ntoken; + // This.#signature = signature; + this.#signature_timestamp = signature_timestamp; + this.#player_id = player_id; } @@ -56,13 +60,15 @@ export default class Player { const player_js = await player_res.text(); const sig_timestamp = this.extractSigTimestamp(player_js); - const sig_decipher_sc = this.extractSigDecipherSc(player_js); - const ntoken_sc = this.extractNTokenSc(player_js); - return await Player.fromSource(cache, sig_timestamp, sig_decipher_sc, ntoken_sc, player_id); + // Const sig_decipher_sc = this.extractSigDecipherSc(player_js); + // Const ntoken_sc = this.extractNTokenSc(player_js); + + return await Player.fromSource(cache, sig_timestamp, player_id); } - decipher(url?: string, signature_cipher?: string, cipher?: string) { + /* + Decipher(url?: string, signature_cipher?: string, cipher?: string) { url = url || signature_cipher || cipher; if (!url) @@ -90,7 +96,7 @@ export default class Player { } return url_components.toString(); - } + }*/ static async fromCache(cache: UniversalCache, player_id: string) { const buffer = await cache.get(player_id); @@ -105,15 +111,18 @@ export default class Player { return null; const sig_timestamp = view.getUint32(4, true); - const sig_decipher_len = view.getUint32(8, true); - const sig_decipher_buf = buffer.slice(12, 12 + sig_decipher_len); - const ntoken_transform_buf = buffer.slice(12 + sig_decipher_len); - return new Player(Signature.fromArrayBuffer(sig_decipher_buf), NToken.fromArrayBuffer(ntoken_transform_buf), sig_timestamp, player_id); + /* + * Const sig_decipher_len = view.getUint32(8, true); + * const sig_decipher_buf = buffer.slice(12, 12 + sig_decipher_len); + * const ntoken_transform_buf = buffer.slice(12 + sig_decipher_len); + */ + + return new Player(sig_timestamp, player_id); } - static async fromSource(cache: UniversalCache | undefined, sig_timestamp: number, sig_decipher_sc: string, ntoken_sc: string, player_id: string) { - const player = new Player(Signature.fromSourceCode(sig_decipher_sc), NToken.fromSourceCode(ntoken_sc), sig_timestamp, player_id); + static async fromSource(cache: UniversalCache | undefined, sig_timestamp: number, player_id: string) { + const player = new Player(sig_timestamp, player_id); await player.cache(cache); return player; } @@ -121,17 +130,21 @@ export default class Player { async cache(cache?: UniversalCache) { if (!cache) return; - const ntoken_buf = this.#ntoken.toArrayBuffer(); - const sig_decipher_buf = this.#signature.toArrayBuffer(); - const buffer = new ArrayBuffer(12 + sig_decipher_buf.byteLength + ntoken_buf.byteLength); + /** + * Const ntoken_buf = this.#ntoken.toArrayBuffer(); + * const sig_decipher_buf = this.#signature.toArrayBuffer(); + */ + + const buffer = new ArrayBuffer(12 /* + sig_decipher_buf.byteLength + ntoken_buf.byteLength */); const view = new DataView(buffer); view.setUint32(0, Player.LIBRARY_VERSION, true); view.setUint32(4, this.#signature_timestamp, true); - view.setUint32(8, sig_decipher_buf.byteLength, true); - new Uint8Array(buffer).set(new Uint8Array(sig_decipher_buf), 12); - new Uint8Array(buffer).set(new Uint8Array(ntoken_buf), 12 + sig_decipher_buf.byteLength); + // View.setUint32(8, sig_decipher_buf.byteLength, true); + + // New Uint8Array(buffer).set(new Uint8Array(sig_decipher_buf), 12); + // New Uint8Array(buffer).set(new Uint8Array(ntoken_buf), 12 + sig_decipher_buf.byteLength); await cache.set(this.#player_id, new Uint8Array(buffer)); } @@ -162,6 +175,7 @@ export default class Player { static extractNTokenSc(data: string) { const sc = `var b=a.split("")${getStringBetweenStrings(data, 'b=a.split("")', '}return b.join("")}')}} return b.join("");`; + console.log(sc); if (!sc) throw new PlayerError('Failed to extract n-token decipher algorithm'); diff --git a/src/parser/classes/misc/Format.js b/src/parser/classes/misc/Format.js index f0cb336c..a529b645 100644 --- a/src/parser/classes/misc/Format.js +++ b/src/parser/classes/misc/Format.js @@ -36,12 +36,11 @@ class Format { /** * Decipher the streaming url of the format. - * - * @param {import('../../../core/Player').default} player * @returns {string} Deciphered URL for downloading */ - decipher(player) { - return player.decipher(this.url, this.signature_cipher, this.cipher); + decipher() { + return this.url; + // Return player.decipher(this.url, this.signature_cipher, this.cipher); } } diff --git a/src/parser/youtube/VideoInfo.ts b/src/parser/youtube/VideoInfo.ts index 31d83a4a..6f6de94b 100644 --- a/src/parser/youtube/VideoInfo.ts +++ b/src/parser/youtube/VideoInfo.ts @@ -6,7 +6,6 @@ import Player from '../../core/Player'; import TwoColumnWatchNextResults from '../classes/TwoColumnWatchNextResults'; import VideoPrimaryInfo from '../classes/VideoPrimaryInfo'; import VideoSecondaryInfo from '../classes/VideoSecondaryInfo'; -import PlayerMicroformat from '../classes/PlayerMicroformat'; import Format from '../classes/misc/Format'; import MerchandiseShelf from '../classes/MerchandiseShelf'; @@ -96,22 +95,8 @@ class VideoInfo { if (info.playability_status?.status === 'ERROR') throw new InnertubeError('This video is unavailable', info.playability_status); - if (!info.microformat?.is(PlayerMicroformat)) - throw new InnertubeError('Invalid microformat', info.microformat); - this.basic_info = { // This type is inferred so no need for an explicit type ...info.video_details, - ...{ - /** - * Microformat is a bit redundant, so only - * a few things there are interesting to us. - */ - embed: info.microformat?.embed, - channel: info.microformat?.channel, - is_unlisted: info.microformat?.is_unlisted, - is_family_safe: info.microformat?.is_family_safe, - has_ypc_metadata: info.microformat?.has_ypc_metadata - }, like_count: undefined as number | undefined, is_liked: undefined as boolean | undefined, is_disliked: undefined as boolean | undefined @@ -438,7 +423,7 @@ class VideoInfo { if (!format.index_range || !format.init_range) throw new InnertubeError('Index and init ranges not available', { format }); - const url = new URL(format.decipher(this.#player)); + const url = new URL(format.decipher()); url.searchParams.set('cpn', this.#cpn); set.appendChild(this.#el(document, 'Representation', { diff --git a/test/main.test.js b/test/main.test.js index b6891908..fe8f2397 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -83,24 +83,6 @@ describe('YouTube.js Tests', () => { expect(result).toBeTruthy(); }, 30000); }); - - /* - // TODO: fix this, doesn't run on node 12 - const { default: NToken } = require('../../src/deciphers/NToken'); - const { default: Signature} = require('../../src/deciphers/Signature'); - - describe('Deciphers', () => { - it('Should decipher signature', () => { - const result = Signature.fromSourceCode(Constants.DECIPHERS.SIG.ALGORITHM).decipher(Constants.DECIPHERS.SIG.ORIGINAL_URL); - expect(result).toEqual(Constants.DECIPHERS.SIG.DECIPHERED_URL); - }); - - it('Should decipher ntoken', () => { - const result = NToken.fromSourceCode(Constants.DECIPHERS.N.ALGORITHM).transform(Constants.DECIPHERS.N.ORIGINAL_TOKEN); - expect(result).toEqual(Constants.DECIPHERS.N.DECIPHERED_TOKEN); - }); - }); - */ }); async function download(id, session) {