diff --git a/lib/parser/contents/classes/Message.js b/lib/parser/contents/classes/Message.js index 1d43d2381..6929d1232 100644 --- a/lib/parser/contents/classes/Message.js +++ b/lib/parser/contents/classes/Message.js @@ -6,7 +6,7 @@ class Message { type = 'messageRenderer' constructor(data) { - this.text = new Text(data.text); + this.text = new Text(data.text).toString(); } } diff --git a/lib/parser/contents/index.js b/lib/parser/contents/index.js index 66d6baa5c..f504e4b48 100644 --- a/lib/parser/contents/index.js +++ b/lib/parser/contents/index.js @@ -13,6 +13,7 @@ class AppendContinuationItemsAction { } } +/** @namespace */ class ReloadContinuationItemsCommand { type = 'reloadContinuationItemsCommand'; @@ -108,7 +109,7 @@ class Parser { const keys = Object.keys(data); const classname = this.sanitizeClassName(keys[0]); - if (this.shouldIgnore(classname)) return; + if (this.shouldIgnore(classname))return; try { const TargetClass = require('./classes/' + classname); diff --git a/lib/parser/youtube/VideoInfo.js b/lib/parser/youtube/VideoInfo.js index 9fc596ea7..784faa7f2 100644 --- a/lib/parser/youtube/VideoInfo.js +++ b/lib/parser/youtube/VideoInfo.js @@ -63,7 +63,7 @@ class VideoInfo { /** * @type {import('../contents/classes/MerchandiseShelf')} */ - this.merchandise = results?.get({ type: 'merchandiseShelfRenderer' }) || {}; + this.merchandise = results?.get({ type: 'merchandiseShelfRenderer' }) || null; /** * @type {import('../contents/classes/ChipCloud')} @@ -152,7 +152,7 @@ class VideoInfo { return this.watch_next_feed; } - /** @type {string} */ + /** @type {string[]} */ get filters() { return this.related_chip_cloud?.chips.map((chip) => chip.text) || []; } diff --git a/lib/parser/ytmusic/Search.js b/lib/parser/ytmusic/Search.js index ed705c11a..0d317af07 100644 --- a/lib/parser/ytmusic/Search.js +++ b/lib/parser/ytmusic/Search.js @@ -9,6 +9,7 @@ class Search { #actions; #continuation; #shelf_title; + #header; /** * @param {object} response - API response. @@ -27,6 +28,8 @@ class Search { const shelves = tab.content.contents; const item_section = shelves.get({ type: 'itemSectionRenderer' }); + + this.#header = tab.content.header; /** * @type {import('../contents/classes/DidYouMean')} @@ -38,7 +41,7 @@ class Search { */ this.showing_results_for = item_section?.contents.get({ type: 'showingResultsForRenderer' }) || null; - this.did_you_mean || this.showing_results_for && shelves.shift(); + (!!this.did_you_mean || !!this.showing_results_for) && shelves.shift(); if (is_continuation) { const shelf = shelves.get({ type: 'musicShelfRenderer' }); @@ -78,9 +81,32 @@ class Search { return this; } + /** + * Applies given filter to the search. + * @param {string} name + * @returns {Promise.} + */ + async selectFilter(name) { + if (!this.filters.includes(name)) + throw new InnertubeError('Invalid filter', { available_filters: this.filters }); + + const filter = this.#header.chips.get({ text: name }); + if (filter.is_selected) return this; + + const response = await filter.endpoint.call(this.#actions, 'YTMUSIC'); + + return new Search(response, this.#actions, true); + } + + /** @type {boolean} */ get has_continuation() { return !!this.#continuation; } + + /** @type {string[]} */ + get filters() { + return this.#header.chips.map((chip) => chip.text); + } /** @type {import('../contents/classes/MusicResponsiveListItem')[]} */ get songs() {