Skip to content

Commit

Permalink
perf: Cache deciphered n-params by info response (#505)
Browse files Browse the repository at this point in the history
  • Loading branch information
absidue authored Sep 17, 2023
1 parent 68df321 commit d2959b3
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 23 deletions.
31 changes: 21 additions & 10 deletions src/core/Player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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> {
Expand Down Expand Up @@ -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)
Expand All @@ -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
});

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!');
let 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 (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);
Expand Down
10 changes: 8 additions & 2 deletions src/parser/classes/misc/Format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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);
}
}
28 changes: 17 additions & 11 deletions src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (streaming_data) {
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
};

parsed_data.streaming_data = streaming_data;
}

Expand Down Expand Up @@ -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[]) {
Expand Down

0 comments on commit d2959b3

Please sign in to comment.