refactor: Clean up & fix old code

Other changes:
- Renamed "getShortsWatchItem" to "getShortsVideoInfo".
- Fixed `ShortFormVideoInfo`. This never worked for me ever since it was introduced. Turned out it was just implemented incorrectly.
- Moved `basic_info` extraction to `MediaInfo`. Less of a pain to maintain as we only have to modify one file.
- Removed unneeded tsdoc comments.
- Fixed `Innertube#getStreamingData()`. Now it actually returns a deciphered format.
- Simplified some types.
This commit is contained in:
Luan
2024-07-30 18:49:24 -03:00
parent 3153375bca
commit 9b9fb82131
52 changed files with 362 additions and 557 deletions

View File

@@ -7,7 +7,7 @@ import type { IParsedResponse } from '../types/index.js';
import type AccountItemSection from '../classes/AccountItemSection.js';
import type AccountChannel from '../classes/AccountChannel.js';
class AccountInfo {
export default class AccountInfo {
#page: IParsedResponse;
contents: AccountItemSection | null;
@@ -31,6 +31,4 @@ class AccountInfo {
get page(): IParsedResponse {
return this.#page;
}
}
export default AccountInfo;
}

View File

@@ -3,7 +3,7 @@ import Element from '../classes/Element.js';
import type { ApiResponse } from '../../core/index.js';
import type { IBrowseResponse } from '../types/index.js';
class Analytics {
export default class Analytics {
#page: IBrowseResponse;
sections;
@@ -15,6 +15,4 @@ class Analytics {
get page(): IBrowseResponse {
return this.#page;
}
}
export default Analytics;
}

View File

@@ -315,7 +315,7 @@ export class ChannelListContinuation extends Feed<IBrowseResponse> {
export class FilteredChannelList extends FilterableFeed<IBrowseResponse> {
applied_filter?: ChipCloudChip;
contents: ReloadContinuationItemsCommand | AppendContinuationItemsAction;
contents?: ReloadContinuationItemsCommand | AppendContinuationItemsAction;
constructor(actions: Actions, data: ApiResponse | IBrowseResponse, already_parsed = false) {
super(actions, data, already_parsed);
@@ -330,7 +330,7 @@ export class FilteredChannelList extends FilterableFeed<IBrowseResponse> {
this.page.on_response_received_actions.shift();
}
this.contents = this.page.on_response_received_actions.first();
this.contents = this.page.on_response_received_actions?.first();
}
/**

View File

@@ -11,7 +11,7 @@ import type { Actions, ApiResponse } from '../../core/index.js';
import type { ObservedArray } from '../helpers.js';
import type { INextResponse } from '../types/index.js';
class Comments {
export default class Comments {
#page: INextResponse;
#actions: Actions;
#continuation?: ContinuationItem;
@@ -121,6 +121,4 @@ class Comments {
get page(): INextResponse {
return this.#page;
}
}
export default Comments;
}

View File

@@ -7,13 +7,13 @@ import type { IGuideResponse } from '../types/index.js';
import type { IRawResponse } from '../index.js';
export default class Guide {
#page: IGuideResponse;
contents: ObservedArray<GuideSection | GuideSubscriptionsSection>;
contents?: ObservedArray<GuideSection | GuideSubscriptionsSection>;
constructor(data: IRawResponse) {
this.#page = Parser.parseResponse<IGuideResponse>(data);
this.contents = this.#page.items.array().as(GuideSection, GuideSubscriptionsSection);
if (this.#page.items)
this.contents = this.#page.items.array().as(GuideSection, GuideSubscriptionsSection);
}
get page(): IGuideResponse {

View File

@@ -6,7 +6,7 @@ import type { Actions, ApiResponse } from '../../core/index.js';
import type { IBrowseResponse } from '../types/index.js';
// TODO: make feed actions usable
class History extends Feed<IBrowseResponse> {
export default class History extends Feed<IBrowseResponse> {
sections: ItemSection[];
feed_actions: BrowseFeedActions;
@@ -25,6 +25,4 @@ class History extends Feed<IBrowseResponse> {
throw new Error('No continuation data found');
return new History(this.actions, response, true);
}
}
export default History;
}

View File

@@ -8,13 +8,13 @@ import type { ApiResponse, Actions } from '../../core/index.js';
import type ChipCloudChip from '../classes/ChipCloudChip.js';
export default class HomeFeed extends FilterableFeed<IBrowseResponse> {
contents: RichGrid | AppendContinuationItemsAction | ReloadContinuationItemsCommand;
header: FeedTabbedHeader;
contents?: RichGrid | AppendContinuationItemsAction | ReloadContinuationItemsCommand;
header?: FeedTabbedHeader;
constructor(actions: Actions, data: ApiResponse | IBrowseResponse, already_parsed = false) {
super(actions, data, already_parsed);
this.header = this.memo.getType(FeedTabbedHeader).first();
this.contents = this.memo.getType(RichGrid).first() || this.page.on_response_received_actions.first();
this.contents = this.memo.getType(RichGrid).first() || this.page.on_response_received_actions?.first();
}
/**
@@ -34,7 +34,9 @@ export default class HomeFeed extends FilterableFeed<IBrowseResponse> {
// Keep the page header
feed.page.header = this.page.header;
feed.page.header_memo?.set(this.header.type, [ this.header ]);
if (this.header)
feed.page.header_memo?.set(this.header.type, [ this.header ]);
return new HomeFeed(this.actions, feed.page, true);
}

View File

@@ -8,7 +8,7 @@ import type { ObservedArray, YTNode } from '../helpers.js';
import type { IParsedResponse } from '../types/index.js';
import type NavigationEndpoint from '../classes/NavigationEndpoint.js';
class ItemMenu {
export default class ItemMenu {
#page: IParsedResponse;
#actions: Actions;
#items: ObservedArray<YTNode>;
@@ -65,6 +65,4 @@ class ItemMenu {
page(): IParsedResponse {
return this.#page;
}
}
export default ItemMenu;
}

View File

@@ -10,7 +10,7 @@ import PageHeader from '../classes/PageHeader.js';
import type { Actions, ApiResponse } from '../../core/index.js';
import type { IBrowseResponse } from '../types/index.js';
class Library extends Feed<IBrowseResponse> {
export default class Library extends Feed<IBrowseResponse> {
header: PageHeader | null;
sections;
@@ -32,7 +32,7 @@ class Library extends Feed<IBrowseResponse> {
}));
}
async #getAll(shelf: Shelf): Promise<Playlist | History | Feed> {
async #getAll(shelf: Shelf): Promise<Playlist | History | Feed<IBrowseResponse>> {
if (!shelf.menu?.as(Menu).hasKey('top_level_buttons'))
throw new InnertubeError(`The ${shelf.title.text} shelf doesn't have more items`);
@@ -75,6 +75,4 @@ class Library extends Feed<IBrowseResponse> {
get clips() {
return this.sections.find((section) => section.type === 'CONTENT_CUT');
}
}
export default Library;
}

View File

@@ -51,7 +51,7 @@ export interface LiveMetadata {
date?: UpdateDateTextAction;
}
class LiveChat extends EventEmitter {
export default class LiveChat extends EventEmitter {
smoothed_queue: SmoothedQueue;
#actions: Actions;
@@ -309,6 +309,4 @@ class LiveChat extends EventEmitter {
async #wait(ms: number) {
return new Promise<void>((resolve) => setTimeout(() => resolve(), ms));
}
}
export default LiveChat;
}

View File

@@ -8,7 +8,7 @@ import Notification from '../classes/Notification.js';
import type { ApiResponse, Actions } from '../../core/index.js';
import type { IGetNotificationsMenuResponse } from '../types/index.js';
class NotificationsMenu {
export default class NotificationsMenu {
#page: IGetNotificationsMenuResponse;
#actions: Actions;
@@ -19,12 +19,15 @@ class NotificationsMenu {
this.#actions = actions;
this.#page = Parser.parseResponse<IGetNotificationsMenuResponse>(response.data);
if (!this.#page.actions_memo)
throw new InnertubeError('Page actions not found');
this.header = this.#page.actions_memo.getType(SimpleMenuHeader).first();
this.contents = this.#page.actions_memo.getType(Notification);
}
async getContinuation(): Promise<NotificationsMenu> {
const continuation = this.#page.actions_memo.getType(ContinuationItem).first();
const continuation = this.#page.actions_memo?.getType(ContinuationItem).first();
if (!continuation)
throw new InnertubeError('Continuation not found');
@@ -37,6 +40,4 @@ class NotificationsMenu {
get page(): IGetNotificationsMenuResponse {
return this.#page;
}
}
export default NotificationsMenu;
}

View File

@@ -12,7 +12,7 @@ import type { ApiResponse, Actions } from '../../core/index.js';
import type { ObservedArray, YTNode } from '../helpers.js';
import type { ISearchResponse } from '../types/index.js';
class Search extends Feed<ISearchResponse> {
export default class Search extends Feed<ISearchResponse> {
header?: SearchHeader;
results?: ObservedArray<YTNode> | null;
refinements: string[];
@@ -34,13 +34,18 @@ class Search extends Feed<ISearchResponse> {
if (this.page.header)
this.header = this.page.header.item().as(SearchHeader);
this.results = contents.find((content) => content.is(ItemSection) && content.contents && content.contents.length > 0)?.as(ItemSection).contents;
this.results = contents.find(
(content) => content.is(ItemSection) && (content.contents && content.contents.length > 0)
)?.as(ItemSection)?.contents;
this.refinements = this.page.refinements || [];
this.estimated_results = this.page.estimated_results;
this.estimated_results = this.page.estimated_results || 0;
if (this.page.contents_memo) {
this.sub_menu = this.page.contents_memo.getType(SearchSubMenu).first();
this.watch_card = this.page.contents_memo.getType(UniversalWatchCard).first();
}
this.sub_menu = this.page.contents_memo?.getType(SearchSubMenu).first();
this.watch_card = this.page.contents_memo?.getType(UniversalWatchCard).first();
this.refinement_cards = this.results?.firstOfType(HorizontalCardList);
}
@@ -82,6 +87,4 @@ class Search extends Feed<ISearchResponse> {
throw new InnertubeError('Could not get continuation data');
return new Search(this.actions, response, true);
}
}
export default Search;
}

View File

@@ -17,7 +17,7 @@ import TwoColumnBrowseResults from '../classes/TwoColumnBrowseResults.js';
import type { ApiResponse, Actions } from '../../core/index.js';
import type { IBrowseResponse } from '../types/index.js';
class Settings {
export default class Settings {
#page: IBrowseResponse;
#actions: Actions;
@@ -132,6 +132,4 @@ class Settings {
get page(): IBrowseResponse {
return this.#page;
}
}
export default Settings;
}

View File

@@ -43,7 +43,7 @@ class DelayQueue {
}
}
class SmoothedQueue {
export default class SmoothedQueue {
#last_update_time: number | null;
#estimated_update_interval: number | null;
#callback: Function | null;
@@ -154,6 +154,4 @@ class SmoothedQueue {
get poll_response_delay_queue(): DelayQueue {
return this.#poll_response_delay_queue;
}
}
export default SmoothedQueue;
}

View File

@@ -8,7 +8,7 @@ import type { ApiResponse } from '../../core/index.js';
import type { ObservedArray } from '../helpers.js';
import type { IBrowseResponse } from '../types/index.js';
class TimeWatched {
export default class TimeWatched {
#page: IBrowseResponse;
contents?: ObservedArray<ItemSection>;
@@ -29,6 +29,4 @@ class TimeWatched {
get page(): IBrowseResponse {
return this.#page;
}
}
export default TimeWatched;
}

View File

@@ -12,6 +12,10 @@ export default class TranscriptInfo {
constructor(actions: Actions, response: ApiResponse) {
this.#page = Parser.parseResponse(response.data);
this.#actions = actions;
if (!this.#page.actions_memo)
throw new Error('Page actions not found');
this.transcript = this.#page.actions_memo.getType(Transcript).first();
}

View File

@@ -27,26 +27,11 @@ import VideoDescriptionMusicSection from '../classes/VideoDescriptionMusicSectio
import LiveChatWrap from './LiveChat.js';
import type { RawNode } from '../index.js';
import type CardCollection from '../classes/CardCollection.js';
import type Endscreen from '../classes/Endscreen.js';
import type PlayerAnnotationsExpanded from '../classes/PlayerAnnotationsExpanded.js';
import type PlayerCaptionsTracklist from '../classes/PlayerCaptionsTracklist.js';
import type PlayerLiveStoryboardSpec from '../classes/PlayerLiveStoryboardSpec.js';
import type PlayerStoryboardSpec from '../classes/PlayerStoryboardSpec.js';
import type { ApiResponse, Actions } from '../../core/index.js';
import type { ObservedArray, YTNode } from '../helpers.js';
class VideoInfo extends MediaInfo {
export default class VideoInfo extends MediaInfo {
#watch_next_continuation?: ContinuationItem;
basic_info;
annotations?: ObservedArray<PlayerAnnotationsExpanded>;
storyboards?: PlayerStoryboardSpec | PlayerLiveStoryboardSpec;
endscreen?: Endscreen;
captions?: PlayerCaptionsTracklist;
cards?: CardCollection;
primary_info?: VideoPrimaryInfo | null;
secondary_info?: VideoSecondaryInfo | null;
playlist?;
@@ -70,37 +55,6 @@ class VideoInfo extends MediaInfo {
const [ info, next ] = this.page;
if (info.microformat && !info.microformat?.is(PlayerMicroformat, MicroformatData))
throw new InnertubeError('Invalid microformat', info.microformat);
this.basic_info = { // This type is inferred so no need for an explicit type
...info.video_details,
/**
* Microformat is a bit redundant, so only
* a few things there are interesting to us.
*/
...{
embed: info.microformat?.is(PlayerMicroformat) ? info.microformat?.embed : null,
channel: info.microformat?.is(PlayerMicroformat) ? info.microformat?.channel : null,
is_unlisted: info.microformat?.is_unlisted,
is_family_safe: info.microformat?.is_family_safe,
category: info.microformat?.is(PlayerMicroformat) ? info.microformat?.category : null,
has_ypc_metadata: info.microformat?.is(PlayerMicroformat) ? info.microformat?.has_ypc_metadata : null,
start_timestamp: info.microformat?.is(PlayerMicroformat) ? info.microformat.start_timestamp : null,
end_timestamp: info.microformat?.is(PlayerMicroformat) ? info.microformat.end_timestamp : null,
view_count: info.microformat?.is(PlayerMicroformat) && isNaN(info.video_details?.view_count as number) ? info.microformat.view_count : info.video_details?.view_count
},
like_count: undefined as number | undefined,
is_liked: undefined as boolean | undefined,
is_disliked: undefined as boolean | undefined
};
this.annotations = info.annotations;
this.storyboards = info.storyboards;
this.endscreen = info.endscreen;
this.captions = info.captions;
this.cards = info.cards;
if (this.streaming_data) {
const default_audio_track = this.streaming_data.adaptive_formats.find((format) => format.audio_track?.audio_is_default);
if (default_audio_track) {
@@ -398,7 +352,7 @@ class VideoInfo extends MediaInfo {
* @returns `VideoInfo` for the trailer, or `null` if none.
*/
getTrailerInfo(): VideoInfo | null {
if (this.has_trailer) {
if (this.has_trailer && this.playability_status) {
const player_response = this.playability_status.error_screen?.as(PlayerLegacyDesktopYpcTrailer).trailer?.player_response;
if (player_response) {
return new VideoInfo([ { data: player_response } as ApiResponse ], this.actions, this.cpn);
@@ -432,7 +386,7 @@ class VideoInfo extends MediaInfo {
* Checks if trailer is available.
*/
get has_trailer(): boolean {
return !!this.playability_status.error_screen?.is(PlayerLegacyDesktopYpcTrailer);
return !!this.playability_status?.error_screen?.is(PlayerLegacyDesktopYpcTrailer);
}
/**
@@ -487,6 +441,4 @@ class VideoInfo extends MediaInfo {
}
return [];
}
}
export default VideoInfo;
}