diff --git a/src/parser/classes/MusicTwoRowItem.js b/src/parser/classes/MusicTwoRowItem.js index b66d7a56..6779dcfd 100644 --- a/src/parser/classes/MusicTwoRowItem.js +++ b/src/parser/classes/MusicTwoRowItem.js @@ -9,31 +9,30 @@ class MusicTwoRowItem extends YTNode { constructor(data) { super(); - this.id = - this.endpoint.browse?.id || - this.endpoint.watch?.video_id; - this.title = new Text(data.title); this.endpoint = new NavigationEndpoint(data.navigationEndpoint); + this.id = + this.endpoint?.browse?.id || + this.endpoint?.watch?.video_id; + this.subtitle = new Text(data.subtitle); this.badges = Parser.parse(data.subtitleBadges); switch (this.endpoint?.browse?.page_type) { case 'MUSIC_PAGE_TYPE_ARTIST': - this.type = 'artist'; + this.item_type = 'artist'; this.subscribers = this.subtitle.toString(); break; case 'MUSIC_PAGE_TYPE_PLAYLIST': - this.type = 'playlist'; - this.item_count = parseInt(this.subtitle.runs - .find((run) => run.text - .match(/\d+ (songs|song)/))?.text - .match(/\d+/g)) || null; + this.item_type = 'playlist'; + this.item_count = this.subtitle.runs + .find((run) => run.text.match(/\d+ songs|song/))?.item || null; + break; case 'MUSIC_PAGE_TYPE_ALBUM': - this.type = 'album'; - const artists = this.subtitle.runs.filter((run) => run.endpoint.browse?.id.startsWith('UC')); + this.item_type = 'album'; + const artists = this.subtitle.runs.filter((run) => run.endpoint?.browse?.id.startsWith('UC')); if (artists) { this.artists = artists.map((artist) => ({ name: artist.text, @@ -47,16 +46,16 @@ class MusicTwoRowItem extends YTNode { break; default: if (this.subtitle.runs[0].text !== 'Song') { - this.type = 'video'; + this.item_type = 'video'; } else { - this.type = 'song'; + this.item_type = 'song'; } - if (this.type == 'video') { + if (this.item_type == 'video') { this.views = this.subtitle.runs .find((run) => run.text.match(/(.*?) views/)).text; - const author = this.subtitle.runs.find((run) => run.endpoint.browse?.id.startsWith('UC')); + const author = this.subtitle.runs.find((run) => run.endpoint?.browse?.id.startsWith('UC')); if (author) { this.author = { name: author.text, @@ -65,7 +64,7 @@ class MusicTwoRowItem extends YTNode { }; } } else { - const artists = this.subtitle.runs.filter((run) => run.endpoint.browse?.id.startsWith('UC')); + const artists = this.subtitle.runs.filter((run) => run.endpoint?.browse?.id.startsWith('UC')); if (artists) { this.artists = artists.map((artist) => ({ name: artist.text, @@ -83,4 +82,4 @@ class MusicTwoRowItem extends YTNode { } } -export default MusicTwoRowItem; \ No newline at end of file +export default MusicTwoRowItem; diff --git a/src/parser/ytmusic/HomeFeed.js b/src/parser/ytmusic/HomeFeed.js deleted file mode 100644 index 96f178eb..00000000 --- a/src/parser/ytmusic/HomeFeed.js +++ /dev/null @@ -1,37 +0,0 @@ -import Parser from '../index'; - -class HomeFeed { - #page; - #actions; - #continuation; - - /** - * @param {object} response - API response. - * @param {import('../../core/Actions').default} actions - */ - constructor(response, actions) { - this.#actions = actions; - this.#page = Parser.parseResponse(response.data); - - const tab = this.#page.contents.tabs.get({ title: 'Home' }); - this.#continuation = tab.content?.continuation || this.#page.continuation_contents.continuation; - - /** @type {import('../classes/MusicCarouselShelf')[]} */ - this.sections = tab.content?.contents || this.#page.continuation_contents.contents; - } - - /** - * Retrieves home feed continuation. - * @returns {Promise.} - */ - async getContinuation() { - const response = await this.#actions.browse(this.#continuation, { is_ctoken: true, client: 'YTMUSIC' }); - return new HomeFeed(response, this.#actions); - } - - get page() { - return this.#page; - } -} - -export default HomeFeed; \ No newline at end of file diff --git a/src/parser/ytmusic/HomeFeed.ts b/src/parser/ytmusic/HomeFeed.ts new file mode 100644 index 00000000..85abe32e --- /dev/null +++ b/src/parser/ytmusic/HomeFeed.ts @@ -0,0 +1,60 @@ +import Parser, { ParsedResponse } from '../index'; +import Actions, { AxioslikeResponse } from '../../core/Actions'; +import { InnertubeError } from '../../utils/Utils'; + +import MusicCarouselShelfBasicHeader from '../classes/MusicCarouselShelfBasicHeader'; +import MusicTwoRowItem from '../classes/MusicTwoRowItem'; + +class HomeFeed { + #page; + #actions; + #continuation; + + sections; + + constructor(response: AxioslikeResponse | ParsedResponse, actions: Actions) { + this.#actions = actions; + this.#page = Parser.parseResponse((response as AxioslikeResponse).data); + + const tab = this.#page.contents.item().key('tabs').parsed().array().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(); + } + + this.sections = contents?.map((content) => ({ + header: content.header.item() as MusicCarouselShelfBasicHeader, + contents: content.contents.array() as Array + })); + } + + /** + * Retrieves home feed continuation. + */ + async getContinuation(): Promise { + if (!this.#continuation) + throw new InnertubeError('Continuation not found.'); + + const response = await this.#actions.browse(this.#continuation, { is_ctoken: true, client: 'YTMUSIC' }); + return new HomeFeed(response, this.#actions); + } + + get has_continuation(): boolean { + return !!this.#continuation; + } + + get page() { + return this.#page; + } +} + +export default HomeFeed; \ No newline at end of file