refactor!: fix inconsistent use of SuperParsedResult

This commit is contained in:
LuanRT
2022-08-07 06:15:55 -03:00
parent 709c448053
commit 40fc24b043
11 changed files with 84 additions and 78 deletions

View File

@@ -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];

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 ]);
}
}

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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[]);
}

View File

@@ -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).');

View File

@@ -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[];

View File

@@ -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);
}
/**

View File

@@ -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;