Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(general): Clean up and add a logger #587

Merged
merged 4 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 32 additions & 30 deletions src/Innertube.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,8 @@
import type { SessionOptions } from './core/Session.js';
import Session from './core/Session.js';

import NavigationEndpoint from './parser/classes/NavigationEndpoint.js';
import type Format from './parser/classes/misc/Format.js';
import Channel from './parser/youtube/Channel.js';
import Comments from './parser/youtube/Comments.js';
import Guide from './parser/youtube/Guide.js';
import HashtagFeed from './parser/youtube/HashtagFeed.js';
import History from './parser/youtube/History.js';
import HomeFeed from './parser/youtube/HomeFeed.js';
import Library from './parser/youtube/Library.js';
import NotificationsMenu from './parser/youtube/NotificationsMenu.js';
import Playlist from './parser/youtube/Playlist.js';
import Search from './parser/youtube/Search.js';
import VideoInfo from './parser/youtube/VideoInfo.js';
import ShortsVideoInfo from './parser/ytshorts/VideoInfo.js';

import { Kids, Music, Studio } from './core/clients/index.js';
import { AccountManager, InteractionManager, PlaylistManager } from './core/managers/index.js';
import { Feed, TabbedFeed } from './core/mixins/index.js';

import * as Proto from './proto/index.js';
import * as Constants from './utils/Constants.js';
import { InnertubeError, generateRandomString, throwIfMissing } from './utils/Utils.js';

import {
BrowseEndpoint,
GetNotificationMenuEndpoint,
Expand All @@ -32,16 +11,39 @@ import {
PlayerEndpoint,
ResolveURLEndpoint,
SearchEndpoint,
Reel
Reel,
Notification
} from './core/endpoints/index.js';

import { GetUnseenCountEndpoint } from './core/endpoints/notification/index.js';
import {
Channel,
Comments,
Guide,
HashtagFeed,
History,
HomeFeed,
Library,
NotificationsMenu,
Playlist,
Search,
VideoInfo
} from './parser/youtube/index.js';

import { VideoInfo as ShortsVideoInfo } from './parser/ytshorts/index.js';

import NavigationEndpoint from './parser/classes/NavigationEndpoint.js';

import * as Proto from './proto/index.js';
import * as Constants from './utils/Constants.js';
import { InnertubeError, generateRandomString, throwIfMissing } from './utils/Utils.js';


import type { ApiResponse } from './core/Actions.js';
import { type IBrowseResponse, type IParsedResponse } from './parser/types/index.js';
import type { INextRequest } from './types/index.js';
import type { IBrowseResponse, IParsedResponse } from './parser/types/index.js';
import type { DownloadOptions, FormatOptions } from './types/FormatUtils.js';
import { encodeReelSequence } from './proto/index.js';
import type { SessionOptions } from './core/Session.js';
import type Format from './parser/classes/misc/Format.js';

export type InnertubeConfig = SessionOptions;

Expand Down Expand Up @@ -151,7 +153,7 @@ export default class Innertube {

const sequenceResponse = this.actions.execute(
Reel.WatchSequenceEndpoint.PATH, Reel.WatchSequenceEndpoint.build({
sequenceParams: encodeReelSequence(short_id)
sequenceParams: Proto.encodeReelSequence(short_id)
})
);

Expand Down Expand Up @@ -261,7 +263,7 @@ export default class Innertube {
}

/**
* Retrieves trending content.
* Retrieves Trending content.
*/
async getTrending(): Promise<TabbedFeed<IBrowseResponse>> {
const response = await this.actions.execute(
Expand All @@ -271,7 +273,7 @@ export default class Innertube {
}

/**
* Retrieves subscriptions feed.
* Retrieves Subscriptions feed.
*/
async getSubscriptionsFeed(): Promise<Feed<IBrowseResponse>> {
const response = await this.actions.execute(
Expand All @@ -281,7 +283,7 @@ export default class Innertube {
}

/**
* Retrieves channels feed.
* Retrieves Channels feed.
*/
async getChannelsFeed(): Promise<Feed<IBrowseResponse>> {
const response = await this.actions.execute(
Expand Down Expand Up @@ -318,7 +320,7 @@ export default class Innertube {
* Retrieves unseen notifications count.
*/
async getUnseenNotificationsCount(): Promise<number> {
const response = await this.actions.execute(GetUnseenCountEndpoint.PATH);
const response = await this.actions.execute(Notification.GetUnseenCountEndpoint.PATH);
// TODO: properly parse this
return response.data?.unseenCount || response.data?.actions?.[0].updateNotificationsUnseenCountAction?.unseenCount || 0;
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/Actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Parser, NavigateAction } from '../parser/index.js';
import { InnertubeError } from '../utils/Utils.js';

import type Session from './Session.js';
import type { Session } from './index.js';

import type {
IBrowseResponse, IGetNotificationsMenuResponse,
Expand Down
9 changes: 8 additions & 1 deletion src/core/OAuth.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as Constants from '../utils/Constants.js';
import { Log, Constants } from '../utils/index.js';
import { OAuthError, Platform } from '../utils/Utils.js';
import type Session from './Session.js';

Expand Down Expand Up @@ -45,6 +45,8 @@ export type OAuthClientIdentity = {
};

export default class OAuth {
static TAG = 'OAuth';

#identity?: Record<string, string>;
#session: Session;
#credentials?: Credentials;
Expand Down Expand Up @@ -250,6 +252,7 @@ export default class OAuth {
*/
async #getClientIdentity(): Promise<OAuthClientIdentity> {
if (this.#credentials?.client_id && this.credentials?.client_secret) {
Log.info(OAuth.TAG, 'Using custom OAuth2 credentials.\n');
return {
client_id: this.#credentials.client_id,
client_secret: this.credentials.client_secret
Expand All @@ -264,6 +267,8 @@ export default class OAuth {
if (!url_body)
throw new OAuthError('Could not obtain script url.', { status: 'FAILED' });

Log.info(OAuth.TAG, `Got YouTubeTV script URL (${url_body})`);

const script = await this.#session.http.fetch(url_body, { baseURL: Constants.URLS.YT_BASE });

const client_identity = (await script.text())
Expand All @@ -275,6 +280,8 @@ export default class OAuth {
if (!groups)
throw new OAuthError('Could not obtain client identity.', { status: 'FAILED' });

Log.info(OAuth.TAG, 'OAuth2 credentials retrieved.\n', groups);

return groups;
}

Expand Down
35 changes: 23 additions & 12 deletions src/core/Player.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Log, Constants } from '../utils/index.js';
import { Platform, getRandomUserAgent, getStringBetweenStrings, PlayerError } from '../utils/Utils.js';

import * as Constants from '../utils/Constants.js';

import type { ICache } from '../types/Cache.js';
import type { FetchFunction } from '../types/PlatformShim.js';
import type { ICache, FetchFunction } from '../types/index.js';

/**
* Represents YouTube's player script. This is required to decipher signatures.
*/
export default class Player {
static TAG = 'Player';

#nsig_sc;
#sig_sc;
#sig_sc_timestamp;
Expand All @@ -17,9 +16,7 @@ export default class Player {
constructor(signature_timestamp: number, sig_sc: string, nsig_sc: string, player_id: string) {
this.#nsig_sc = nsig_sc;
this.#sig_sc = sig_sc;

this.#sig_sc_timestamp = signature_timestamp;

this.#player_id = player_id;
}

Expand All @@ -34,18 +31,23 @@ export default class Player {

const player_id = getStringBetweenStrings(js, 'player\\/', '\\/');

Log.info(Player.TAG, `Got player id (${player_id}). Checking for cached players..`);

if (!player_id)
throw new PlayerError('Failed to get player id');

// We have the playerID now we can check if we have a cached player
// We have the player id, now we can check if we have a cached player.
if (cache) {
Log.info(Player.TAG, 'Found a cached player.');
const cached_player = await Player.fromCache(cache, player_id);
if (cached_player)
return cached_player;
}

const player_url = new URL(`/s/player/${player_id}/player_ias.vflset/en_US/base.js`, Constants.URLS.YT_BASE);

Log.info(Player.TAG, `Could not find any cached player. Will download a new player from ${player_url}.`);

const player_res = await fetch(player_url, {
headers: {
'user-agent': getRandomUserAgent('desktop')
Expand All @@ -59,10 +61,11 @@ export default class Player {
const player_js = await player_res.text();

const sig_timestamp = this.extractSigTimestamp(player_js);

const sig_sc = this.extractSigSourceCode(player_js);
const nsig_sc = this.extractNSigSourceCode(player_js);

Log.info(Player.TAG, `Got signature timestamp (${sig_timestamp}) and algorithms needed to decipher signatures.`);

return await Player.fromSource(cache, sig_timestamp, sig_sc, nsig_sc, player_id);
}

Expand All @@ -80,6 +83,8 @@ export default class Player {
sig: args.get('s')
});

Log.info(Player.TAG, `Transformed signature ${args.get('s')} to ${signature}.`);

if (typeof signature !== 'string')
throw new PlayerError('Failed to decipher signature');

Expand All @@ -102,11 +107,13 @@ export default class Player {
nsig: n
});

Log.info(Player.TAG, `Transformed nsig ${n} to ${nsig}.`);

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!');
Log.warn(Player.TAG, 'Could 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);
}
Expand Down Expand Up @@ -138,6 +145,10 @@ export default class Player {
break;
}

const result = url_components.toString();

Log.info(Player.TAG, `Full deciphered URL: ${result}`);

return url_components.toString();
}

Expand Down Expand Up @@ -204,7 +215,7 @@ export default class Player {
const functions = getStringBetweenStrings(data, `var ${obj_name}={`, '};');

if (!functions || !calls)
console.warn(new PlayerError('Failed to extract signature decipher algorithm'));
Log.warn(Player.TAG, 'Failed to extract signature decipher algorithm.');

return `function descramble_sig(a) { a = a.split(""); let ${obj_name}={${functions}}${calls} return a.join("") } descramble_sig(sig);`;
}
Expand All @@ -213,7 +224,7 @@ export default class Player {
const sc = `function descramble_nsig(a) { let b=a.split("")${getStringBetweenStrings(data, 'b=a.split("")', '}return b.join("")}')}} return b.join(""); } descramble_nsig(nsig)`;

if (!sc)
console.warn(new PlayerError('Failed to extract n-token decipher algorithm'));
Log.warn(Player.TAG, 'Failed to extract n-token decipher algorithm');

return sc;
}
Expand Down
Loading
Loading