diff --git a/src/Innertube.ts b/src/Innertube.ts index 48110d9d6..d594c3b3c 100644 --- a/src/Innertube.ts +++ b/src/Innertube.ts @@ -52,6 +52,11 @@ import { Hashtag, SearchFilter, ReelSequence, GetCommentsSectionParams } from '. /** * Provides access to various services and modules in the YouTube API. + * + * @example + * ```ts + * import { Innertube, UniversalCache } from 'youtubei.js'; + * const innertube = await Innertube.create({ cache: new UniversalCache(true)}); */ export default class Innertube { #session: Session; @@ -427,7 +432,6 @@ export default class Innertube { /** * Resolves the given URL. - * @param url - The URL. */ async resolveURL(url: string): Promise { const response = await this.actions.execute( @@ -442,8 +446,6 @@ export default class Innertube { /** * Utility method to call an endpoint without having to use {@link Actions}. - * @param endpoint -The endpoint to call. - * @param args - Call arguments. */ call(endpoint: NavigationEndpoint, args: { [key: string]: any; parse: true }): Promise; call(endpoint: NavigationEndpoint, args?: { [key: string]: any; parse?: false }): Promise; diff --git a/src/core/Player.ts b/src/core/Player.ts index 4c5fd43c8..e9a7e924c 100644 --- a/src/core/Player.ts +++ b/src/core/Player.ts @@ -25,8 +25,8 @@ export default class Player { const url = new URL('/iframe_api', Constants.URLS.YT_BASE); const res = await fetch(url); - if (res.status !== 200) - throw new PlayerError('Failed to request player id'); + if (!res.ok) + throw new PlayerError(`Failed to get player id: ${res.status} (${res.statusText})`); const js = await res.text(); @@ -121,7 +121,7 @@ export default class Player { throw new PlayerError('Failed to decipher nsig'); if (nsig.startsWith('enhanced_except_')) { - Log.warn(TAG, 'Could not transform nsig, download may be throttled.'); + Log.warn(TAG, 'Something went wrong while deciphering nsig.'); } else if (this_response_nsig_cache) { this_response_nsig_cache.set(n, nsig); } @@ -146,6 +146,9 @@ export default class Player { case 'WEB_KIDS': url_components.searchParams.set('cver', Constants.CLIENTS.WEB_KIDS.VERSION); break; + case 'TVHTML5': + url_components.searchParams.set('cver', Constants.CLIENTS.TV.VERSION); + break; case 'TVHTML5_SIMPLY_EMBEDDED_PLAYER': url_components.searchParams.set('cver', Constants.CLIENTS.TV_EMBEDDED.VERSION); break; @@ -242,4 +245,4 @@ export default class Player { static get LIBRARY_VERSION(): number { return 11; } -} +} \ No newline at end of file diff --git a/src/parser/classes/misc/Format.ts b/src/parser/classes/misc/Format.ts index 9bdbe2a8d..4254b7f8b 100644 --- a/src/parser/classes/misc/Format.ts +++ b/src/parser/classes/misc/Format.ts @@ -1,7 +1,35 @@ import type Player from '../../../core/Player.js'; -import { InnertubeError } from '../../../utils/Utils.js'; import type { RawNode } from '../../index.js'; +export type ProjectionType = 'RECTANGULAR' | 'EQUIRECTANGULAR' | 'EQUIRECTANGULAR_THREED_TOP_BOTTOM' | 'MESH'; +export type SpatialAudioType = 'AMBISONICS_5_1' | 'AMBISONICS_QUAD' | 'FOA_WITH_NON_DIEGETIC'; +export type StereoLayout = 'LEFT_RIGHT' | 'TOP_BOTTOM'; + +export type Range = { + start: number; + end: number; +}; + +export type ColorInfo = { + primaries?: string; + transfer_characteristics?: string; + matrix_coefficients?: string; +}; + +export type AudioTrack = { + audio_is_default: boolean; + display_name: string; + id: string; +}; + +export type CaptionTrack = { + display_name: string; + vss_id: string; + language_code: string; + kind?: 'asr' | 'frc'; + id: string; +}; + export default class Format { #this_response_nsig_cache?: Map; @@ -17,13 +45,13 @@ export default class Format { drm_families?: string[]; fps?: number; quality_label?: string; - projection_type?: 'RECTANGULAR' | 'EQUIRECTANGULAR' | 'EQUIRECTANGULAR_THREED_TOP_BOTTOM' | 'MESH'; + projection_type?: ProjectionType; average_bitrate?: number; bitrate: number; - spatial_audio_type?: 'AMBISONICS_5_1' | 'AMBISONICS_QUAD' | 'FOA_WITH_NON_DIEGETIC'; + spatial_audio_type?: SpatialAudioType; target_duration_dec?: number; fair_play_key_uri?: string; - stereo_layout?: 'LEFT_RIGHT' | 'TOP_BOTTOM'; + stereo_layout?: StereoLayout; max_dvr_duration_sec?: number; high_replication?: boolean; audio_quality?: string; @@ -38,20 +66,10 @@ export default class Format { track_absolute_loudness_lkfs?: number; mime_type: string; is_type_otf: boolean; - init_range?: { - start: number; - end: number; - }; - index_range?: { - start: number; - end: number; - }; + init_range?: Range; + index_range?: Range; cipher?: string; - audio_track?: { - audio_is_default: boolean; - display_name: string; - id: string; - }; + audio_track?: AudioTrack; has_audio: boolean; has_video: boolean; has_text: boolean; @@ -60,23 +78,12 @@ export default class Format { is_descriptive?: boolean; is_secondary?: boolean; is_original?: boolean; - color_info?: { - primaries?: string; - transfer_characteristics?: string; - matrix_coefficients?: string; - }; - caption_track?: { - display_name: string; - vss_id: string; - language_code: string; - kind?: 'asr' | 'frc'; - id: string; - }; + color_info?: ColorInfo; + caption_track?: CaptionTrack; constructor(data: RawNode, this_response_nsig_cache?: Map) { - if (this_response_nsig_cache) { + if (this_response_nsig_cache) this.#this_response_nsig_cache = this_response_nsig_cache; - } this.itag = data.itag; this.mime_type = data.mimeType; @@ -228,11 +235,13 @@ export default class Format { } /** - * Deciphers the streaming url of the format. - * @returns Deciphered URL. + * Deciphers the URL using the provided player instance. + * @param player - An optional instance of the Player class used to decipher the URL. + * @returns The deciphered URL as a string. If no player is provided, returns the original URL or an empty string. */ - decipher(player: Player | undefined): string { - if (!player) throw new InnertubeError('Cannot decipher format, this session appears to have no valid player.'); + decipher(player?: Player): string { + if (!player) + return this.url || ''; return player.decipher(this.url, this.signature_cipher, this.cipher, this.#this_response_nsig_cache); } } \ No newline at end of file diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 9aceda39e..51136f4cf 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -7,6 +7,7 @@ import { Jinter } from 'jintr'; import type { EmojiRun, TextRun } from '../parser/misc.js'; import type { FetchFunction } from '../types/PlatformShim.js'; import type PlatformShim from '../types/PlatformShim.js'; +import type { Node } from 'estree'; const TAG_ = 'Utils'; @@ -289,7 +290,7 @@ export function findFunction(source: string, args: FindFunctionArgs): FindFuncti const { name, includes, regexp } = args; const node = Jinter.parseScript(source); - const stack = [ node ]; + const stack = [ node ] as (Node & { start: number; end: number})[]; for (let i = 0; i < stack.length; i++) { const current = stack[i]; @@ -319,7 +320,7 @@ export function findFunction(source: string, args: FindFunctionArgs): FindFuncti } for (const key in current) { - const child = current[key]; + const child = (current as Record)[key]; if (Array.isArray(child)) { stack.push(...child); } else if (typeof child === 'object' && child !== null) {