mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-17 11:32:27 +00:00
chore: clean up build steps
This commit is contained in:
134
src/utils/HTTPClient.ts
Normal file
134
src/utils/HTTPClient.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import Session, { Context } from '../core/Session';
|
||||
import Constants from './Constants';
|
||||
import { generateSidAuth, getRandomUserAgent, getStringBetweenStrings, InnertubeError, isServer } from './Utils';
|
||||
|
||||
export type FetchFunction = typeof fetch;
|
||||
|
||||
export interface HTTPClientInit {
|
||||
baseURL?: string;
|
||||
}
|
||||
|
||||
export default class HTTPClient {
|
||||
#session: Session;
|
||||
#cookie?: string;
|
||||
#fetch: FetchFunction;
|
||||
constructor(session: Session, cookie?: string, fetch?: FetchFunction) {
|
||||
this.#session = session;
|
||||
this.#cookie = cookie;
|
||||
this.#fetch = fetch || globalThis.fetch;
|
||||
}
|
||||
|
||||
get fetch_function() {
|
||||
return this.#fetch;
|
||||
}
|
||||
|
||||
async fetch(
|
||||
input: URL | Request | string,
|
||||
init?: RequestInit & HTTPClientInit
|
||||
) {
|
||||
const innertube_url = Constants.URLS.API.PRODUCTION + this.#session.api_version;
|
||||
const baseURL = init?.baseURL || innertube_url;
|
||||
|
||||
const request_url =
|
||||
typeof input === 'string' ?
|
||||
(!baseURL.endsWith('/') && !input.startsWith('/')) ?
|
||||
new URL(`${baseURL}/${input}`) :
|
||||
new URL(baseURL + input) :
|
||||
input instanceof URL ?
|
||||
input :
|
||||
new URL(input.url, baseURL);
|
||||
|
||||
const headers =
|
||||
init?.headers ||
|
||||
(input instanceof Request ? input.headers : new Headers()) ||
|
||||
new Headers();
|
||||
|
||||
const body =
|
||||
init?.body || (input instanceof Request ? input.body : undefined);
|
||||
|
||||
const request_headers = new Headers(headers);
|
||||
|
||||
request_headers.set('Accept', '*/*');
|
||||
request_headers.set('Accept-Language', `en-${this.#session.context.client.gl || 'US'}`);
|
||||
request_headers.set('x-goog-visitor-id', this.#session.context.client.visitorData || '');
|
||||
request_headers.set('x-origin', request_url.origin);
|
||||
request_headers.set('x-youtube-client-version', this.#session.context.client.clientVersion || '');
|
||||
|
||||
if (isServer()) {
|
||||
request_headers.set('User-Agent', getRandomUserAgent('desktop').userAgent);
|
||||
request_headers.set('origin', request_url.origin);
|
||||
}
|
||||
|
||||
request_url.searchParams.set('key', this.#session.key);
|
||||
request_url.searchParams.set('prettyPrint', 'false');
|
||||
|
||||
const contentType = request_headers.get('Content-Type');
|
||||
|
||||
let request_body = body;
|
||||
|
||||
const is_innertube_req = baseURL === innertube_url;
|
||||
|
||||
// Copy context into payload when possible
|
||||
if (contentType === 'application/json' && is_innertube_req && (typeof body === 'string')) {
|
||||
const json = JSON.parse(body);
|
||||
const n_body = {
|
||||
...json,
|
||||
// Deep copy since we're gonna be modifying it
|
||||
context: JSON.parse(JSON.stringify(this.#session.context))
|
||||
};
|
||||
this.#adjustContext(n_body.context, n_body.client);
|
||||
request_headers.set('x-youtube-client-version', n_body.context.client.clientVersion);
|
||||
delete n_body.client;
|
||||
request_body = JSON.stringify(n_body);
|
||||
}
|
||||
|
||||
// Authenticate
|
||||
if (this.#session.logged_in && is_innertube_req) {
|
||||
const oauth = this.#session.oauth;
|
||||
if (oauth.validateCredentials()) {
|
||||
// Check if the access token is valid to avoid authorization errors.
|
||||
await oauth.checkAccessTokenValidity();
|
||||
request_headers.set('authorization', `Bearer ${oauth.credentials.access_token}`);
|
||||
// Remove API key as it is not required when using oauth.
|
||||
request_url.searchParams.delete('key');
|
||||
}
|
||||
if (this.#cookie) {
|
||||
const papisid = getStringBetweenStrings(this.#cookie, 'PAPISID=', ';');
|
||||
if (papisid) {
|
||||
request_headers.set('authorization', await generateSidAuth(papisid));
|
||||
}
|
||||
request_headers.set('cookie', this.#cookie);
|
||||
}
|
||||
}
|
||||
|
||||
const request = new Request(request_url, input instanceof Request ? input : init);
|
||||
|
||||
const response = await this.#fetch(request, {
|
||||
body: request_body,
|
||||
headers: request_headers,
|
||||
credentials: 'include',
|
||||
redirect: input instanceof Request ? input.redirect : init?.redirect || 'follow'
|
||||
});
|
||||
|
||||
// Check if 2xx
|
||||
if (response.ok) {
|
||||
return response;
|
||||
} throw new InnertubeError(`Request to ${response.url} failed with status ${response.status}`, response);
|
||||
}
|
||||
|
||||
#adjustContext(ctx: Context, client: string) {
|
||||
switch (client) {
|
||||
case 'YTMUSIC':
|
||||
ctx.client.clientVersion = Constants.CLIENTS.YTMUSIC.VERSION;
|
||||
ctx.client.clientName = Constants.CLIENTS.YTMUSIC.NAME;
|
||||
break;
|
||||
case 'ANDROID':
|
||||
ctx.client.clientVersion = Constants.CLIENTS.ANDROID.VERSION;
|
||||
ctx.client.clientFormFactor = 'SMALL_FORM_FACTOR';
|
||||
ctx.client.clientName = Constants.CLIENTS.ANDROID.NAME;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user