From 4943685e5750102aab906d7e449beedbaa265ca7 Mon Sep 17 00:00:00 2001 From: LuanRT Date: Thu, 5 May 2022 04:17:11 -0300 Subject: [PATCH] refactor: simplify the player class --- lib/core/Player.js | 87 ++++++++++++++++++++++++++---------- lib/deciphers/NToken.js | 7 ++- lib/deciphers/Signature.js | 91 ++++++++++++++++++++++---------------- lib/utils/Constants.js | 20 +++------ 4 files changed, 128 insertions(+), 77 deletions(-) diff --git a/lib/core/Player.js b/lib/core/Player.js index f15609e4..354230d5 100644 --- a/lib/core/Player.js +++ b/lib/core/Player.js @@ -6,44 +6,85 @@ const Utils = require('../utils/Utils'); const Constants = require('../utils/Constants'); class Player { - constructor(session) { - this.session = session; - this.player_name = Utils.getStringBetweenStrings(this.session.player_url, '/player/', '/'); - this.tmp_cache_dir = __dirname.slice(0, -8) + 'cache'; + #player_id; + #player_url; + #player_path; + + #ntoken_decipher_sc; + #signature_decipher_sc; + #signature_timestamp; + + #cache_dir; + + constructor(id) { + this.#player_id = id; + this.#cache_dir = __dirname.slice(0, -8) + 'cache'; + this.#player_url = Constants.URLS.YT_BASE + '/s/player/' + this.#player_id + '/player_ias.vflset/en_US/base.js'; + this.#player_path = `${this.#cache_dir}/${this.#player_id}.js`; } async init() { - if (Fs.existsSync(`${this.tmp_cache_dir}/${this.player_name}.js`)) { - const player_data = Fs.readFileSync(`${this.tmp_cache_dir}/${this.player_name}.js`).toString(); - this.sig_decipher_sc = this.#getSigDecipherCode(player_data); - this.ntoken_sc = this.#getNEncoder(player_data); + if (this.isCached()) { + const player_data = Fs.readFileSync(this.#player_path).toString(); + + this.#signature_timestamp = this.#extractSigTimestamp(player_data); + this.#signature_decipher_sc = this.#extractSigDecipherSc(player_data); + this.#ntoken_decipher_sc = this.#extractNTokenSc(player_data); } else { - const response = await Axios.get(`${Constants.URLS.YT_BASE}${this.session.player_url}`, { path: this.session.playerUrl, headers: { 'content-type': 'text/javascript', 'user-agent': Utils.getRandomUserAgent('desktop').userAgent } }).catch((error) => error); - if (response instanceof Error) throw new Error('Could not download player script: ' + response.message); - + const response = await Axios.get(this.#player_url, { headers: { 'content-type': 'text/javascript', 'user-agent': Utils.getRandomUserAgent('desktop').userAgent } }).catch((error) => error); + if (response instanceof Error) throw new Utils.InnertubeError('Could not download js player', { player_id }); + + this.#signature_timestamp = this.#extractSigTimestamp(response.data); + this.#signature_decipher_sc = this.#extractSigDecipherSc(response.data); + this.#ntoken_decipher_sc = this.#extractNTokenSc(response.data); + try { - // Deletes old players - Fs.existsSync(this.tmp_cache_dir) && Fs.rmSync(this.tmp_cache_dir, { recursive: true }); - - // Caches the current player so we don't have to download it all the time. - Fs.mkdirSync(this.tmp_cache_dir, { recursive: true }); - Fs.writeFileSync(`${this.tmp_cache_dir}/${this.player_name}.js`, response.data); + // Delete the old player + Fs.existsSync(this.#cache_dir) && + Fs.rmSync(this.#cache_dir, { recursive: true }); + + // Cache the current player + Fs.mkdirSync(this.#cache_dir, { recursive: true }); + Fs.writeFileSync(this.#player_path, response.data); } catch (err) {} - - this.sig_decipher_sc = this.#getSigDecipherCode(response.data); - this.ntoken_sc = this.#getNEncoder(response.data); } + + return this; } - - #getSigDecipherCode(data) { + + get url() { + return this.#player_url; + } + + get sts() { + return this.#signature_timestamp; + } + + get ntoken_decipher() { + return this.#ntoken_decipher_sc; + } + + get signature_decipher() { + return this.#signature_decipher_sc; + } + + #extractSigTimestamp(data) { + return parseInt(Utils.getStringBetweenStrings(data, 'signatureTimestamp:', ',')); + } + + #extractSigDecipherSc(data) { const sig_alg_sc = Utils.getStringBetweenStrings(data, 'this.audioTracks};var', '};'); const sig_data = Utils.getStringBetweenStrings(data, 'function(a){a=a.split("")', 'return a.join("")}'); return sig_alg_sc + sig_data; } - #getNEncoder(data) { + #extractNTokenSc(data) { return `var b=a.split("")${Utils.getStringBetweenStrings(data, 'b=a.split("")', '}return b.join("")}')}} return b.join("");`; } + + isCached() { + return Fs.existsSync(this.#player_path); + } } module.exports = Player; \ No newline at end of file diff --git a/lib/deciphers/NToken.js b/lib/deciphers/NToken.js index 388e9a3d..74180afd 100644 --- a/lib/deciphers/NToken.js +++ b/lib/deciphers/NToken.js @@ -52,14 +52,17 @@ class NToken { transformations[data.index](transformations[param_index[0]], transformations[param_index[1]], base64_dia); }); } catch (err) { - console.error(`Could not transform n-token (${this.n}), download may be throttled:`, err.message); + console.error(new Utils.ParsingError('Could not transform n-token, download may be throttled.', { + original_token: this.n, + stack: err.stack + })); return this.n; } return n_token.join(''); } #getFunc(el) { - return el.match(Constants.FUNCS_REGEX); + return el.match(Constants.NTOKEN_REGEX.FUNCTIONS); } /** diff --git a/lib/deciphers/Signature.js b/lib/deciphers/Signature.js index c23002a8..469f5000 100644 --- a/lib/deciphers/Signature.js +++ b/lib/deciphers/Signature.js @@ -1,75 +1,90 @@ 'use strict'; +const Constants = require('../utils/Constants'); const QueryString = require('querystring'); class Signature { - constructor(url, player) { + constructor(url, sig_decipher_sc) { this.url = url; - this.player = player; - this.func_regex = /(.{2}):function\(.*?\){(.*?)}/g; - this.actions_regex = /;.{2}\.(.{2})\(.*?,(.*?)\)/g; + this.sig_decipher_sc = sig_decipher_sc; } - + /** * Deciphers signature. * @returns {string} */ decipher() { + let actions; + const args = QueryString.parse(this.url); + const signature = args.s.split(''); + const functions = this.#getFunctions(); - function splice(arr, end) { - arr.splice(0, end); - } - - function swap(arr, index) { - let origArrI = arr[0]; - arr[0] = arr[index % arr.length]; - arr[index % arr.length] = origArrI; - } - - function reverse(arr) { - arr.reverse(); - } - - let actions; - let signature = args.s.split(''); - - while ((actions = this.actions_regex.exec(this.player.sig_decipher_sc)) !== null) { - switch (actions[1]) { + /** + * Decides what function should be used to modify the + * the signature. + */ + while ((actions = Constants.SIG_REGEX.ACTIONS.exec(this.sig_decipher_sc)) !== null) { + const action = actions.groups; + switch (action.name) { case functions[0]: - reverse(signature, actions[2]); + this.#reverse(signature); break; case functions[1]: - splice(signature, actions[2]); + this.#splice(signature, action.param); break; case functions[2]: - swap(signature, actions[2]); + this.#swap(signature, action.param); break; default: } } const url_components = new URL(args.url); - args.sp ? url_components.searchParams.set(args.sp, signature.join('')) : url_components.searchParams.set('signature', signature.join('')); + + args.sp ? + url_components.searchParams.set(args.sp, signature.join('')) : + url_components.searchParams.set('signature', signature.join('')); + return url_components.toString(); } - + + /** + * Extracts the functions used to modify the signature + * and returns them in the correct order. + * + * @returns {Array.} + */ #getFunctions() { let func; - let func_name = []; - - while ((func = this.func_regex.exec(this.player.sig_decipher_sc)) !== null) { - if (func[0].includes('reverse()')) { - func_name[0] = func[1]; + let functions = []; + + while ((func = Constants.SIG_REGEX.FUNCTIONS.exec(this.sig_decipher_sc)) !== null) { + if (func[0].includes('reverse')) { + functions[0] = func[1]; } else if (func[0].includes('splice')) { - func_name[1] = func[1]; + functions[1] = func[1]; } else { - func_name[2] = func[1]; + functions[2] = func[1]; } } - - return func_name; + + return functions; + } + + #swap(arr, index) { + let origArrI = arr[0]; + arr[0] = arr[index % arr.length]; + arr[index % arr.length] = origArrI; + } + + #splice(arr, end) { + arr.splice(0, end); + } + + #reverse(arr) { + arr.reverse(); } } diff --git a/lib/utils/Constants.js b/lib/utils/Constants.js index 919bd48a..06fe33c0 100644 --- a/lib/utils/Constants.js +++ b/lib/utils/Constants.js @@ -40,18 +40,6 @@ module.exports = { VERSION: '17.17.32' } }, - DEFAULT_HEADERS: (config) => { - return { - headers: { - 'Cookie': config?.cookie || '', - 'user-agent': Utils.getRandomUserAgent('desktop').userAgent, - 'Referer': 'https://www.google.com/', - 'Accept': 'text/html', - 'Accept-Language': `en-${config?.gl || 'US'}`, - 'Accept-Encoding': 'gzip' - } - }; - }, STREAM_HEADERS: { 'Accept': '*/*', 'User-Agent': Utils.getRandomUserAgent('desktop').userAgent, @@ -94,11 +82,15 @@ module.exports = { NORMAL: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'.split(''), REVERSE: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'.split('') }, + SIG_REGEX: { + ACTIONS: /;.{2}\.(?.{2})\(.*?,(?.*?)\)/g, + FUNCTIONS: /(?.{2}):function\(.*?\){(.*?)}/g + }, NTOKEN_REGEX: { CALLS: /c\[(.*?)\]\((.+?)\)/g, PLACEHOLDERS: /c\[(.*?)\]=c/g, + FUNCTIONS: /d\.push\(e\)|d\.reverse\(\)|d\[0\]\)\[0\]\)|f=d\[0];d\[0\]|d\.length;d\.splice\(e,1\)|function\(\){for\(var|function\(d,e,f\){var|function\(d\){for\(var|reverse\(\)\.forEach|unshift\(d\.pop\(\)\)|function\(d,e\){for\(var f/ }, - FUNCS_REGEX: /d\.push\(e\)|d\.reverse\(\)|d\[0\]\)\[0\]\)|f=d\[0];d\[0\]|d\.length;d\.splice\(e,1\)|function\(\){for\(var|function\(d,e,f\){var|function\(d\){for\(var|reverse\(\)\.forEach|unshift\(d\.pop\(\)\)|function\(d,e\){for\(var f/, FUNCS: { PUSH: 'd.push(e)', REVERSE_1: 'd.reverse()', @@ -112,4 +104,4 @@ module.exports = { TRANSLATE_1: 'function(d,e){for(var f', TRANSLATE_2: 'function(d,e,f){var' } -}; +}; \ No newline at end of file