diff --git a/lib/Innertube.js b/lib/Innertube.js index ec4a8124..74658924 100644 --- a/lib/Innertube.js +++ b/lib/Innertube.js @@ -27,6 +27,7 @@ const Signature = require('./deciphers/Signature'); * @namespace */ class Innertube { + #axios; #player; /** @@ -40,7 +41,10 @@ class Innertube { * @param {string} [config.gl] * @param {string} [config.cookie] * @param {boolean} [config.debug] - * + * @param {object} [config.proxy] + * @param {object} [config.httpAgent] + * @param {object} [config.httpsAgent] + * * @returns {Innertube} * @constructor */ @@ -51,7 +55,8 @@ class Innertube { async #init() { const session = await new SessionBuilder(this.config).build(); - + + this.#axios = session.axios; this.key = session.key; this.version = session.api_version; this.context = session.context; @@ -68,7 +73,7 @@ class Innertube { * @type {EventEmitter} */ this.ev = new EventEmitter(); - this.oauth = new OAuth(this.ev); + this.oauth = new OAuth(this.ev, this.axios); if (this.config.cookie) { this.auth_apisid = Utils.getStringBetweenStrings(this.config.cookie, 'PAPISID=', ';'); @@ -647,6 +652,14 @@ class Innertube { return stream; } + + /** + * @readonly + * @type {AxiosInstance} + */ + get axios() { + return this.#axios; + } } module.exports = Innertube; \ No newline at end of file diff --git a/lib/core/OAuth.js b/lib/core/OAuth.js index 29bc3815..25c2b94d 100644 --- a/lib/core/OAuth.js +++ b/lib/core/OAuth.js @@ -6,6 +6,7 @@ const Uuid = require('uuid'); /** @namespace */ class OAuth { + #axios; #oauth_code_url = `${Constants.URLS.YT_BASE}/o/oauth2/device/code`; #oauth_token_url = `${Constants.URLS.YT_BASE}/o/oauth2/token`; #oauth_revoke_url = `${Constants.URLS.YT_BASE}/o/oauth2/revoke`; @@ -16,10 +17,12 @@ class OAuth { /** * @param {EventEmitter} ev + * @param {AxiosInstance} axios * @constructor */ - constructor(ev) { + constructor(ev, axios) { this.#ev = ev; + this.#axios = axios; } /** @@ -52,7 +55,7 @@ class OAuth { model_name: Constants.OAUTH.MODEL_NAME }; - const response = await Axios.post(this.#oauth_code_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error); + const response = await this.axios.post(this.#oauth_code_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error); if (response instanceof Error) return this.#ev.emit('auth', { error: 'Could not obtain user code.', status: 'FAILED' }); this.#ev.emit('auth', { @@ -82,7 +85,7 @@ class OAuth { }; setTimeout(async () => { - const response = await Axios.post(this.#oauth_token_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error); + const response = await this.axios.post(this.#oauth_token_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error); if (response instanceof Error) return this.#ev.emit('auth', { error: 'Could not get authentication token.', status: 'FAILED' }); if (response.data.error) { @@ -149,7 +152,7 @@ class OAuth { grant_type: 'refresh_token', }; - const response = await Axios.post(this.#oauth_token_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error); + const response = await this.axios.post(this.#oauth_token_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error); if (response instanceof Error) return this.#ev.emit('update-credentials', { @@ -178,7 +181,7 @@ class OAuth { * @returns {Promise.} */ async revokeAccessToken() { - const response = await Axios.post(`${this.#oauth_revoke_url}?token=${this.getAccessToken()}`, Constants.OAUTH.HEADERS).catch((error) => error); + const response = await this.axios.post(`${this.#oauth_revoke_url}?token=${this.getAccessToken()}`, Constants.OAUTH.HEADERS).catch((error) => error); return { success: !(response instanceof Error), status_code: response.status || 0 @@ -191,14 +194,14 @@ class OAuth { */ async #getClientIdentity() { // This request is made to get the auth script url, hard-coding it isn't viable as it changes overtime. - const yttv_response = await Axios.get(`${Constants.URLS.YT_BASE}/tv`, Constants.OAUTH.HEADERS).catch((error) => error); + const yttv_response = await this.axios.get(`${Constants.URLS.YT_BASE}/tv`, Constants.OAUTH.HEADERS).catch((error) => error); if (yttv_response instanceof Error) throw new Error(`Could not extract client identity: ${yttv_response.message}`); // Here we download the script and extract the necessary data to proceed with the auth flow. const url_body = Constants.OAUTH.REGEX.AUTH_SCRIPT.exec(yttv_response.data)[1]; const script_url = `${Constants.URLS.YT_BASE}/${url_body}`; - const response = await Axios.get(script_url).catch((error) => error); + const response = await this.axios.get(script_url).catch((error) => error); if (response instanceof Error) throw new Error(`Could not extract client identity: ${response.message}`); const client_identity = response.data.replace(/\n/g, '').match(Constants.OAUTH.REGEX.CLIENT_IDENTITY); @@ -239,6 +242,14 @@ class OAuth { const timestamp = new Date(this.#auth_info.expires).getTime(); return new Date().getTime() > timestamp; } + + /** + * @readonly + * @type {AxiosInstance} + */ + get axios() { + return this.#axios; + } } module.exports = OAuth; \ No newline at end of file diff --git a/lib/core/Player.js b/lib/core/Player.js index 45b3fc96..7de8dbab 100644 --- a/lib/core/Player.js +++ b/lib/core/Player.js @@ -8,6 +8,7 @@ const Constants = require('../utils/Constants'); /** @namespace */ class Player { + #axios; #player_id; #player_url; #player_path; @@ -20,10 +21,12 @@ class Player { /** * Represents the YouTube Web player script. * @param {string} id - the id of the player. + * @param {AxiosInstance} axios * @constructor */ - constructor(id) { + constructor(id,axios) { this.#player_id = id; + this.#axios = axios; this.#cache_dir = `${os.tmpdir()}/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`; @@ -36,7 +39,7 @@ class Player { this.#signature_decipher_sc = this.#extractSigDecipherSc(player_data); this.#ntoken_decipher_sc = this.#extractNTokenSc(player_data); } else { - const response = await Axios.get(this.#player_url, { headers: { 'content-type': 'text/javascript', 'user-agent': Utils.getRandomUserAgent('desktop').userAgent } }).catch((error) => error); + const response = await this.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.#player_id }); this.#signature_timestamp = this.#extractSigTimestamp(response.data); @@ -122,6 +125,14 @@ class Player { isCached() { return Fs.existsSync(this.#player_path); } + + /** + * @readonly + * @type {AxiosInstance} + */ + get axios() { + return this.#axios; + } } module.exports = Player; \ No newline at end of file diff --git a/lib/core/SessionBuilder.js b/lib/core/SessionBuilder.js index 2da4f971..46ebb3b0 100644 --- a/lib/core/SessionBuilder.js +++ b/lib/core/SessionBuilder.js @@ -9,6 +9,7 @@ const UserAgent = require('user-agents'); /** @namespace */ class SessionBuilder { + #axios; #config; #key; @@ -28,6 +29,8 @@ class SessionBuilder { } async build() { + this.#axios = Axios.create({ proxy: this.#config.proxy, httpAgent: this.#config.httpAgent, httpsAgent: this.#config.httpsAgent }) + const data = await Promise.all([ this.#getYtConfig(), this.#getPlayerId() @@ -40,7 +43,7 @@ class SessionBuilder { this.#client_name = Constants.CLIENTS.WEB.NAME; this.#client_version = ytcfg[0][0][16]; this.#remote_host = ytcfg[0][0][3]; - this.#player = await new Player(data[1]).init(); + this.#player = await new Player(data[1], this.axios).init(); this.#context = this.#buildContext(); @@ -85,7 +88,7 @@ class SessionBuilder { * @returns Promise. */ async #getYtConfig() { - const response = await Axios.get(`${Constants.URLS.YT_BASE}/sw.js_data`).catch((err) => err); + const response = await this.axios.get(`${Constants.URLS.YT_BASE}/sw.js_data`).catch((err) => err); if (response instanceof Error) throw new Utils.InnertubeError('Could not retrieve configuration data', { @@ -101,7 +104,7 @@ class SessionBuilder { * @returns {Promise. */ async #getPlayerId() { - const response = await Axios.get(`${Constants.URLS.YT_BASE}/iframe_api`).catch((err) => err); + const response = await this.axios.get(`${Constants.URLS.YT_BASE}/iframe_api`).catch((err) => err); if (response instanceof Error) throw new Utils.InnertubeError('Could not retrieve js player id', { @@ -111,7 +114,15 @@ class SessionBuilder { return Utils.getStringBetweenStrings(response.data, 'player\\/', '\\/'); } - + + /** + * @readonly + * @type {AxiosInstance} + */ + get axios() { + return this.#axios; + } + /** @readonly */ get key() { return this.#key; diff --git a/lib/utils/Request.js b/lib/utils/Request.js index c929dd00..c4005442 100644 --- a/lib/utils/Request.js +++ b/lib/utils/Request.js @@ -12,8 +12,8 @@ class Request { */ constructor(session) { this.session = session; - this.instance = Axios.create({ + ...session.axios.defaults, baseURL: Constants.URLS.YT_BASE_API + session.version, headers: Constants.INNERTUBE_HEADERS_BASE, params: { key: session.key, prettyPrint: false },