mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-07-01 02:15:42 +00:00
perf: Cache deciphered n-params by info response (#505)
This commit is contained in:
@@ -10,6 +10,7 @@ import type { FetchFunction } from '../types/PlatformShim.js';
|
||||
*/
|
||||
export default class Player {
|
||||
#nsig_sc;
|
||||
#nsig_cache;
|
||||
#sig_sc;
|
||||
#sig_sc_timestamp;
|
||||
#player_id;
|
||||
@@ -21,6 +22,8 @@ export default class Player {
|
||||
this.#sig_sc_timestamp = signature_timestamp;
|
||||
|
||||
this.#player_id = player_id;
|
||||
|
||||
this.#nsig_cache = new Map<string, string>();
|
||||
}
|
||||
|
||||
static async create(cache: ICache | undefined, fetch: FetchFunction = Platform.shim.fetch): Promise<Player> {
|
||||
@@ -66,7 +69,7 @@ export default class Player {
|
||||
return await Player.fromSource(cache, sig_timestamp, sig_sc, nsig_sc, player_id);
|
||||
}
|
||||
|
||||
decipher(url?: string, signature_cipher?: string, cipher?: string): string {
|
||||
decipher(url?: string, signature_cipher?: string, cipher?: string, this_response_nsig_cache?: Map<string, string>): string {
|
||||
url = url || signature_cipher || cipher;
|
||||
|
||||
if (!url)
|
||||
@@ -93,15 +96,23 @@ export default class Player {
|
||||
const n = url_components.searchParams.get('n');
|
||||
|
||||
if (n) {
|
||||
const nsig = Platform.shim.eval(this.#nsig_sc, {
|
||||
nsig: n
|
||||
});
|
||||
let nsig;
|
||||
|
||||
if (typeof nsig !== 'string')
|
||||
throw new PlayerError('Failed to decipher nsig');
|
||||
if (this_response_nsig_cache && this_response_nsig_cache.has(n)) {
|
||||
nsig = this_response_nsig_cache.get(n) as string;
|
||||
} else {
|
||||
nsig = Platform.shim.eval(this.#nsig_sc, {
|
||||
nsig: n
|
||||
});
|
||||
|
||||
if (nsig.startsWith('enhanced_except_')) {
|
||||
console.warn('Warning:\nCould not transform nsig, download may be throttled.\nChanging the InnerTube client to "ANDROID" might help!');
|
||||
if (typeof nsig !== 'string')
|
||||
throw new PlayerError('Failed to decipher nsig');
|
||||
|
||||
if (nsig.startsWith('enhanced_except_')) {
|
||||
console.warn('Warning:\nCould not transform nsig, download may be throttled.\nChanging the InnerTube client to "ANDROID" might help!');
|
||||
} else if (this_response_nsig_cache) {
|
||||
this_response_nsig_cache.set(n, nsig);
|
||||
}
|
||||
}
|
||||
|
||||
url_components.searchParams.set('n', nsig);
|
||||
|
||||
@@ -3,6 +3,8 @@ import { InnertubeError } from '../../../utils/Utils.js';
|
||||
import type { RawNode } from '../../index.js';
|
||||
|
||||
export default class Format {
|
||||
#this_response_nsig_cache?: Map<string, string>;
|
||||
|
||||
itag: number;
|
||||
mime_type: string;
|
||||
is_type_otf: boolean;
|
||||
@@ -52,7 +54,11 @@ export default class Format {
|
||||
matrix_coefficients?: string;
|
||||
};
|
||||
|
||||
constructor(data: RawNode) {
|
||||
constructor(data: RawNode, this_response_nsig_cache?: Map<string, string>) {
|
||||
if (this_response_nsig_cache) {
|
||||
this.#this_response_nsig_cache = this_response_nsig_cache;
|
||||
}
|
||||
|
||||
this.itag = data.itag;
|
||||
this.mime_type = data.mimeType;
|
||||
this.is_type_otf = data.type === 'FORMAT_STREAM_TYPE_OTF';
|
||||
@@ -122,6 +128,6 @@ export default class Format {
|
||||
*/
|
||||
decipher(player: Player | undefined): string {
|
||||
if (!player) throw new InnertubeError('Cannot decipher format, this session appears to have no valid player.');
|
||||
return player.decipher(this.url, this.signature_cipher, this.cipher);
|
||||
return player.decipher(this.url, this.signature_cipher, this.cipher, this.#this_response_nsig_cache);
|
||||
}
|
||||
}
|
||||
@@ -367,15 +367,21 @@ export function parseResponse<T extends IParsedResponse = IParsedResponse>(data:
|
||||
parsed_data.playability_status = playability_status;
|
||||
}
|
||||
|
||||
const streaming_data = data.streamingData ? {
|
||||
expires: new Date(Date.now() + parseInt(data.streamingData.expiresInSeconds) * 1000),
|
||||
formats: parseFormats(data.streamingData.formats),
|
||||
adaptive_formats: parseFormats(data.streamingData.adaptiveFormats),
|
||||
dash_manifest_url: data.streamingData.dashManifestUrl || null,
|
||||
hls_manifest_url: data.streamingData.hlsManifestUrl || null
|
||||
} : undefined;
|
||||
if (data.streamingData) {
|
||||
// Currently each response with streaming data only has two n param values
|
||||
// One for the adaptive formats and another for the combined formats
|
||||
// As they are the same for a response, we only need to decipher them once
|
||||
// For all futher deciphering calls on formats from that response, we can use the cached output, given the same input n param
|
||||
const this_response_nsig_cache = new Map<string, string>();
|
||||
|
||||
const streaming_data = {
|
||||
expires: new Date(Date.now() + parseInt(data.streamingData.expiresInSeconds) * 1000),
|
||||
formats: parseFormats(data.streamingData.formats, this_response_nsig_cache),
|
||||
adaptive_formats: parseFormats(data.streamingData.adaptiveFormats, this_response_nsig_cache),
|
||||
dash_manifest_url: data.streamingData.dashManifestUrl || null,
|
||||
hls_manifest_url: data.streamingData.hlsManifestUrl || null
|
||||
};
|
||||
|
||||
if (streaming_data) {
|
||||
parsed_data.streaming_data = streaming_data;
|
||||
}
|
||||
|
||||
@@ -598,8 +604,8 @@ export function parseActions(data: RawData) {
|
||||
return new SuperParsedResult(parseItem(data));
|
||||
}
|
||||
|
||||
export function parseFormats(formats: RawNode[]): Format[] {
|
||||
return formats?.map((format) => new Format(format)) || [];
|
||||
export function parseFormats(formats: RawNode[], this_response_nsig_cache: Map<string, string>): Format[] {
|
||||
return formats?.map((format) => new Format(format, this_response_nsig_cache)) || [];
|
||||
}
|
||||
|
||||
export function applyMutations(memo: Memo, mutations: RawNode[]) {
|
||||
|
||||
Reference in New Issue
Block a user