mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-30 09:55:18 +00:00
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:
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user