From 494299263054b5557d25107bf09647a1e03abf0f Mon Sep 17 00:00:00 2001 From: Luan Date: Thu, 8 Aug 2024 10:11:38 -0300 Subject: [PATCH] refactor: Throw an error if an invalid client is specified --- README.md | 8 ++++---- src/Innertube.ts | 2 +- src/utils/Constants.ts | 4 +++- src/utils/HTTPClient.ts | 24 ++++++++++++++++-------- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index b04526568..2bcfa662a 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ const youtube = await Innertube.create(/* options */); | `generate_session_locally` | `boolean` | Specifies whether to generate the session data locally or retrieve it from YouTube. This can be useful if you need more performance. **NOTE:** If you are using the cache option and a session has already been generated, this will be ignored. If you want to force a new session to be generated, you must clear the cache or disable session caching. | `false` | | `enable_session_cache` | `boolean` | Specifies whether to cache the session data. | `true` | | `device_category` | `DeviceCategory` | Platform to use for the session. | `DESKTOP` | -| `client_type` | `ClientType` | InnerTube client type. | `WEB` | +| `client_type` | `ClientType` | InnerTube client type. It is not recommended to change this unless you know what you are doing. | `WEB` | | `timezone` | `string` | The time zone. | `*` | | `cache` | `ICache` | Used to cache algorithms, session data, and OAuth2 tokens. | `undefined` | | `cookie` | `string` | YouTube cookies. | `undefined` | @@ -271,7 +271,7 @@ Retrieves video info. | Param | Type | Description | | --- | --- | --- | | target | `string` \| `NavigationEndpoint` | If `string`, the id of the video. If `NavigationEndpoint`, the endpoint of watchable elements such as `Video`, `Mix` and `Playlist`. To clarify, valid endpoints have payloads containing at least `videoId` and optionally `playlistId`, `params` and `index`. | -| client? | `InnerTubeClient` | `WEB`, `ANDROID`, `YTMUSIC`, `YTMUSIC_ANDROID` or `TV_EMBEDDED` | +| client? | `InnerTubeClient` | InnerTube client to use. |
Methods & Getters @@ -338,7 +338,7 @@ Suitable for cases where you only need basic video metadata. Also, it is faster | Param | Type | Description | | --- | --- | --- | | video_id | `string` | The id of the video | -| client? | `InnerTubeClient` | `WEB`, `ANDROID`, `YTMUSIC_ANDROID`, `YTMUSIC`, `TV_EMBEDDED` | +| client? | `InnerTubeClient` | InnerTube client to use. | ### `search(query, filters?)` @@ -688,7 +688,7 @@ import { Innertube } from 'youtubei.js'; const videoInfo = await yt.actions.execute('/player', { // You can add any additional payloads here, and they'll merge with the default payload sent to InnerTube. videoId, - client: 'YTMUSIC', // InnerTube client options: ANDROID, YTMUSIC, YTMUSIC_ANDROID, WEB, or TV_EMBEDDED. + client: 'YTMUSIC', // InnerTube client to use. parse: true // tells YouTube.js to parse the response (not sent to InnerTube). }); diff --git a/src/Innertube.ts b/src/Innertube.ts index 0727fc0c1..8efe55ed5 100644 --- a/src/Innertube.ts +++ b/src/Innertube.ts @@ -46,7 +46,7 @@ import type Format from './parser/classes/misc/Format.js'; export type InnertubeConfig = SessionOptions; -export type InnerTubeClient = 'WEB' | 'iOS' | 'ANDROID' | 'YTMUSIC_ANDROID' | 'YTMUSIC' | 'YTSTUDIO_ANDROID' | 'TV_EMBEDDED' | 'YTKIDS'; +export type InnerTubeClient = 'IOS' | 'WEB' | 'ANDROID' | 'YTMUSIC' | 'YTMUSIC_ANDROID' | 'YTSTUDIO_ANDROID' | 'TV_EMBEDDED' | 'YTKIDS'; export type SearchFilters = Partial<{ upload_date: 'all' | 'hour' | 'today' | 'week' | 'month' | 'year'; diff --git a/src/utils/Constants.ts b/src/utils/Constants.ts index 2e6335974..3cb6dd013 100644 --- a/src/utils/Constants.ts +++ b/src/utils/Constants.ts @@ -22,7 +22,7 @@ export const OAUTH = Object.freeze({ }) }); export const CLIENTS = Object.freeze({ - iOS: { + IOS: { NAME_ID: '5', NAME: 'iOS', VERSION: '18.06.35', @@ -81,3 +81,5 @@ export const INNERTUBE_HEADERS_BASE = Object.freeze({ 'accept-encoding': 'gzip, deflate', 'content-type': 'application/json' }); + +export const SUPPORTED_CLIENTS = [ 'IOS', 'WEB', 'YTKIDS', 'YTMUSIC', 'ANDROID', 'YTSTUDIO_ANDROID', 'YTMUSIC_ANDROID', 'TV_EMBEDDED' ]; \ No newline at end of file diff --git a/src/utils/HTTPClient.ts b/src/utils/HTTPClient.ts index 0e6ea657c..8d11bd5a0 100644 --- a/src/utils/HTTPClient.ts +++ b/src/utils/HTTPClient.ts @@ -91,7 +91,7 @@ export default class HTTPClient { const n_body = { ...json, // Deep copy since we're gonna be modifying it - context: JSON.parse(JSON.stringify(this.#session.context)) + context: JSON.parse(JSON.stringify(this.#session.context)) as Context }; this.#adjustContext(n_body.context, n_body.client); @@ -111,7 +111,7 @@ export default class HTTPClient { request_headers.set('User-Agent', Constants.CLIENTS.ANDROID.USER_AGENT); request_headers.set('X-GOOG-API-FORMAT-VERSION', '2'); } else if (n_body.context.client.clientName === 'iOS') { - request_headers.set('User-Agent', Constants.CLIENTS.iOS.USER_AGENT); + request_headers.set('User-Agent', Constants.CLIENTS.IOS.USER_AGENT); } is_web_kids = n_body.context.client.clientName === 'WEB_KIDS'; @@ -165,7 +165,15 @@ export default class HTTPClient { throw new InnertubeError(`Request to ${response.url} failed with status ${response.status}`, await response.text()); } - #adjustContext(ctx: Context, client: string): void { + #adjustContext(ctx: Context, client?: string): void { + if (!client) + return; + + if (!Constants.SUPPORTED_CLIENTS.includes(client.toUpperCase())) + throw new InnertubeError(`Invalid client: ${client}`, { + available_innertube_clients: Constants.SUPPORTED_CLIENTS + }); + if ( client === 'ANDROID' || client === 'YTMUSIC_ANDROID' || @@ -179,12 +187,12 @@ export default class HTTPClient { ctx.client.platform = 'MOBILE'; } - switch (client) { - case 'iOS': + switch (client.toUpperCase()) { + case 'IOS': ctx.client.deviceMake = 'Apple'; - ctx.client.deviceModel = Constants.CLIENTS.iOS.DEVICE_MODEL; - ctx.client.clientVersion = Constants.CLIENTS.iOS.VERSION; - ctx.client.clientName = Constants.CLIENTS.iOS.NAME; + ctx.client.deviceModel = Constants.CLIENTS.IOS.DEVICE_MODEL; + ctx.client.clientVersion = Constants.CLIENTS.IOS.VERSION; + ctx.client.clientName = Constants.CLIENTS.IOS.NAME; ctx.client.platform = 'MOBILE'; ctx.client.osName = 'iOS'; delete ctx.client.browserName;