diff --git a/src/Innertube.ts b/src/Innertube.ts index 78ec476b..8bdf6f98 100644 --- a/src/Innertube.ts +++ b/src/Innertube.ts @@ -202,7 +202,7 @@ class Innertube { */ async getNotifications() { const response = await this.actions.notifications('get_notification_menu'); - return new NotificationsMenu(this.actions, response.data); + return new NotificationsMenu(this.actions, response); } /** diff --git a/src/parser/classes/NavigationEndpoint.ts b/src/parser/classes/NavigationEndpoint.ts index 7c69bd68..5b7f3d6b 100644 --- a/src/parser/classes/NavigationEndpoint.ts +++ b/src/parser/classes/NavigationEndpoint.ts @@ -212,15 +212,30 @@ class NavigationEndpoint extends YTNode { } /** - * Calls the endpoint. (This is an experiment and may replace {@link call} in the future.). + * Call endpoint. (This is an experiment and may replace {@link call} in the future.). */ - async callTest(actions: Actions, args = { parse: true, params: {} }) { + async callTest(actions: Actions, args: { + parse: false; + params?: { [key: string]: any; } + }): Promise; + async callTest(actions: Actions, args?: { + parse: true; + params?: { [key: string]: any; } + }): Promise; + async callTest(actions: Actions, args: { + parse: boolean; + params: { [key: string]: any; } + }): Promise; + async callTest(actions: Actions, args?: { + parse?: boolean; + params?: { [key: string]: any; } + }): Promise { if (!actions) throw new Error('An active caller must be provided'); if (!this.metadata.api_url) throw new Error('Expected an api_url, but none was found, this is a bug.'); - const response = await actions.execute(this.metadata.api_url, { ...this.payload, ...args.params, parse: args.parse }); + const response = await actions.execute(this.metadata.api_url, { ...this.payload, ...(args?.params || {}), parse: args ? args.parse : true }); return response; } diff --git a/src/parser/youtube/NotificationsMenu.js b/src/parser/youtube/NotificationsMenu.js deleted file mode 100644 index 9e808eca..00000000 --- a/src/parser/youtube/NotificationsMenu.js +++ /dev/null @@ -1,35 +0,0 @@ -import Parser from '../index'; -import { InnertubeError } from '../../utils/Utils'; - -class NotificationsMenu { - #page; - #actions; - #continuation; - /** - * @param {import('../../core/Actions').default} actions - * @param {object} response - API response. - */ - constructor(actions, response) { - this.#actions = actions; - this.#page = Parser.parseResponse(response); - /** @type {import('../classes/menus/SimpleMenuHeader')} */ - this.header = this.#page.actions_memo.get('SimpleMenuHeader')?.[0] || null; - /** @type {import('../classes/Notification')} */ - this.contents = this.#page.actions_memo.get('Notification'); - } - /** - * Retrieves next batch of notifications. - * @returns {Promise.} - */ - async getContinuation() { - const continuation = this.#page.actions_memo.get('ContinuationItem')?.[0]; - if (!continuation) - throw new InnertubeError('Continuation not found'); - const response = await continuation.endpoint.callTest(this.#actions, { parse: false }); - return new NotificationsMenu(this.#actions, response.data); - } - get page() { - return this.#page; - } -} -export default NotificationsMenu; diff --git a/src/parser/youtube/NotificationsMenu.ts b/src/parser/youtube/NotificationsMenu.ts new file mode 100644 index 00000000..3b5fdc88 --- /dev/null +++ b/src/parser/youtube/NotificationsMenu.ts @@ -0,0 +1,35 @@ +import Parser from '..'; +import Actions, { AxioslikeResponse } from '../../core/Actions'; +import { InnertubeError } from '../../utils/Utils'; + +import Notification from '../classes/Notification'; +import SimpleMenuHeader from '../classes/menus/SimpleMenuHeader'; +import ContinuationItem from '../classes/ContinuationItem'; + +class NotificationsMenu { + #page; + #actions; + + header; + contents; + + constructor(actions: Actions, response: AxioslikeResponse) { + this.#actions = actions; + this.#page = Parser.parseResponse(response.data); + + this.header = this.#page.actions_memo.get('SimpleMenuHeader')?.[0]?.as(SimpleMenuHeader) || null; + this.contents = this.#page.actions_memo.get('Notification') as Notification[]; + } + + async getContinuation(): Promise { + const continuation = this.#page.actions_memo.get('ContinuationItem')?.[0].as(ContinuationItem); + + if (!continuation) + throw new InnertubeError('Continuation not found'); + + const response = await continuation.endpoint.callTest(this.#actions, { parse: false }); + return new NotificationsMenu(this.#actions, response); + } +} + +export default NotificationsMenu; \ No newline at end of file diff --git a/src/parser/youtube/Search.ts b/src/parser/youtube/Search.ts index 1d0b8bc3..214762d8 100644 --- a/src/parser/youtube/Search.ts +++ b/src/parser/youtube/Search.ts @@ -20,30 +20,40 @@ class Search extends Feed { constructor(actions: Actions, data: any, already_parsed = false) { super(actions, data, already_parsed); - const contents = this.page.contents.item().as(TwoColumnSearchResults).primary_contents.item().key('contents').parsed().array() - || this.page.on_response_received_commands?.[0].contents; + + const contents = + this.page.contents.item().as(TwoColumnSearchResults).primary_contents.item().key('contents').parsed().array() || + this.page.on_response_received_commands?.[0].contents; + const secondary_contents_maybe = this.page.contents.item().key('secondary_contents'); const secondary_contents = secondary_contents_maybe.isParsed() ? secondary_contents_maybe.parsed().item().key('contents').parsed().array() : undefined; + this.results = contents.firstOfType(ItemSection)?.contents; + const card_list = this.results?.get({ type: 'HorizontalCardList' }, true)?.as(HorizontalCardList); const universal_watch_card = secondary_contents?.firstOfType(UniversalWatchCard); + this.refinements = this.page.refinements || []; this.estimated_results = this.page.estimated_results; + this.watch_card = { header: universal_watch_card?.header.item() || null, call_to_action: universal_watch_card?.call_to_action.item().as(WatchCardHeroVideo) || null, sections: universal_watch_card?.sections.array().filterType(WatchCardSectionSequence) || [] }; + this.refinement_cards = { header: card_list?.header.item().as(RichListHeader) || null, cards: card_list?.cards.array().filterType(SearchRefinementCard) || observe([] as SearchRefinementCard[]) }; } + /** * Applies given refinement card and returns a new {@link Search} object. */ async selectRefinementCard(card: SearchRefinementCard | string) { let target_card: SearchRefinementCard | undefined; + if (typeof card === 'string') { target_card = this.refinement_cards.cards.get({ query: card }); if (!target_card) @@ -53,16 +63,20 @@ class Search extends Feed { } else { throw new InnertubeError('Invalid refinement card!'); } + const page = await target_card.endpoint.call(this.actions); + return new Search(this.actions, page, true); } + get refinement_card_queries() { return this.refinement_cards.cards.map((card) => card.query); } + /** * Retrieves next batch of results. */ - async getContinuation() { + async getContinuation(): Promise { const continuation = await this.getContinuationData(); return new Search(this.actions, continuation, true); }