mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-18 20:12:12 +00:00
refactor: simplify the player class
This commit is contained in:
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.<string>}
|
||||
*/
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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}\.(?<name>.{2})\(.*?,(?<param>.*?)\)/g,
|
||||
FUNCTIONS: /(?<name>.{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'
|
||||
}
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user