diff --git a/src/core/clients/Music.ts b/src/core/clients/Music.ts index 1638c2b4e..4e4a6e2ee 100644 --- a/src/core/clients/Music.ts +++ b/src/core/clients/Music.ts @@ -11,6 +11,8 @@ import Message from '../../parser/classes/Message.js'; import MusicDescriptionShelf from '../../parser/classes/MusicDescriptionShelf.js'; import MusicQueue from '../../parser/classes/MusicQueue.js'; import MusicTwoRowItem from '../../parser/classes/MusicTwoRowItem.js'; +import MusicResponsiveListItem from '../../parser/classes/MusicResponsiveListItem.js'; +import NavigationEndpoint from '../../parser/classes/NavigationEndpoint.js'; import PlaylistPanel from '../../parser/classes/PlaylistPanel.js'; import SearchSuggestionsSection from '../../parser/classes/SearchSuggestionsSection.js'; import SectionList from '../../parser/classes/SectionList.js'; @@ -44,9 +46,13 @@ export default class Music { * Retrieves track info. Passing a list item of type MusicTwoRowItem automatically starts a radio. * @param target - Video id or a list item. */ - getInfo(target: string | MusicTwoRowItem): Promise { + getInfo(target: string | MusicTwoRowItem | MusicResponsiveListItem | NavigationEndpoint): Promise { if (target instanceof MusicTwoRowItem) { - return this.#fetchInfoFromListItem(target); + return this.#fetchInfoFromEndpoint(target.endpoint); + } else if (target instanceof MusicResponsiveListItem) { + return this.#fetchInfoFromEndpoint(target.overlay?.content?.endpoint ?? target.endpoint); + } else if (target instanceof NavigationEndpoint) { + return this.#fetchInfoFromEndpoint(target); } else if (typeof target === 'string') { return this.#fetchInfoFromVideoId(target); } @@ -75,14 +81,11 @@ export default class Music { return new TrackInfo(response, this.#actions, cpn); } - async #fetchInfoFromListItem(list_item: MusicTwoRowItem | undefined): Promise { - if (!list_item) - throw new InnertubeError('List item cannot be undefined'); - - if (!list_item.endpoint) + async #fetchInfoFromEndpoint(endpoint?: NavigationEndpoint): Promise { + if (!endpoint) throw new Error('This item does not have an endpoint.'); - const player_response = list_item.endpoint.call(this.#actions, { + const player_response = endpoint.call(this.#actions, { client: 'YTMUSIC', playbackContext: { contentPlaybackContext: { @@ -93,7 +96,7 @@ export default class Music { } }); - const next_response = list_item.endpoint.call(this.#actions, { + const next_response = endpoint.call(this.#actions, { client: 'YTMUSIC', enablePersistentPlaylistPanel: true, override_endpoint: '/next' diff --git a/test/main.test.ts b/test/main.test.ts index 030447c05..4c98de997 100644 --- a/test/main.test.ts +++ b/test/main.test.ts @@ -302,6 +302,33 @@ describe('YouTube.js Tests', () => { // expect(info.basic_info.id).toBe('WSeNSzJ2-Jw'); // }); + test('Innertube#music.getInfo.MusicResponsiveListItem', async () => { + const playlist = await innertube.music.getPlaylist('PLQxo8OvVvJ1WI_Bp67F2wdIl_R2Rc_1-u'); + expect(playlist).toBeDefined(); + expect(playlist.header).toBeDefined(); + expect(playlist.contents).toBeDefined(); + expect(playlist.contents?.length).toBeGreaterThan(0); + + const info = await innertube.music.getInfo(playlist.contents!.first()) + expect(info).toBeDefined(); + }); + + test('Innertube#music.getInfo.NavEndpoint', async () => { + const playlist = await innertube.music.getPlaylist('PLQxo8OvVvJ1WI_Bp67F2wdIl_R2Rc_1-u'); + expect(playlist).toBeDefined(); + expect(playlist.header).toBeDefined(); + expect(playlist.contents).toBeDefined(); + expect(playlist.contents?.length).toBeGreaterThan(0); + + const playlistPlayEndpoint = playlist.header!.as(YTNodes.MusicResponsiveHeader).buttons.firstOfType(YTNodes.MusicPlayButton)!.endpoint + + const info = await innertube.music.getInfo(playlistPlayEndpoint) + expect(info).toBeDefined(); + + const upNext = await info.getUpNext(); + expect(upNext.playlist_id).toBe("PLQxo8OvVvJ1WI_Bp67F2wdIl_R2Rc_1-u"); + }); + describe('Innertube#music.search', () => { let search: YTMusic.Search;