Skip to content

Commit

Permalink
refactor(Parser)!: general refactoring of parsers (#344)
Browse files Browse the repository at this point in the history
* refactor: move common info into MediaInfo

* refactor: better inference on Memo

* refactor: improved typesafety in parser methods

* refactor: remove PlaylistAuthor in favor of Author

* refactor: cleanup live chat parsers

- Replace non standard author type with Author class
- Remove redundant code

* fix: new errors due to changes

* fix: pass actions to FormatUtils#toDash

* refactor!: merge NavigatableText and Text into single class
  • Loading branch information
Wykerd authored Mar 15, 2023
1 parent 3d34364 commit b13bf6e
Show file tree
Hide file tree
Showing 78 changed files with 398 additions and 735 deletions.
4 changes: 2 additions & 2 deletions src/Innertube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class Innertube {
const continuation = this.actions.execute('/next', payload);

const response = await Promise.all([ initial_info, continuation ]);
return new VideoInfo(response, this.actions, this.session.player, cpn);
return new VideoInfo(response, this.actions, cpn);
}

/**
Expand All @@ -130,7 +130,7 @@ class Innertube {
const cpn = generateRandomString(16);
const response = await this.actions.getVideoInfo(video_id, cpn, client);

return new VideoInfo([ response ], this.actions, this.session.player, cpn);
return new VideoInfo([ response ], this.actions, cpn);
}

/**
Expand Down
12 changes: 6 additions & 6 deletions src/core/Feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,22 +72,22 @@ class Feed<T extends IParsedResponse = IParsedResponse> {
* Get all videos on a given page via memo
*/
static getVideosFromMemo(memo: Memo) {
return memo.getType<Video | GridVideo | ReelItem | CompactVideo | PlaylistVideo | PlaylistPanelVideo | WatchCardCompactVideo>([
return memo.getType(
Video,
GridVideo,
ReelItem,
CompactVideo,
PlaylistVideo,
PlaylistPanelVideo,
WatchCardCompactVideo
]);
);
}

/**
* Get all playlists on a given page via memo
*/
static getPlaylistsFromMemo(memo: Memo) {
return memo.getType<Playlist | GridPlaylist>([ Playlist, GridPlaylist ]);
return memo.getType(Playlist, GridPlaylist);
}

/**
Expand All @@ -101,14 +101,14 @@ class Feed<T extends IParsedResponse = IParsedResponse> {
* Get all the community posts in the feed
*/
get posts() {
return this.#memo.getType<Post | BackstagePost | SharedPost>([ BackstagePost, Post, SharedPost ]);
return this.#memo.getType(BackstagePost, Post, SharedPost);
}

/**
* Get all the channels in the feed
*/
get channels() {
return this.#memo.getType<Channel | GridChannel>([ Channel, GridChannel ]);
return this.#memo.getType(Channel, GridChannel);
}

/**
Expand Down Expand Up @@ -137,7 +137,7 @@ class Feed<T extends IParsedResponse = IParsedResponse> {
* Returns all segments/sections from the page.
*/
get shelves() {
return this.#memo.getType<Shelf | RichShelf | ReelShelf>([ Shelf, RichShelf, ReelShelf ]);
return this.#memo.getType(Shelf, RichShelf, ReelShelf);
}

/**
Expand Down
103 changes: 103 additions & 0 deletions src/core/MediaInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import Actions, { ApiResponse } from './Actions.js';
import Constants from '../utils/Constants.js';
import FormatUtils, { DownloadOptions, FormatFilter, FormatOptions, URLTransformer } from '../utils/FormatUtils.js';
import { InnertubeError } from '../utils/Utils.js';
import Format from '../parser/classes/misc/Format.js';
import Parser, { INextResponse, IPlayerResponse } from '../parser/index.js';

export class MediaInfo {
#page: [IPlayerResponse, INextResponse?];
#actions: Actions;
#cpn: string;
#playback_tracking;
streaming_data;
playability_status;

constructor(data: [ApiResponse, ApiResponse?], actions: Actions, cpn: string) {
this.#actions = actions;

const info = Parser.parseResponse<IPlayerResponse>(data[0].data);
const next = data?.[1]?.data ? Parser.parseResponse<INextResponse>(data[1].data) : undefined;

this.#page = [ info, next ];
this.#cpn = cpn;

if (info.playability_status?.status === 'ERROR')
throw new InnertubeError('This video is unavailable', info.playability_status);

this.streaming_data = info.streaming_data;
this.playability_status = info.playability_status;
this.#playback_tracking = info.playback_tracking;
}

/**
* Generates a DASH manifest from the streaming data.
* @param url_transformer - Function to transform the URLs.
* @param format_filter - Function to filter the formats.
* @returns DASH manifest
*/
async toDash(url_transformer?: URLTransformer, format_filter?: FormatFilter): Promise<string> {
return FormatUtils.toDash(this.streaming_data, url_transformer, format_filter, this.#cpn, this.#actions.session.player, this.#actions);
}

/**
* Selects the format that best matches the given options.
* @param options - Options
*/
chooseFormat(options: FormatOptions): Format {
return FormatUtils.chooseFormat(options, this.streaming_data);
}

/**
* Downloads the video.
* @param options - Download options.
*/
async download(options: DownloadOptions = {}): Promise<ReadableStream<Uint8Array>> {
return FormatUtils.download(options, this.#actions, this.playability_status, this.streaming_data, this.#actions.session.player, this.cpn);
}

/**
* Adds video to the watch history.
*/
async addToWatchHistory(client_name = Constants.CLIENTS.WEB.NAME, client_version = Constants.CLIENTS.WEB.VERSION, replacement = 'https://www.'): Promise<Response> {
if (!this.#playback_tracking)
throw new InnertubeError('Playback tracking not available');

const url_params = {
cpn: this.#cpn,
fmt: 251,
rtn: 0,
rt: 0
};

const url = this.#playback_tracking.videostats_playback_url.replace('https://s.', replacement);

const response = await this.#actions.stats(url, {
client_name,
client_version
}, url_params);

return response;
}

/**
* Actions instance.
*/
get actions(): Actions {
return this.#actions;
}

/**
* Content Playback Nonce.
*/
get cpn(): string {
return this.#cpn;
}

/**
* Original parsed InnerTube response.
*/
get page(): [IPlayerResponse, INextResponse?] {
return this.#page;
}
}
2 changes: 1 addition & 1 deletion src/parser/classes/AccountItemSection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class AccountItemSection extends YTNode {
constructor(data: RawNode) {
super();
this.contents = data.contents.map((ac: any) => new AccountItem(ac.accountItem));
this.header = Parser.parseItem<AccountItemSectionHeader>(data.header, AccountItemSectionHeader);
this.header = Parser.parseItem(data.header, AccountItemSectionHeader);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/parser/classes/AccountSectionList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class AccountSectionList extends YTNode {

constructor(data: RawNode) {
super();
this.contents = Parser.parseItem<AccountItemSection>(data.contents[0], AccountItemSection);
this.footers = Parser.parseItem<AccountChannel>(data.footers[0], AccountChannel);
this.contents = Parser.parseItem(data.contents[0], AccountItemSection);
this.footers = Parser.parseItem(data.footers[0], AccountChannel);
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/parser/classes/BackstagePost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import Parser from '../index.js';
import Author from './misc/Author.js';
import Text from './misc/Text.js';
import NavigationEndpoint from './NavigationEndpoint.js';
import type CommentActionButtons from './comments/CommentActionButtons.js';
import type Menu from './menus/Menu.js';
import CommentActionButtons from './comments/CommentActionButtons.js';
import Menu from './menus/Menu.js';

import { YTNode } from '../helpers.js';

Expand Down Expand Up @@ -49,15 +49,15 @@ class BackstagePost extends YTNode {
}

if (data.actionMenu) {
this.menu = Parser.parseItem<Menu>(data.actionMenu);
this.menu = Parser.parseItem(data.actionMenu, Menu);
}

if (data.actionButtons) {
this.action_buttons = Parser.parseItem<CommentActionButtons>(data.actionButtons);
this.action_buttons = Parser.parseItem(data.actionButtons, CommentActionButtons);
}

if (data.voteButton) {
this.vote_button = Parser.parseItem(data.voteButton);
this.vote_button = Parser.parseItem(data.voteButton, CommentActionButtons);
}

if (data.navigationEndpoint) {
Expand Down
14 changes: 7 additions & 7 deletions src/parser/classes/C4TabbedHeader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import Author from './misc/Author.js';
import Text from './misc/Text.js';
import Thumbnail from './misc/Thumbnail.js';

import type Button from './Button.js';
import type ChannelHeaderLinks from './ChannelHeaderLinks.js';
import type SubscribeButton from './SubscribeButton.js';
import Button from './Button.js';
import ChannelHeaderLinks from './ChannelHeaderLinks.js';
import SubscribeButton from './SubscribeButton.js';

import { YTNode } from '../helpers.js';

Expand All @@ -19,7 +19,7 @@ class C4TabbedHeader extends YTNode {
subscribers?: Text;
videos_count?: Text;
sponsor_button?: Button | null;
subscribe_button?: SubscribeButton | null;
subscribe_button?: SubscribeButton | Button | null;
header_links?: ChannelHeaderLinks | null;
channel_handle?: Text;
channel_id?: string;
Expand Down Expand Up @@ -52,15 +52,15 @@ class C4TabbedHeader extends YTNode {
}

if (data.sponsorButton) {
this.sponsor_button = Parser.parseItem<Button>(data.sponsorButton);
this.sponsor_button = Parser.parseItem(data.sponsorButton, Button);
}

if (data.subscribeButton) {
this.subscribe_button = Parser.parseItem<SubscribeButton>(data.subscribeButton);
this.subscribe_button = Parser.parseItem(data.subscribeButton, [ SubscribeButton, Button ]);
}

if (data.headerLinks) {
this.header_links = Parser.parseItem<ChannelHeaderLinks>(data.headerLinks);
this.header_links = Parser.parseItem(data.headerLinks, ChannelHeaderLinks);
}

if (data.channelHandleText) {
Expand Down
4 changes: 2 additions & 2 deletions src/parser/classes/Channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Text from './misc/Text.js';
import Author from './misc/Author.js';
import NavigationEndpoint from './NavigationEndpoint.js';

import type SubscribeButton from './SubscribeButton.js';
import SubscribeButton from './SubscribeButton.js';

import { YTNode } from '../helpers.js';

Expand Down Expand Up @@ -36,7 +36,7 @@ class Channel extends YTNode {
this.long_byline = new Text(data.longBylineText);
this.short_byline = new Text(data.shortBylineText);
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
this.subscribe_button = Parser.parseItem<SubscribeButton>(data.subscribeButton);
this.subscribe_button = Parser.parseItem(data.subscribeButton, SubscribeButton);
this.description_snippet = new Text(data.descriptionSnippet);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/parser/classes/ChannelAboutFullMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Text from './misc/Text.js';
import Thumbnail from './misc/Thumbnail.js';
import NavigationEndpoint from './NavigationEndpoint.js';

import type Button from './Button.js';
import Button from './Button.js';

import { YTNode } from '../helpers.js';

Expand Down Expand Up @@ -49,7 +49,7 @@ class ChannelAboutFullMetadata extends YTNode {
this.email_reveal = new NavigationEndpoint(data.onBusinessEmailRevealClickCommand);
this.can_reveal_email = !data.signInForBusinessEmail;
this.country = new Text(data.country);
this.buttons = Parser.parseArray<Button>(data.actionButtons);
this.buttons = Parser.parseArray(data.actionButtons, Button);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/parser/classes/ChannelAgeGate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class ChannelAgeGate extends YTNode {
this.avatar = Thumbnail.fromResponse(data.avatar);
this.header = new Text(data.header);
this.main_text = new Text(data.mainText);
this.sign_in_button = Parser.parseItem<Button>(data.signInButton, Button);
this.sign_in_button = Parser.parseItem(data.signInButton, Button);
this.secondary_text = new Text(data.secondaryText);
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/parser/classes/ChipCloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ class ChipCloud extends YTNode {
constructor(data: any) {
super();
// TODO: check this assumption that chipcloudchip is always returned
this.chips = Parser.parseArray<ChipCloudChip>(data.chips, ChipCloudChip);
this.next_button = Parser.parseItem<Button>(data.nextButton, Button);
this.previous_button = Parser.parseItem<Button>(data.previousButton, Button);
this.chips = Parser.parseArray(data.chips, ChipCloudChip);
this.next_button = Parser.parseItem(data.nextButton, Button);
this.previous_button = Parser.parseItem(data.previousButton, Button);
this.horizontal_scrollable = data.horizontalScrollable;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/parser/classes/CompactChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Parser from '../index.js';
import Text from './misc/Text.js';
import Thumbnail from './misc/Thumbnail.js';
import NavigationEndpoint from './NavigationEndpoint.js';
import type Menu from './menus/Menu.js';
import Menu from './menus/Menu.js';
import { YTNode } from '../helpers.js';

class CompactChannel extends YTNode {
Expand All @@ -28,7 +28,7 @@ class CompactChannel extends YTNode {
this.subscriber_count = new Text(data.subscriberCountText);
this.endpoint = new NavigationEndpoint(data.navigationEndpoint);
this.tv_banner = Thumbnail.fromResponse(data.tvBanner);
this.menu = Parser.parseItem<Menu>(data.menu);
this.menu = Parser.parseItem(data.menu, Menu);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/parser/classes/ConfirmDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class ConfirmDialog extends YTNode {
constructor (data: any) {
super();
this.title = new Text(data.title);
this.confirm_button = Parser.parseItem<Button>(data.confirmButton, Button);
this.cancel_button = Parser.parseItem<Button>(data.cancelButton, Button);
this.confirm_button = Parser.parseItem(data.confirmButton, Button);
this.cancel_button = Parser.parseItem(data.cancelButton, Button);
this.dialog_messages = data.dialogMessages.map((txt: any) => new Text(txt));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/parser/classes/ConversationBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class ConversationBar extends YTNode {

constructor(data: RawNode) {
super();
this.availability_message = Parser.parseItem<Message>(data.availabilityMessage, Message);
this.availability_message = Parser.parseItem(data.availabilityMessage, Message);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/parser/classes/CopyLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class CopyLink extends YTNode {

constructor(data: any) {
super();
this.copy_button = Parser.parseItem<Button>(data.copyButton, Button);
this.copy_button = Parser.parseItem(data.copyButton, Button);
this.short_url = data.shortUrl;
this.style = data.style;
}
Expand Down
4 changes: 2 additions & 2 deletions src/parser/classes/CreatePlaylistDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ class CreatePlaylistDialog extends YTNode {
this.title = new Text(data.dialogTitle).toString();
this.title_placeholder = data.titlePlaceholder || '';
this.privacy_option = Parser.parseItem(data.privacyOption, Dropdown)?.entries || null;
this.create_button = Parser.parseItem(data.cancelButton);
this.cancel_button = Parser.parseItem(data.cancelButton);
this.create_button = Parser.parseItem(data.cancelButton, Button);
this.cancel_button = Parser.parseItem(data.cancelButton, Button);
}
}

Expand Down
Loading

0 comments on commit b13bf6e

Please sign in to comment.