'use strict'; const Axios = require('axios'); const Player = require('./Player'); const Proto = require('../proto'); const Utils = require('../utils/Utils'); const Constants = require('../utils/Constants'); const UserAgent = require('user-agents'); /** @namespace */ class SessionBuilder { /** @type {Axios} */ #axios; #config; #key; #client_name; #client_version; #api_version; #remote_host; #context; #player; /** * @param {object} config * @param {object} [config.proxy] * @param {object} [config.http_agent] * @param {object} [config.https_agent] */ constructor(config) { this.#config = config; this.#axios = Axios.create({ proxy: this.#config.proxy, httpAgent: this.#config.http_agent, httpsAgent: this.#config.https_agent }); } async build() { const data = await Promise.all([ this.#getYtConfig(), this.#getPlayerId() ]); const ytcfg = data[0][0][2]; this.#key = ytcfg[1]; this.#api_version = `v${ytcfg[0][0][6]}`; 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.#context = this.#buildContext(); return this; } /** * Builds a valid context object. * * @returns {object} */ #buildContext() { const user_agent = new UserAgent({ deviceCategory: 'desktop' }); const id = Utils.generateRandomString(11); const timestamp = Math.floor(Date.now() / 1000); const visitor_data = Proto.encodeVisitorData(id, timestamp); const context = { client: { hl: 'en', gl: this.#config.gl || 'US', remoteHost: this.#remote_host, deviceMake: user_agent.vendor, deviceModel: user_agent.platform, visitorData: visitor_data, userAgent: user_agent.toString(), clientName: this.#client_name, clientVersion: this.#client_version, originalUrl: Constants.URLS.API.BASE }, user: { lockedSafetyMode: false }, request: { useSsl: true } }; return context; } /** * Retrieves initial configuration such as keys, * client data, etc. * * @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 }); return JSON.parse(response.data.replace(')]}\'', '')); } /** * Retrives 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 }); return Utils.getStringBetweenStrings(response.data, 'player\\/', '\\/'); } /** @readonly */ get axios() { return this.#axios; } /** @readonly */ get key() { return this.#key; } /** @readonly */ get context() { return this.#context; } /** @readonly */ get api_version() { return this.#api_version; } /** @readonly */ get client_version() { return this.#client_version; } /** @readonly */ get client_name() { return this.#client_name; } /** @readonly */ get player() { return this.#player; } } module.exports = SessionBuilder;