mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-24 07:11:48 +00:00
refactor!: fix inconsistent use of SuperParsedResult
This commit is contained in:
@@ -119,7 +119,7 @@ class Feed {
|
||||
* Returns contents from the page.
|
||||
*/
|
||||
get contents() {
|
||||
const tab_content = this.#memo.getType(Tab)?.[0]?.content.item();
|
||||
const tab_content = this.#memo.getType(Tab)?.[0]?.content;
|
||||
const reload_continuation_items = this.#memo.getType(ReloadContinuationItemsCommand)?.[0];
|
||||
const append_continuation_items = this.#memo.getType(AppendContinuationItemsAction)?.[0];
|
||||
|
||||
|
||||
@@ -167,9 +167,9 @@ class Music {
|
||||
if (!tab)
|
||||
throw new InnertubeError('Could not find target tab.');
|
||||
|
||||
const music_queue = tab.content.item().as(MusicQueue);
|
||||
const music_queue = tab.content?.as(MusicQueue);
|
||||
|
||||
if (!music_queue.content)
|
||||
if (!music_queue || !music_queue.content)
|
||||
throw new InnertubeError('Music queue was empty, the given id is probably invalid.', music_queue);
|
||||
|
||||
const playlist_panel = music_queue.content.item().as(PlaylistPanel);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import Parser from '../index';
|
||||
import Text from './misc/Text';
|
||||
import PlaylistPanelVideo from './PlaylistPanelVideo';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class PlaylistPanel extends YTNode {
|
||||
@@ -19,7 +21,7 @@ class PlaylistPanel extends YTNode {
|
||||
super();
|
||||
this.title = data.title;
|
||||
this.title_text = new Text(data.titleText);
|
||||
this.contents = Parser.parse(data.contents);
|
||||
this.contents = Parser.parseArray<PlaylistPanelVideo>(data.contents, PlaylistPanelVideo);
|
||||
this.playlist_id = data.playlistId;
|
||||
this.is_infinite = data.isInfinite;
|
||||
this.continuation = data.continuations[0]?.nextRadioContinuationData?.continuation;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import Parser from '../index';
|
||||
import NavigationEndpoint from './NavigationEndpoint';
|
||||
import SectionList from './SectionList';
|
||||
import MusicQueue from './MusicQueue';
|
||||
import RichGrid from './RichGrid';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class Tab extends YTNode {
|
||||
@@ -15,7 +19,7 @@ class Tab extends YTNode {
|
||||
this.title = data.title || 'N/A';
|
||||
this.selected = data.selected || false;
|
||||
this.endpoint = new NavigationEndpoint(data.endpoint);
|
||||
this.content = Parser.parse(data.content);
|
||||
this.content = Parser.parseItem<SectionList | MusicQueue | RichGrid>(data.content, [ SectionList, MusicQueue, RichGrid ]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ class Author {
|
||||
this.#nav_text = new NavigatableText(item);
|
||||
|
||||
this.id =
|
||||
this.#nav_text.runs?.[0].endpoint.browse?.id ||
|
||||
this.#nav_text.runs?.[0].endpoint?.browse?.id ||
|
||||
this.#nav_text.endpoint?.browse?.id || 'N/A';
|
||||
|
||||
this.name = this.#nav_text.text || 'N/A';
|
||||
@@ -21,8 +21,8 @@ class Author {
|
||||
this.is_verified_artist = this.badges?.some((badge) => badge.style == 'BADGE_STYLE_TYPE_VERIFIED_ARTIST') || null;
|
||||
|
||||
this.url =
|
||||
this.#nav_text.runs?.[0].endpoint.browse &&
|
||||
`${Constants.URLS.YT_BASE}${this.#nav_text.runs[0].endpoint.browse?.base_url || `/u/${this.#nav_text.runs[0].endpoint.browse?.id}`}` ||
|
||||
this.#nav_text.runs?.[0].endpoint?.browse &&
|
||||
`${Constants.URLS.YT_BASE}${this.#nav_text.runs[0].endpoint?.browse?.base_url || `/u/${this.#nav_text.runs[0].endpoint?.browse?.id}`}` ||
|
||||
`${Constants.URLS.YT_BASE}${this.#nav_text.endpoint?.browse?.base_url || `/u/${this.#nav_text.endpoint?.browse?.id}`}` ||
|
||||
null;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,10 @@ class Library {
|
||||
|
||||
this.profile = { stats, user_info };
|
||||
|
||||
const shelves = tab.content.item().as(SectionList).contents.array().as(ItemSection).map((is: ItemSection) => is.contents?.firstOfType(Shelf));
|
||||
if (!tab.content)
|
||||
throw new InnertubeError('Target tab did not have any content.');
|
||||
|
||||
const shelves = tab.content.as(SectionList).contents.array().as(ItemSection).map((is: ItemSection) => is.contents?.firstOfType(Shelf));
|
||||
|
||||
this.sections = shelves.map((shelf: any) => ({
|
||||
type: shelf.icon_type,
|
||||
|
||||
@@ -4,7 +4,6 @@ import Actions, { AxioslikeResponse } from '../../core/Actions';
|
||||
import MusicDetailHeader from '../classes/MusicDetailHeader';
|
||||
import MicroformatData from '../classes/MicroformatData';
|
||||
import MusicCarouselShelf from '../classes/MusicCarouselShelf';
|
||||
import MusicResponsiveListItem from '../classes/MusicResponsiveListItem';
|
||||
import MusicShelf from '../classes/MusicShelf';
|
||||
|
||||
class Album {
|
||||
@@ -24,7 +23,7 @@ class Album {
|
||||
this.header = this.#page.header.item().as(MusicDetailHeader);
|
||||
this.url = this.#page.microformat?.as(MicroformatData).url_canonical || null;
|
||||
|
||||
this.contents = this.#page.contents_memo.get('MusicShelf')?.[0].as(MusicShelf).contents.array().as(MusicResponsiveListItem);
|
||||
this.contents = this.#page.contents_memo.get('MusicShelf')?.[0].as(MusicShelf).contents;
|
||||
this.sections = this.#page.contents_memo.get('MusicCarouselShelf') as MusicCarouselShelf[] || ([] as MusicCarouselShelf[]);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class Artist {
|
||||
if (!music_shelves.length)
|
||||
throw new InnertubeError('Could not find any node of type MusicShelf.');
|
||||
|
||||
const shelf = music_shelves.find((shelf) => shelf.title === 'Songs') as MusicShelf;
|
||||
const shelf = music_shelves.find((shelf) => shelf.title.toString() === 'Songs') as MusicShelf;
|
||||
|
||||
if (!shelf)
|
||||
throw new InnertubeError('Could not find target shelf (Songs).');
|
||||
|
||||
@@ -3,7 +3,6 @@ import Parser, { ParsedResponse } from '..';
|
||||
import { InnertubeError } from '../../utils/Utils';
|
||||
import { AxioslikeResponse } from '../../core/Actions';
|
||||
|
||||
import Tab from '../classes/Tab';
|
||||
import Grid from '../classes/Grid';
|
||||
import SectionList from '../classes/SectionList';
|
||||
import SingleColumnBrowseResults from '../classes/SingleColumnBrowseResults';
|
||||
@@ -19,12 +18,15 @@ class Explore {
|
||||
constructor(response: AxioslikeResponse) {
|
||||
this.#page = Parser.parseResponse(response.data);
|
||||
|
||||
const tab = this.#page.contents.item().as(SingleColumnBrowseResults).tabs.array().as(Tab).get({ selected: true });
|
||||
const tab = this.#page.contents.item().as(SingleColumnBrowseResults).tabs.get({ selected: true });
|
||||
|
||||
if (!tab)
|
||||
throw new InnertubeError('Could not find target tab.');
|
||||
|
||||
const section_list = tab.content.item().as(SectionList);
|
||||
const section_list = tab.content?.as(SectionList);
|
||||
|
||||
if (!section_list)
|
||||
throw new InnertubeError('Target tab did not have any content.');
|
||||
|
||||
this.top_buttons = section_list.contents.array().firstOfType(Grid)?.items.array().as(MusicNavigationButton) || ([] as MusicNavigationButton[]);
|
||||
this.sections = section_list.contents.array().getAll({ type: 'MusicCarouselShelf' }) as MusicCarouselShelf[];
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import Parser, { ParsedResponse } from '../index';
|
||||
import Parser, { ParsedResponse, SectionListContinuation } from '../index';
|
||||
import Actions, { AxioslikeResponse } from '../../core/Actions';
|
||||
import { InnertubeError } from '../../utils/Utils';
|
||||
|
||||
import MusicCarouselShelfBasicHeader from '../classes/MusicCarouselShelfBasicHeader';
|
||||
import MusicTwoRowItem from '../classes/MusicTwoRowItem';
|
||||
import SectionList from '../classes/SectionList';
|
||||
import SingleColumnBrowseResults from '../classes/SingleColumnBrowseResults';
|
||||
import MusicCarouselShelf from '../classes/MusicCarouselShelf';
|
||||
|
||||
class HomeFeed {
|
||||
#page;
|
||||
@@ -16,25 +17,23 @@ class HomeFeed {
|
||||
this.#actions = actions;
|
||||
this.#page = Parser.parseResponse((response as AxioslikeResponse).data);
|
||||
|
||||
const tab = this.#page.contents.item().key('tabs').parsed().array().get({ selected: true });
|
||||
const tab = this.#page.contents.item().as(SingleColumnBrowseResults).tabs.get({ selected: true });
|
||||
|
||||
if (!tab)
|
||||
throw new InnertubeError('Could not get Home tab.');
|
||||
|
||||
let contents;
|
||||
|
||||
if (tab.key('content').isNull()) {
|
||||
this.#continuation = this.#page.continuation_contents?.key('continuation').string();
|
||||
contents = this.#page.continuation_contents?.key('contents').array();
|
||||
} else {
|
||||
this.#continuation = tab.key('content').parsed().item().key('continuation').string();
|
||||
contents = tab.key('content').parsed().item().key('contents').parsed().array();
|
||||
if (!this.#page.continuation_contents)
|
||||
throw new InnertubeError('Continuation did not have any content.');
|
||||
|
||||
this.#continuation = this.#page.continuation_contents.as(SectionListContinuation).continuation;
|
||||
this.sections = this.#page.continuation_contents.as(SectionListContinuation).contents?.as(MusicCarouselShelf);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.sections = contents?.map((content) => ({
|
||||
header: content.header.item() as MusicCarouselShelfBasicHeader,
|
||||
contents: content.contents.array() as Array<MusicTwoRowItem>
|
||||
}));
|
||||
this.#continuation = tab.content?.as(SectionList).continuation;
|
||||
this.sections = tab.content?.as(SectionList).contents.array().as(MusicCarouselShelf);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
import Parser, { ParsedResponse } from '../index';
|
||||
import Actions, { AxioslikeResponse } from '../../core/Actions';
|
||||
|
||||
import { InnertubeError } from '../../utils/Utils';
|
||||
|
||||
import SectionList from '../classes/SectionList';
|
||||
import TabbedSearchResults from '../classes/TabbedSearchResults';
|
||||
|
||||
import DidYouMean from '../classes/DidYouMean';
|
||||
import ShowingResultsFor from '../classes/ShowingResultsFor';
|
||||
import MusicShelf from '../classes/MusicShelf';
|
||||
import MusicResponsiveListItem from '../classes/MusicResponsiveListItem';
|
||||
|
||||
import Shelf from '../classes/Shelf';
|
||||
import RichShelf from '../classes/RichShelf';
|
||||
import ReelShelf from '../classes/ReelShelf';
|
||||
import ChipCloud from '../classes/ChipCloud';
|
||||
import ChipCloudChip from '../classes/ChipCloudChip';
|
||||
import ItemSection from '../classes/ItemSection';
|
||||
import Message from '../classes/Message';
|
||||
|
||||
import Parser, { ParsedResponse } from '../index';
|
||||
import { InnertubeError } from '../../utils/Utils';
|
||||
import Actions, { AxioslikeResponse } from '../../core/Actions';
|
||||
|
||||
class Search {
|
||||
#page;
|
||||
#actions;
|
||||
#continuation;
|
||||
#header;
|
||||
|
||||
did_you_mean;
|
||||
showing_results_for;
|
||||
message;
|
||||
header;
|
||||
|
||||
did_you_mean: DidYouMean | null;
|
||||
showing_results_for: ShowingResultsFor | null;
|
||||
message: Message | null;
|
||||
|
||||
results;
|
||||
sections;
|
||||
|
||||
@@ -34,44 +37,39 @@ class Search {
|
||||
response as ParsedResponse :
|
||||
Parser.parseResponse((response as AxioslikeResponse).data);
|
||||
|
||||
const tab = this.#page.contents.item().key('tabs').parsed().array().get({ selected: true });
|
||||
const tab = this.#page.contents.item().as(TabbedSearchResults).tabs.get({ selected: true });
|
||||
|
||||
if (!tab)
|
||||
throw new InnertubeError('Could not find target tab.');
|
||||
|
||||
const tab_content = tab.key('content').parsed().item();
|
||||
const tab_content = tab.content?.as(SectionList);
|
||||
|
||||
if (tab_content.hasKey('header')) {
|
||||
this.#header = tab_content.key('header').parsed().item().as(ChipCloud);
|
||||
}
|
||||
if (!tab_content)
|
||||
throw new InnertubeError('Target tab did not have any content.');
|
||||
|
||||
const shelves = tab_content.key('contents').parsed().array();
|
||||
this.header = tab_content.hasKey('header') ? tab_content.header?.item().as(ChipCloud) : null;
|
||||
|
||||
const shelves = tab_content.contents.array().as(MusicShelf, ItemSection);
|
||||
const item_section = shelves.firstOfType(ItemSection);
|
||||
|
||||
this.did_you_mean = item_section?.contents?.firstOfType(DidYouMean) || null;
|
||||
this.showing_results_for = item_section?.contents?.firstOfType(ShowingResultsFor) || null;
|
||||
this.message = item_section?.contents?.firstOfType(Message) || null;
|
||||
|
||||
if (!!this.did_you_mean || !!this.showing_results_for || !!this.message)
|
||||
shelves.shift();
|
||||
|
||||
if (args.is_continuation || args.is_filtered) {
|
||||
const shelf = shelves.firstOfType(MusicShelf);
|
||||
this.results = shelf?.contents.array().as(MusicResponsiveListItem);
|
||||
this.#continuation = shelf?.continuation;
|
||||
return;
|
||||
this.results = shelves.firstOfType(MusicShelf)?.contents;
|
||||
this.#continuation = shelves.firstOfType(MusicShelf)?.continuation;
|
||||
} else {
|
||||
this.sections = shelves.filterType(MusicShelf);
|
||||
}
|
||||
|
||||
this.sections = shelves.as(MusicShelf, Shelf, RichShelf, ReelShelf).map((shelf) => ({
|
||||
title: shelf.title.toString(),
|
||||
contents: shelf.key('contents').parsed().array().as(MusicResponsiveListItem),
|
||||
getMore: () => this.#getMore(shelf)
|
||||
}));
|
||||
}
|
||||
|
||||
async #getMore(shelf: MusicShelf | Shelf | RichShelf | ReelShelf): Promise<Search> {
|
||||
if (!shelf.endpoint)
|
||||
throw new InnertubeError(`${shelf.title} doesn't have more items`);
|
||||
/**
|
||||
* Equivalent to clicking on the shelf to load more items.
|
||||
*/
|
||||
async getMore(shelf: MusicShelf | undefined): Promise<Search> {
|
||||
if (!shelf || !shelf.endpoint)
|
||||
throw new InnertubeError('Cannot retrieve more items for this shelf because it does not have an endpoint.');
|
||||
|
||||
const response = await shelf.endpoint.call(this.#actions, 'YTMUSIC', true);
|
||||
|
||||
@@ -104,8 +102,7 @@ class Search {
|
||||
if (!this.filters?.includes(name))
|
||||
throw new InnertubeError('Invalid filter', { available_filters: this.filters });
|
||||
|
||||
// TODO: make sure this is a ChipCloudChip or use the property accessor helpers on YTNode
|
||||
const filter = this.#header?.chips?.as(ChipCloudChip).get({ text: name });
|
||||
const filter = this.header?.chips?.as(ChipCloudChip).get({ text: name });
|
||||
|
||||
if (filter?.is_selected) return this;
|
||||
|
||||
@@ -122,32 +119,32 @@ class Search {
|
||||
}
|
||||
|
||||
get filters(): string[] | null {
|
||||
return this.#header?.chips?.as(ChipCloudChip).map((chip) => chip.text) || null;
|
||||
return this.header?.chips?.as(ChipCloudChip).map((chip) => chip.text) || null;
|
||||
}
|
||||
|
||||
get songs() {
|
||||
return this.sections?.find((section) => section.title === 'Songs');
|
||||
get songs(): MusicShelf | undefined {
|
||||
return this.sections?.find((section) => section.title.toString() === 'Songs');
|
||||
}
|
||||
|
||||
get videos() {
|
||||
return this.sections?.find((section) => section.title === 'Videos');
|
||||
get videos(): MusicShelf | undefined {
|
||||
return this.sections?.find((section) => section.title.toString() === 'Videos');
|
||||
}
|
||||
|
||||
get albums() {
|
||||
return this.sections?.find((section) => section.title === 'Albums');
|
||||
get albums(): MusicShelf | undefined {
|
||||
return this.sections?.find((section) => section.title.toString() === 'Albums');
|
||||
}
|
||||
|
||||
get artists() {
|
||||
return this.sections?.find((section) => section.title === 'Artists');
|
||||
get artists(): MusicShelf | undefined {
|
||||
return this.sections?.find((section) => section.title.toString() === 'Artists');
|
||||
}
|
||||
|
||||
get playlists() {
|
||||
return this.sections?.find((section) => section.title === 'Community playlists');
|
||||
get playlists(): MusicShelf | undefined {
|
||||
return this.sections?.find((section) => section.title.toString() === 'Community playlists');
|
||||
}
|
||||
|
||||
get page() {
|
||||
get page(): ParsedResponse {
|
||||
return this.#page;
|
||||
}
|
||||
}
|
||||
|
||||
export default Search;
|
||||
export default Search;
|
||||
Reference in New Issue
Block a user