From 84d5edb6f0d10dbc4798da8c6e5a9b6a9ceaeedd Mon Sep 17 00:00:00 2001 From: LuanRT Date: Mon, 4 Jul 2022 16:34:02 -0300 Subject: [PATCH] refactor: rewrite OAuth and Requester (#90) * chore: update type declarations * dev: refactor oauth & requester * chore: tidy things up * chore: remove unneeded check --- lib/Innertube.js | 116 ++++---- lib/core/Actions.js | 6 +- lib/core/OAuth.js | 273 ++++++++---------- lib/core/Player.js | 27 +- lib/core/SessionBuilder.js | 49 +--- .../RemoveBannerForLiveChatCommand.js | 11 + lib/parser/contents/index.js | 2 +- lib/utils/Constants.js | 16 +- lib/utils/Request.js | 167 ++++++----- lib/utils/Utils.js | 3 +- typings/lib/Innertube.d.ts | 34 ++- typings/lib/core/OAuth.d.ts | 63 ++-- typings/lib/core/Player.d.ts | 13 +- typings/lib/core/SessionBuilder.d.ts | 13 +- .../classes/CommentActionButtons.d.ts | 8 - .../contents/classes/comments/Comment.d.ts | 26 -- .../RemoveBannerForLiveChatCommand.d.ts | 6 + typings/lib/parser/contents/index.d.ts | 8 +- typings/lib/utils/Constants.d.ts | 18 +- typings/lib/utils/Request.d.ts | 12 +- typings/lib/utils/Utils.d.ts | 2 + 21 files changed, 394 insertions(+), 479 deletions(-) create mode 100644 lib/parser/contents/classes/livechat/RemoveBannerForLiveChatCommand.js delete mode 100644 typings/lib/parser/contents/classes/CommentActionButtons.d.ts delete mode 100644 typings/lib/parser/contents/classes/comments/Comment.d.ts create mode 100644 typings/lib/parser/contents/classes/livechat/RemoveBannerForLiveChatCommand.d.ts diff --git a/lib/Innertube.js b/lib/Innertube.js index fef7d96a..22b04428 100644 --- a/lib/Innertube.js +++ b/lib/Innertube.js @@ -7,9 +7,6 @@ const AccountManager = require('./core/AccountManager'); const PlaylistManager = require('./core/PlaylistManager'); const InteractionManager = require('./core/InteractionManager'); -const Utils = require('./utils/Utils'); -const Request = require('./utils/Request'); - const Search = require('./parser/youtube/Search'); const VideoInfo = require('./parser/youtube/VideoInfo'); const Channel = require('./parser/youtube/Channel'); @@ -22,16 +19,24 @@ const YTMusic = require('./core/Music'); const FilterableFeed = require('./core/FilterableFeed'); const TabbedFeed = require('./core/TabbedFeed'); -const OldParser = require('./parser'); - -const Proto = require('./proto'); - const EventEmitter = require('events'); const { PassThrough } = require('stream'); +const Request = require('./utils/Request'); + +const { + InnertubeError, + throwIfMissing, + generateRandomString +} = require('./utils/Utils'); + +const OldParser = require('./parser'); +const Proto = require('./proto'); + +/** @namespace */ class Innertube { - #axios; #player; + #request; /** * @example @@ -53,39 +58,46 @@ class Innertube { } async #init() { - const session = await new SessionBuilder(this.config).build(); + const request = new Request(this.config); + const session = await new SessionBuilder(this.config, request.instance).build(); + /** @type {string} */ this.key = session.key; + + /** @type {string} */ this.version = session.api_version; + + /** @type {object} */ this.context = session.context; - this.logged_in = false; - this.player_url = session.player.url; + /** @type {boolean} */ + this.logged_in = !!this.config.cookie; + + /** @type {number} */ this.sts = session.player.sts; - this.#axios = session.axios; + /** @type {string} */ + this.player_url = session.player.url; + + /** @type {import('./core/Player')} */ this.#player = session.player; + request.setSession(this); + + this.#request = request.instance; + /** * @fires Innertube#auth - fired when signing in to an account. * @fires Innertube#update-credentials - fired when the access token is no longer valid. * @type {EventEmitter} */ this.ev = new EventEmitter(); - this.oauth = new OAuth(this.ev, session.axios); + this.oauth = new OAuth(this.ev, request.instance); - if (this.config.cookie) { - this.auth_apisid = Utils.getStringBetweenStrings(this.config.cookie, 'PAPISID=', ';'); - this.auth_apisid = Utils.generateSidAuth(this.auth_apisid); - } - - this.request = new Request(this); this.actions = new Actions(this); - this.account = new AccountManager(this.actions); this.playlist = new PlaylistManager(this.actions); this.interact = new InteractionManager(this.actions); - this.music = new YTMusic(this); return this; @@ -94,50 +106,40 @@ class Innertube { /** * Signs in to a google account. * - * @param {object} auth_info - * @param {string} auth_info.access_token - Token used to sign in. - * @param {string} auth_info.refresh_token - Token used to get a new access token. - * @param {Date} auth_info.expires - Access token's expiration date, which is usually 24hrs-ish. + * @param {object} credentials + * @param {string} credentials.access_token - Token used to sign in. + * @param {string} credentials.refresh_token - Token used to get a new access token. + * @param {Date} credentials.expires - Access token's expiration date, which is usually 24hrs-ish. * @returns {Promise.} */ - signIn(auth_info = {}) { + signIn(credentials = {}) { return new Promise(async (resolve) => { - this.oauth.init(auth_info); + this.oauth.init(credentials); - if (this.oauth.isValidAuthInfo()) { - await this.oauth.checkTokenValidity(); - this.#updateCredentials(); - return resolve(); + if (this.oauth.validateCredentials()) { + await this.oauth.checkAccessTokenValidity(); + this.logged_in = true; + resolve(); } this.ev.on('auth', (data) => { - if (data.status === 'SUCCESS') { - this.#updateCredentials(); - resolve(); - } + this.logged_in = true; + if (data.status === 'SUCCESS') resolve(); }); }); } - #updateCredentials() { - this.access_token = this.oauth.getAccessToken(); - this.refresh_token = this.oauth.getRefreshToken(); - this.logged_in = true; - } - /** * Signs out of an account. * * @returns {Promise.<{ success: boolean, status_code: number }>} */ async signOut() { - if (!this.logged_in) throw new Utils.InnertubeError('You are not signed in'); + if (!this.logged_in) throw new InnertubeError('You are not signed in'); const response = await this.oauth.revokeAccessToken(); - if (response.success) { - this.logged_in = false; - } + this.logged_in = false; return response; } @@ -149,8 +151,8 @@ class Innertube { * @returns {Promise.} */ async getInfo(video_id) { - Utils.throwIfMissing({ video_id }); - const cpn = Utils.generateRandomString(16); + throwIfMissing({ video_id }); + const cpn = generateRandomString(16); const initial_info = this.actions.getVideoInfo(video_id, cpn); const continuation = this.actions.next({ video_id }); @@ -166,8 +168,8 @@ class Innertube { * @returns {Promise.} */ async getBasicInfo(video_id) { - Utils.throwIfMissing({ video_id }); - const cpn = Utils.generateRandomString(16); + throwIfMissing({ video_id }); + const cpn = generateRandomString(16); const response = await this.actions.getVideoInfo(video_id, cpn); return new VideoInfo([ response, {} ], this.actions, this.#player, cpn); @@ -185,7 +187,7 @@ class Innertube { * @returns {Promise.} */ async search(query, filters = {}) { - Utils.throwIfMissing({ query }); + throwIfMissing({ query }); const response = await this.actions.search({ query, filters }); return new Search(this.actions, response.data); @@ -200,7 +202,7 @@ class Innertube { * @returns {Promise.<{ query: string, results: string[] }>} */ async getSearchSuggestions(query, options = { client: 'YOUTUBE' }) { - Utils.throwIfMissing({ query }); + throwIfMissing({ query }); const response = await this.actions.getSearchSuggestions(options.client, query); if (options.client === 'YTMUSIC' && !response.data.contents) return []; @@ -221,7 +223,7 @@ class Innertube { * @returns {Promise.} */ async getComments(video_id, sort_by) { - Utils.throwIfMissing({ video_id }); + throwIfMissing({ video_id }); const payload = Proto.encodeCommentsSectionParams(video_id, { sort_by: sort_by || 'TOP_COMMENTS' @@ -296,7 +298,7 @@ class Innertube { * @returns {Promise} */ async getChannel(id) { - Utils.throwIfMissing({ id }); + throwIfMissing({ id }); const response = await this.actions.browse(id); return new Channel(this.actions, response.data); } @@ -334,7 +336,7 @@ class Innertube { * @returns {Promise.} */ async getPlaylist(playlist_id) { - Utils.throwIfMissing({ playlist_id }); + throwIfMissing({ playlist_id }); const response = await this.actions.browse(`VL${playlist_id.replace(/VL/g, '')}`); return new Playlist(this.actions, response.data); } @@ -369,7 +371,7 @@ class Innertube { * @returns {PassThrough} */ download(video_id, options = {}) { - Utils.throwIfMissing({ video_id }); + throwIfMissing({ video_id }); const stream = new PassThrough(); (async () => { @@ -386,8 +388,8 @@ class Innertube { } /** @readonly */ - get axios() { - return this.#axios; + get request() { + return this.#request; } } diff --git a/lib/core/Actions.js b/lib/core/Actions.js index de1a2346..f2ca8ff8 100644 --- a/lib/core/Actions.js +++ b/lib/core/Actions.js @@ -186,14 +186,14 @@ class Actions { } if (args.params) { - data.params; + data.params = args.params; } if (args.filters) { if (args.client == 'YTMUSIC') { - data.filters = Proto.encodeMusicSearchFilters(args.filters); + data.params = Proto.encodeMusicSearchFilters(args.filters); } else { - data.filters = Proto.encodeSearchFilters(args.filters); + data.params = Proto.encodeSearchFilters(args.filters); } } diff --git a/lib/core/OAuth.js b/lib/core/OAuth.js index 63ba6ffd..59a031b0 100644 --- a/lib/core/OAuth.js +++ b/lib/core/OAuth.js @@ -1,260 +1,229 @@ 'use strict'; -const Constants = require('../utils/Constants'); const Uuid = require('uuid'); +const Constants = require('../utils/Constants'); +const { OAuthError } = require('../utils/Utils'); -/** @namespace */ class OAuth { - /** - * @type {AxiosInstance} - */ - #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`; + #request; + #identity; + #credentials = {}; - #auth_info = {}; #polling_interval = 5; #ev = null; /** * @param {EventEmitter} ev - * @param {AxiosInstance} axios + * @param {AxiosInstance} request */ - constructor(ev, axios) { + constructor(ev, request) { this.#ev = ev; - this.#axios = axios; + this.#request = request; } /** * Starts the auth flow in case no valid credentials are available. - * - * @param {object} auth_info - * @param {string} auth_info.access_token - * @param {string} auth_info.refresh_token - * @param {Date} auth_info.expires_in - * @returns {Promise.} + * @param {object} credentials + * @param {string} credentials.access_token + * @param {string} credentials.refresh_token + * @param {Date} credentials.expires_in */ - async init(auth_info) { - this.#auth_info = auth_info; - if (!auth_info.access_token) { - this.#requestUserCode(); + init(credentials) { + this.#credentials = credentials; + if (!credentials.access_token) { + this.#getUserCode(); } } /** - * Asks the OAuth server for a user code - * and verification URL. - * + * Asks the server for a user code and verification URL. * @returns {Promise.} */ - async #requestUserCode() { - const identity = await this.#getClientIdentity(); - - this.client_id = identity.id; - this.client_secret = identity.secret; + async #getUserCode() { + this.#identity = await this.#getClientIdentity(); const data = { - client_id: this.client_id, + client_id: this.#identity.client_id, scope: Constants.OAUTH.SCOPE, device_id: Uuid.v4(), model_name: Constants.OAUTH.MODEL_NAME }; - 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' }); + const response = await this.#request({ + data, + url: '/o/oauth2/device/code', + baseURL: Constants.URLS.YT_BASE, + method: 'post' + }).catch((err) => err); + + if (response instanceof Error) + return this.#ev.emit('auth', new OAuthError('Could not obtain user code.', response.message)); this.#ev.emit('auth', { - code: response.data.user_code, - status: 'AUTHORIZATION_PENDING', - expires_in: response.data.expires_in, - verification_url: response.data.verification_url + ...response.data, + status: 'AUTHORIZATION_PENDING' }); this.#polling_interval = response.data.interval; - this.#waitForAuth(response.data.device_code); + this.#startPolling(response.data.device_code); } /** - * Waits for sign-in authorization. - * - * @param {string} device_code - Client's device code. - * @returns {void} + * Polls the authorization server until access is granted by the user. + * @param {string} device_code */ - #waitForAuth(device_code) { - const data = { - client_id: this.client_id, - client_secret: this.client_secret, - code: device_code, - grant_type: Constants.OAUTH.GRANT_TYPE - }; + #startPolling(device_code) { + const poller = setInterval(async () => { + const data = { + ...this.#identity, + code: device_code, + grant_type: Constants.OAUTH.GRANT_TYPE + }; - setTimeout(async () => { - 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' }); + const response = await this.#request({ + data, + url: '/o/oauth2/token', + baseURL: Constants.URLS.YT_BASE, + method: 'post' + }).catch((err) => err); + + if (response instanceof Error) + return this.#ev.emit('auth', new OAuthError('Could not obtain user code.', { status: 'FAILED', message: response.message })); if (response.data.error) { switch (response.data.error) { - case 'slow_down': - case 'authorization_pending': - this.#waitForAuth(device_code); - break; case 'access_denied': - this.#ev.emit('auth', { - error: 'Access was denied.', - status: 'ACCESS_DENIED' - }); + this.#ev.emit('auth', new OAuthError('Access was denied.', { status: 'ACCESS_DENIED' })); break; case 'expired_token': - this.#ev.emit('auth', { - error: 'The user code has expired, requesting a new one.', - status: 'DEVICE_CODE_EXPIRED' - }); - this.#requestUserCode(); + this.#ev.emit('auth', new OAuthError('The device code has expired, restarting auth flow.', { status: 'DEVICE_CODE_EXPIRED' })); + clearInterval(poller); + this.#getUserCode(); break; default: + break; } - } else { - const expiration_date = new Date(new Date().getTime() + response.data.expires_in * 1000); - const credentials = { - access_token: response.data.access_token, - refresh_token: response.data.refresh_token, - expires: expiration_date - }; - - this.#auth_info = credentials; - - this.#ev.emit('auth', { - credentials, - status: 'SUCCESS' - }); + return; } - }, 1000 * this.#polling_interval); + + const expiration_date = new Date(new Date().getTime() + response.data.expires_in * 1000); + + this.#credentials = { + access_token: response.data.access_token, + refresh_token: response.data.refresh_token, + expires: expiration_date + }; + + this.#ev.emit('auth', { + credentials: this.#credentials, + status: 'SUCCESS' + }); + + clearInterval(poller); + }, this.#polling_interval * 1000); } /** * Refreshes the access token if necessary. - * * @returns {Promise.} */ - async checkTokenValidity() { - if (this.shouldRefreshToken()) { + async checkAccessTokenValidity() { + const timestamp = new Date(this.#credentials.expires).getTime(); + + if (new Date().getTime() > timestamp) { await this.#refreshAccessToken(); } } /** - * Gets a new access token using a refresh token. - * + * Retrieves a new access token using the refresh token. * @returns {Promise.} */ async #refreshAccessToken() { - const identity = await this.#getClientIdentity(); + this.#identity = await this.#getClientIdentity(); const data = { - client_id: identity.id, - client_secret: identity.secret, - refresh_token: this.#auth_info.refresh_token, + ...this.#identity, + refresh_token: this.#credentials.refresh_token, grant_type: 'refresh_token' }; - const response = await this.#axios.post(this.#oauth_token_url, JSON.stringify(data), Constants.OAUTH.HEADERS).catch((error) => error); + const response = await this.#request({ + data, + url: '/o/oauth2/token', + baseURL: Constants.URLS.YT_BASE, + method: 'post' + }).catch((err) => err); if (response instanceof Error) - return this.#ev.emit('update-credentials', { - error: 'Could not refresh access token.', - status: 'FAILED' - }); + return this.#ev.emit('update-credentials', new OAuthError('Could not refresh access token.', { status: 'FAILED' })); const expiration_date = new Date(new Date().getTime() + response.data.expires_in * 1000); - const credentials = { + this.#credentials = { access_token: response.data.access_token, - refresh_token: response.data.refresh_token || this.#auth_info.refresh_token, + refresh_token: response.data.refresh_token || this.credentials.refresh_token, expires: expiration_date }; - this.#auth_info = credentials; - this.#ev.emit('update-credentials', { - credentials, + credentials: this.#credentials, status: 'SUCCESS' }); } /** - * Revokes access token (note that the refresh token will also be revoked). - * - * @returns {Promise.} + * Revokes credentials. + * @returns {Promise.<{ success: boolean, status_code: number }>} */ - async revokeAccessToken() { - 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 - }; + revokeCredentials() { + return this.#request({ + url: '/o/oauth2/revoke', + baseURL: Constants.URLS.YT_BASE, + params: { token: this.getAccessToken() }, + method: 'post' + }); } /** - * Gets client identity data. - * - * @returns {Promise.<{ id: string, secret: string }>} + * Retrieves client identity from YouTube TV. + * @returns {Promise.<{ client_id: string, client_secret: string }>} */ 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 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}`); + const response = await this.#request({ + url: '/tv', + baseURL: Constants.URLS.YT_BASE, + headers: Constants.OAUTH.HEADERS + }); - // 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 url_body = Constants.OAUTH.REGEX.AUTH_SCRIPT.exec(response.data)[1]; + const script = await this.#request({ url: url_body, baseURL: Constants.URLS.YT_BASE }); - 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 = + script.data + .replace(/\n/g, '') + .match(Constants.OAUTH.REGEX.CLIENT_IDENTITY); - const client_identity = response.data.replace(/\n/g, '').match(Constants.OAUTH.REGEX.CLIENT_IDENTITY); return client_identity.groups; } /** - * Returns the access token. - * - * @returns {string} + * @returns {{ access_token: string, refresh_token: string, expires: Date }} */ - getAccessToken() { - return this.#auth_info.access_token; + get credentials() { + return this.#credentials; } /** - * Returns the refresh token. - * - * @returns {string} + * Validates the credentials. + * @returns {boolean} */ - getRefreshToken() { - return this.#auth_info.refresh_token; - } - - /** - * Checks if the auth info format is valid. - * - * @returns {boolean} true | false - */ - isValidAuthInfo() { - return this.#auth_info.hasOwnProperty('access_token') - && this.#auth_info.hasOwnProperty('refresh_token') - && this.#auth_info.hasOwnProperty('expires'); - } - - /** - * Checks access token validity. - * - * @returns {boolean} true | false - */ - shouldRefreshToken() { - const timestamp = new Date(this.#auth_info.expires).getTime(); - return new Date().getTime() > timestamp; + validateCredentials() { + return this.#credentials.hasOwnProperty('access_token') + && this.#credentials.hasOwnProperty('refresh_token') + && this.#credentials.hasOwnProperty('expires'); } } diff --git a/lib/core/Player.js b/lib/core/Player.js index 66780076..08f6bdc8 100644 --- a/lib/core/Player.js +++ b/lib/core/Player.js @@ -10,10 +10,7 @@ const { default: NToken } = require('../deciphers/NToken'); /** @namespace */ class Player { - /** - * @type {AxiosInstance} - */ - #axios; + #request; #player_id; #player_url; #player_path; @@ -27,11 +24,11 @@ class Player { * Represents the YouTube Web player script. * * @param {string} id - the id of the player. - * @param {AxiosInstance} axios + * @param {import('../utils/Request')} request */ - constructor(id, axios) { + constructor(id, request) { this.#player_id = id; - this.#axios = axios; + this.#request = request; this.#cache_dir = `${os.tmpdir()}/yt-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}.bin`; @@ -40,6 +37,7 @@ class Player { async init() { if (this.isCached()) { const buffer = (await Fs.promises.readFile(this.#player_path)).buffer; + const view = new DataView(buffer); const version = view.getUint32(0, true); @@ -56,11 +54,7 @@ class Player { } } - 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 }); + const response = await this.#request.get(this.#player_url, { headers: { 'content-type': 'text/javascript' } }); this.#signature_timestamp = this.#extractSigTimestamp(response.data); @@ -121,8 +115,6 @@ class Player { /** * Js player url. - * - * @readonly * @returns {string} */ get url() { @@ -131,8 +123,6 @@ class Player { /** * Signature timestamp. - * - * @readonly * @returns {string} */ get sts() { @@ -145,7 +135,6 @@ class Player { /** * Extracts the signature timestamp from the player source code. - * * @param {*} data * @returns {number} */ @@ -155,7 +144,6 @@ class Player { /** * Extracts the signature decipher algorithm. - * * @param {*} data * @returns {string} */ @@ -167,7 +155,6 @@ class Player { /** * Extracts the n-token decipher algorithm. - * * @param {*} data * @returns {string} */ @@ -177,11 +164,9 @@ class Player { /** * Checks if the player script is cached. - * * @returns {boolean} */ isCached() { - // Return false; return Fs.existsSync(this.#player_path); } } diff --git a/lib/core/SessionBuilder.js b/lib/core/SessionBuilder.js index 8a66dea6..b083bc95 100644 --- a/lib/core/SessionBuilder.js +++ b/lib/core/SessionBuilder.js @@ -1,6 +1,5 @@ 'use strict'; -const Axios = require('axios'); const Player = require('./Player'); const Proto = require('../proto'); const Utils = require('../utils/Utils'); @@ -9,9 +8,8 @@ const UserAgent = require('user-agents'); /** @namespace */ class SessionBuilder { - /** @type {Axios} */ - #axios; #config; + #request; #key; #client_name; @@ -23,17 +21,11 @@ class SessionBuilder { /** * @param {object} config - * @param {object} [config.proxy] - * @param {object} [config.http_agent] - * @param {object} [config.https_agent] + * @param {object} request */ - constructor(config) { + constructor(config, request) { this.#config = config; - this.#axios = Axios.create({ - proxy: this.#config.proxy, - httpAgent: this.#config.http_agent, - httpsAgent: this.#config.https_agent - }); + this.#request = request; } async build() { @@ -49,7 +41,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], this.#axios).init(); + this.#player = await new Player(data[1], this.#request).init(); this.#context = this.#buildContext(); @@ -58,7 +50,6 @@ class SessionBuilder { /** * Builds a valid context object. - * * @returns {object} */ #buildContext() { @@ -90,45 +81,23 @@ class SessionBuilder { } /** - * Retrieves initial configuration such as keys, - * client data, etc. - * + * Retrieves initial configuration. * @returns {Promise.} */ async #getYtConfig() { - 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', { - status_code: response?.response?.status || 0, - message: response.message - }); - + const response = await this.#request.get(`${Constants.URLS.YT_BASE}/sw.js_data`); return JSON.parse(response.data.replace(')]}\'', '')); } /** - * Retrives the YouTube player id. - * + * Retrieves the YouTube player id. * @returns {Promise.} */ async #getPlayerId() { - 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', { - status_code: response?.response?.status || 0, - message: response.message - }); - + const response = await this.#request.get(`${Constants.URLS.YT_BASE}/iframe_api`); return Utils.getStringBetweenStrings(response.data, 'player\\/', '\\/'); } - /** @readonly */ - get axios() { - return this.#axios; - } - /** @readonly */ get key() { return this.#key; diff --git a/lib/parser/contents/classes/livechat/RemoveBannerForLiveChatCommand.js b/lib/parser/contents/classes/livechat/RemoveBannerForLiveChatCommand.js new file mode 100644 index 00000000..50c74a14 --- /dev/null +++ b/lib/parser/contents/classes/livechat/RemoveBannerForLiveChatCommand.js @@ -0,0 +1,11 @@ +'use strict'; + +class RemoveBannerForLiveChatCommand { + type = 'RemoveBannerForLiveChatCommand'; + + constructor(data) { + this.target_action_id = data.targetActionId; + } +} + +module.exports = RemoveBannerForLiveChatCommand; \ No newline at end of file diff --git a/lib/parser/contents/index.js b/lib/parser/contents/index.js index 003ece07..087808ad 100644 --- a/lib/parser/contents/index.js +++ b/lib/parser/contents/index.js @@ -247,7 +247,7 @@ class Parser { return observe(results); } - + const keys = Object.keys(data); const classname = this.sanitizeClassName(keys[0]); diff --git a/lib/utils/Constants.js b/lib/utils/Constants.js index 43c84abb..e0eefea5 100644 --- a/lib/utils/Constants.js +++ b/lib/utils/Constants.js @@ -20,18 +20,16 @@ module.exports = { GRANT_TYPE: 'http://oauth.net/grant_type/device/1.0', MODEL_NAME: 'ytlr::', HEADERS: { - headers: { - 'accept': '*/*', - 'origin': 'https://www.youtube.com', - 'user-agent': 'Mozilla/5.0 (ChromiumStylePlatform) Cobalt/Version', - 'content-type': 'application/json', - 'referer': 'https://www.youtube.com/tv', - 'accept-language': 'en-US' - } + 'accept': '*/*', + 'origin': 'https://www.youtube.com', + 'user-agent': 'Mozilla/5.0 (ChromiumStylePlatform) Cobalt/Version', + 'content-type': 'application/json', + 'referer': 'https://www.youtube.com/tv', + 'accept-language': 'en-US' }, REGEX: { AUTH_SCRIPT: /