Share axios instance between modules.

This allows to use axios with http(s) and socks proxies via http(s)Agent and proxy settings.
This commit is contained in:
xrip
2022-05-31 09:47:55 +03:00
committed by LuanRT
parent 4ccb4b07b7
commit f05270daee
5 changed files with 63 additions and 17 deletions

View File

@@ -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;

View File

@@ -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.<void>}
*/
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;

View File

@@ -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;

View File

@@ -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.<object>
*/
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.<string>
*/
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;

View File

@@ -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 },