From d743b5a08845a610e97c3623893d126e2191fe37 Mon Sep 17 00:00:00 2001 From: "luan.lrt4@gmail.com" Date: Wed, 20 Apr 2022 06:18:07 -0300 Subject: [PATCH] refactor: use a single axios instance and remove redundant code --- lib/Innertube.js | 28 ++++------------------ lib/core/Actions.js | 28 ++++++++-------------- lib/utils/Constants.js | 32 ++++--------------------- lib/utils/Request.js | 54 ++++++++++++++++++++++++++++++++++++++++++ test/index.js | 8 +++---- 5 files changed, 76 insertions(+), 74 deletions(-) create mode 100644 lib/utils/Request.js diff --git a/lib/Innertube.js b/lib/Innertube.js index 0abcb63c..56d33383 100644 --- a/lib/Innertube.js +++ b/lib/Innertube.js @@ -12,6 +12,7 @@ const Actions = require('./core/Actions'); const Livechat = require('./core/Livechat'); const Utils = require('./utils/Utils'); +const Request = require('./utils/Request'); const Constants = require('./utils/Constants'); const NToken = require('./deciphers/NToken'); @@ -71,22 +72,9 @@ class Innertube { this.auth_apisid = Utils.getStringBetweenStrings(this.config.cookie, 'PAPISID=', ';'); this.auth_apisid = Utils.generateSidAuth(this.auth_apisid); } - - // Axios instances - this.YTRequester = Axios.create({ - baseURL: Constants.URLS.YT_BASE_API + this.version, - timeout: 15000, - headers: Constants.INNERTUBE_HEADERS({ session: this, ytmusic: false }), - params: { key: this.key } - }); - - this.YTMRequester = Axios.create({ - baseURL: Constants.URLS.YT_MUSIC_BASE_API + this.version, - timeout: 15000, - headers: Constants.INNERTUBE_HEADERS({ session: this, ytmusic: true }), - params: { key: this.key } - }); - + + this.request = new Request(this); + this.#initMethods(); } else { this.#retry_count += 1; @@ -322,14 +310,6 @@ class Innertube { this.access_token = this.#oauth.getAccessToken(); this.refresh_token = this.#oauth.getRefreshToken(); this.logged_in = true; - - // API key is not needed if logged in via OAuth - delete this.YTRequester.defaults.params.key; - delete this.YTMRequester.defaults.params.key; - - // Update default headers - this.YTRequester.defaults.headers = Constants.INNERTUBE_HEADERS({ session: this, ytmusic: false }); - this.YTMRequester.defaults.headers = Constants.INNERTUBE_HEADERS({ session: this, ytmusic: true }); } /** diff --git a/lib/core/Actions.js b/lib/core/Actions.js index b31afcf7..80dbcc22 100644 --- a/lib/core/Actions.js +++ b/lib/core/Actions.js @@ -64,7 +64,7 @@ async function engage(session, engagement_type, args = {}) { throw new Utils.InnertubeError('Invalid action', engagement_type); } - const response = await session.YTRequester.post(`/${engagement_type}`, JSON.stringify(data)).catch((error) => error); + const response = await session.request.post(`/${engagement_type}`, JSON.stringify(data)).catch((error) => error); if (response instanceof Error) return { success: false, status_code: response.response?.status || 0, message: response.message }; return { @@ -129,8 +129,7 @@ async function browse(session, action, args = {}) { throw new Utils.InnertubeError('Invalid action', action); } - const requester = args.ytmusic && session.YTMRequester || session.YTRequester; - const response = await requester.post('/browse', JSON.stringify(data)).catch((error) => error); + const response = await session.request.post('/browse', JSON.stringify(data)).catch((error) => error); if (response instanceof Error) return { success: false, status_code: response.response?.status || 0, message: response.message }; return { @@ -166,7 +165,7 @@ async function account(session, action, args = {}) { throw new Utils.InnertubeError('Invalid action', action); } - const response = await session.YTRequester.post(`/${action}`, JSON.stringify(data)).catch((error) => error); + const response = await session.request.post(`/${action}`, JSON.stringify(data)).catch((error) => error); if (response instanceof Error) return { success: false, status_code: response.response?.status || 0, message: response.message }; return { @@ -202,7 +201,7 @@ async function music(session, action, args) { throw new Utils.InnertubeError('Invalid action', action); } - const response = await session.YTMRequester.post(`/music/${action}`, JSON.stringify(data)).catch((error) => error); + const response = await session.request.post(`/music/${action}`, JSON.stringify(data)).catch((error) => error); if (response instanceof Error) return { success: false, status_code: response.response?.status || 0, message: response.message }; return { @@ -245,8 +244,7 @@ async function search(session, client, args = {}) { throw new Utils.InnertubeError('Invalid client', client); } - const requester = client == 'YOUTUBE' && session.YTRequester || session.YTMRequester; - const response = await requester.post('/search', JSON.stringify(data)).catch((error) => error); + const response = await session.request.post('/search', JSON.stringify(data)).catch((error) => error); if (response instanceof Error) return { success: false, status_code: response.response?.status || 0, message: response.message }; return { @@ -286,7 +284,7 @@ async function notifications(session, action, args = {}) { throw new Utils.InnertubeError('Invalid action', action); } - const response = await session.YTRequester.post(`/notification/${action}`, JSON.stringify(data)).catch((err) => err); + const response = await session.request.post(`/notification/${action}`, JSON.stringify(data)).catch((err) => err); if (response instanceof Error) return { success: false, status_code: response.response?.status || 0, message: response.message }; if (action === 'modify_channel_preference') return { success: true, status_code: response.status }; @@ -336,7 +334,7 @@ async function livechat(session, action, args = {}) { throw new Utils.InnertubeError('Invalid action', action); } - const response = await session.YTRequester.post(`/${action}`, JSON.stringify(data)).catch((err) => err); + const response = await session.request.post(`/${action}`, JSON.stringify(data)).catch((err) => err); if (response instanceof Error) return { success: false, message: response.message }; return { success: true, data: response.data }; @@ -374,14 +372,8 @@ async function next(session, args = {}) { } } - const requester = args.ytmusic && session.YTMRequester || session.YTRequester; - const response = await requester.post('/next', JSON.stringify(data)).catch((error) => error); - - if (response instanceof Error) return { - success: false, - status_code: response.response?.status || 0, - message: response.message - }; + const response = await session.request.post('/next', JSON.stringify(data)).catch((error) => error); + if (response instanceof Error) return { success: false, status_code: response?.status || 0, message: response.message }; return { success: true, @@ -398,7 +390,7 @@ async function next(session, args = {}) { * @returns {Promise.} - Video data. */ async function getVideoInfo(session, args = {}) { - const response = await session.YTRequester.post(`/player`, JSON.stringify(Constants.VIDEO_INFO_REQBODY(args.id, session.sts, session.context))).catch((err) => err); + const response = await session.request.post(`/player`, JSON.stringify(Constants.VIDEO_INFO_REQBODY(args.id, session.sts, session.context))).catch((err) => err); if (response instanceof Error) throw new Utils.InnertubeError(`Could not get video info: ${response.message}`); return response.data; } diff --git a/lib/utils/Constants.js b/lib/utils/Constants.js index 7fdb6f1b..10637951 100644 --- a/lib/utils/Constants.js +++ b/lib/utils/Constants.js @@ -50,33 +50,9 @@ module.exports = { 'Referer': 'https://www.youtube.com', 'DNT': '?1' }, - INNERTUBE_HEADERS: (info) => { - const origin = info.ytmusic && 'https://music.youtube.com' || 'https://www.youtube.com'; - - const headers = { - 'accept': '*/*', - 'user-agent': Utils.getRandomUserAgent('desktop').userAgent, - 'content-type': 'application/json', - 'accept-language': `en-${info.session.config.gl || 'US'}`, - 'x-goog-authuser': 0, - 'x-goog-visitor-id': info.session.context.client.visitorData || '', - 'x-youtube-client-name': 1, - 'x-youtube-client-version': info.session.context.client.clientVersion, - 'x-youtube-chrome-connected': 'source=Chrome,mode=0,enable_account_consistency=true,supervised=false,consistency_enabled_by_default=false', - 'x-origin': origin, - 'origin': origin - }; - - const auth_creds = info.session.cookie - && info.session.auth_apisid - || `Bearer ${info.session.access_token}`; - - if (info.session.logged_in) { - headers.cookie = info.session.config.cookie || ''; - headers.authorization = auth_creds; - } - - return headers; + INNERTUBE_HEADERS_BASE: { + 'accept': '*/*', + 'content-type': 'application/json', }, VIDEO_INFO_REQBODY: (id, sts, context) => { return { @@ -146,4 +122,4 @@ module.exports = { TRANSLATE_1: 'function(d,e){for(var f', TRANSLATE_2: 'function(d,e,f){var' } -}; \ No newline at end of file +}; diff --git a/lib/utils/Request.js b/lib/utils/Request.js new file mode 100644 index 00000000..ce746ff8 --- /dev/null +++ b/lib/utils/Request.js @@ -0,0 +1,54 @@ +'use strict'; + +const Axios = require('axios'); +const Utils = require('./Utils'); +const Constants = require('./Constants'); +const Controller = new AbortController(); + +class Request { + constructor (session) { + this.session = session; + + this.instance = Axios.create({ + baseURL: Constants.URLS.YT_BASE_API + session.version, + headers: Constants.INNERTUBE_HEADERS_BASE, + params: { key: session.key }, + timeout: 15000 + }); + + this.#setupInterceptor(); + + return this.instance; + } + + #setupInterceptor() { + this.instance.interceptors.request.use((config) => { + const is_ytmusic = config.data.includes(Constants.URLS.YT_MUSIC); + + config.headers['accept-language'] = `en-${this.session.config.gl || 'US'}`; + config.headers['x-goog-visitor-id'] = this.session.context.client.visitorData || '' + config.headers['x-youtube-client-version'] = this.session.context.client.clientVersion; + config.headers['x-origin'] = is_ytmusic && Constants.URLS.YT_MUSIC || Constants.URLS.YT_BASE; + config.headers['origin'] = is_ytmusic && Constants.URLS.YT_MUSIC || Constants.URLS.YT_BASE; + + is_ytmusic && (config.baseURL = Constants.URLS.YT_MUSIC_BASE_API + this.session.version); + + if (this.session.logged_in) { + const cookie = this.session.config.cookie; + + const token = cookie + && this.session.auth_apisid + || this.session.access_token; + + config.headers.cookie = cookie || ''; + config.headers.authorization = cookie && token || `Bearer ${token}`; + + !cookie && (delete config.params.key); + } + + return config; + }, (error) => Promise.reject(error)); + } +} + +module.exports = Request; \ No newline at end of file diff --git a/test/index.js b/test/index.js index 067a1551..0739d8ab 100644 --- a/test/index.js +++ b/test/index.js @@ -24,9 +24,9 @@ async function performTests() { const ytsearch_suggestions = await youtube.getSearchSuggestions('test', { client: 'YOUTUBE' }).catch((error) => error); assert(!(ytsearch_suggestions instanceof Error), `should retrieve YouTube search suggestions`, ytsearch_suggestions); - - const ytmsearch_suggestions = await youtube.getSearchSuggestions('test', { client: 'YTMUSIC' }); - assert(!(ytmsearch_suggestions instanceof Error), `should retrieve YouTube Music search suggestions`); + + const ytmsearch_suggestions = await youtube.getSearchSuggestions('test', { client: 'YTMUSIC' }).catch((error) => error); + assert(!(ytmsearch_suggestions instanceof Error), `should retrieve YouTube Music search suggestions`, ytmsearch_suggestions); const details = await youtube.getDetails(Constants.test_video_id).catch((error) => error); assert(!(details instanceof Error), `should retrieve details for ${Constants.test_video_id}`, details); @@ -70,7 +70,7 @@ function downloadVideo(id, youtube) { function assert(outcome, description, data) { const pass_fail = outcome ? 'pass' : 'fail'; - + console.info(pass_fail, ':', description); !outcome && (failed_tests_count += 1); !outcome && console.error('Error: ', data);