UNPKG

@imput/youtubei.js

Version:

A JavaScript client for YouTube's private API, known as InnerTube. Fork of youtubei.js

222 lines 13.1 kB
var _Music_instances, _Music_session, _Music_actions, _Music_fetchInfoFromVideoId, _Music_fetchInfoFromEndpoint; import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib"; import { generateRandomString, InnertubeError, throwIfMissing, u8ToBase64 } from '../../utils/Utils.js'; import { Album, Artist, Explore, HomeFeed, Library, Playlist, Recap, Search, TrackInfo } from '../../parser/ytmusic/index.js'; import AutomixPreviewVideo from '../../parser/classes/AutomixPreviewVideo.js'; 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'; import Tab from '../../parser/classes/Tab.js'; import { SearchFilter } from '../../../protos/generated/misc/params.js'; class Music { constructor(session) { _Music_instances.add(this); _Music_session.set(this, void 0); _Music_actions.set(this, void 0); __classPrivateFieldSet(this, _Music_session, session, "f"); __classPrivateFieldSet(this, _Music_actions, session.actions, "f"); } /** * Retrieves track info. Passing a list item of type MusicTwoRowItem automatically starts a radio. * @param target - Video id or a list item. */ getInfo(target) { if (target instanceof MusicTwoRowItem) { return __classPrivateFieldGet(this, _Music_instances, "m", _Music_fetchInfoFromEndpoint).call(this, target.endpoint); } else if (target instanceof MusicResponsiveListItem) { return __classPrivateFieldGet(this, _Music_instances, "m", _Music_fetchInfoFromEndpoint).call(this, target.overlay?.content?.endpoint ?? target.endpoint); } else if (target instanceof NavigationEndpoint) { return __classPrivateFieldGet(this, _Music_instances, "m", _Music_fetchInfoFromEndpoint).call(this, target); } return __classPrivateFieldGet(this, _Music_instances, "m", _Music_fetchInfoFromVideoId).call(this, target); } async search(query, filters = {}) { throwIfMissing({ query }); let params; if (filters.type && filters.type !== 'all') { const writer = SearchFilter.encode({ filters: { musicSearchType: { [filters.type]: true } } }); params = encodeURIComponent(u8ToBase64(writer.finish())); } const search_endpoint = new NavigationEndpoint({ searchEndpoint: { query, params } }); const response = await search_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC' }); return new Search(response, __classPrivateFieldGet(this, _Music_actions, "f"), Reflect.has(filters, 'type') && filters.type !== 'all'); } async getHomeFeed() { const browse_endpoint = new NavigationEndpoint({ browseEndpoint: { browseId: 'FEmusic_home' } }); const response = await browse_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC' }); return new HomeFeed(response, __classPrivateFieldGet(this, _Music_actions, "f")); } async getExplore() { const browse_endpoint = new NavigationEndpoint({ browseEndpoint: { browseId: 'FEmusic_explore' } }); const response = await browse_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC' }); return new Explore(response); // TODO: return new Explore(response, this.#actions); } async getLibrary() { const browse_endpoint = new NavigationEndpoint({ browseEndpoint: { browseId: 'FEmusic_library_landing' } }); const response = await browse_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC' }); return new Library(response, __classPrivateFieldGet(this, _Music_actions, "f")); } async getArtist(artist_id) { if (!artist_id || !artist_id.startsWith('UC') && !artist_id.startsWith('FEmusic_library_privately_owned_artist')) throw new InnertubeError('Invalid artist id', artist_id); const browse_endpoint = new NavigationEndpoint({ browseEndpoint: { browseId: artist_id } }); const response = await browse_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC' }); return new Artist(response, __classPrivateFieldGet(this, _Music_actions, "f")); } async getAlbum(album_id) { if (!album_id || !album_id.startsWith('MPR') && !album_id.startsWith('FEmusic_library_privately_owned_release')) throw new InnertubeError('Invalid album id', album_id); const browse_endpoint = new NavigationEndpoint({ browseEndpoint: { browseId: album_id } }); const response = await browse_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC' }); return new Album(response); } async getPlaylist(playlist_id) { if (!playlist_id.startsWith('VL')) playlist_id = `VL${playlist_id}`; const browse_endpoint = new NavigationEndpoint({ browseEndpoint: { browseId: playlist_id } }); const response = await browse_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC' }); return new Playlist(response, __classPrivateFieldGet(this, _Music_actions, "f")); } async getUpNext(video_id, automix = true) { throwIfMissing({ video_id }); const watch_next_endpoint = new NavigationEndpoint({ watchNextEndpoint: { videoId: video_id } }); const response = await watch_next_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC', parse: true }); const tabs = response.contents_memo?.getType(Tab); const tab = tabs?.[0]; if (!tab) throw new InnertubeError('Could not find target tab.'); const music_queue = tab.content?.as(MusicQueue); 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.as(PlaylistPanel); if (!playlist_panel.playlist_id && automix) { const automix_preview_video = playlist_panel.contents.firstOfType(AutomixPreviewVideo); if (!automix_preview_video) throw new InnertubeError('Automix item not found'); const page = await automix_preview_video.playlist_video?.endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { videoId: video_id, client: 'YTMUSIC', parse: true }); if (!page || !page.contents_memo) throw new InnertubeError('Could not fetch automix'); return page.contents_memo.getType(PlaylistPanel)[0]; } return playlist_panel; } async getRelated(video_id) { throwIfMissing({ video_id }); const watch_next_endpoint = new NavigationEndpoint({ watchNextEndpoint: { videoId: video_id } }); const response = await watch_next_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC', parse: true }); const tabs = response.contents_memo?.getType(Tab); const tab = tabs?.find((tab) => tab.endpoint.payload.browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType === 'MUSIC_PAGE_TYPE_TRACK_RELATED'); if (!tab) throw new InnertubeError('Could not find target tab.'); const page = await tab.endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC', parse: true }); if (!page.contents) throw new InnertubeError('Unexpected response', page); return page.contents.item().as(SectionList, Message); } async getLyrics(video_id) { throwIfMissing({ video_id }); const watch_next_endpoint = new NavigationEndpoint({ watchNextEndpoint: { videoId: video_id } }); const response = await watch_next_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC', parse: true }); const tabs = response.contents_memo?.getType(Tab); const tab = tabs?.find((tab) => tab.endpoint.payload.browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType === 'MUSIC_PAGE_TYPE_TRACK_LYRICS'); if (!tab) throw new InnertubeError('Could not find target tab.'); const page = await tab.endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC', parse: true }); if (!page.contents) throw new InnertubeError('Unexpected response', page); if (page.contents.item().type === 'Message') throw new InnertubeError(page.contents.item().as(Message).text.toString(), video_id); const section_list = page.contents.item().as(SectionList).contents; return section_list.firstOfType(MusicDescriptionShelf); } async getRecap() { const browse_endpoint = new NavigationEndpoint({ browseEndpoint: { browseId: 'FEmusic_listening_review' } }); const response = await browse_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC' }); return new Recap(response, __classPrivateFieldGet(this, _Music_actions, "f")); } async getSearchSuggestions(input) { const response = await __classPrivateFieldGet(this, _Music_actions, "f").execute('/music/get_search_suggestions', { input, client: 'YTMUSIC', parse: true }); if (!response.contents_memo) return []; return response.contents_memo.getType(SearchSuggestionsSection); } } _Music_session = new WeakMap(), _Music_actions = new WeakMap(), _Music_instances = new WeakSet(), _Music_fetchInfoFromVideoId = async function _Music_fetchInfoFromVideoId(video_id) { const payload = { videoId: video_id, racyCheckOk: true, contentCheckOk: true }; const watch_endpoint = new NavigationEndpoint({ watchEndpoint: payload }); const watch_next_endpoint = new NavigationEndpoint({ watchNextEndpoint: payload }); const extra_payload = { playbackContext: { contentPlaybackContext: { vis: 0, splay: false, lactMilliseconds: '-1', signatureTimestamp: __classPrivateFieldGet(this, _Music_session, "f").player?.sts } }, client: 'YTMUSIC' }; if (__classPrivateFieldGet(this, _Music_session, "f").po_token) { extra_payload.serviceIntegrityDimensions = { poToken: __classPrivateFieldGet(this, _Music_session, "f").po_token }; } const watch_response = watch_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), extra_payload); const watch_next_response = watch_next_endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC' }); const response = await Promise.all([watch_response, watch_next_response]); const cpn = generateRandomString(16); return new TrackInfo(response, __classPrivateFieldGet(this, _Music_actions, "f"), cpn); }, _Music_fetchInfoFromEndpoint = async function _Music_fetchInfoFromEndpoint(endpoint) { if (!endpoint) throw new Error('This item does not have an endpoint.'); const extra_payload = { playbackContext: { contentPlaybackContext: { vis: 0, splay: false, lactMilliseconds: '-1', signatureTimestamp: __classPrivateFieldGet(this, _Music_session, "f").player?.sts } }, client: 'YTMUSIC' }; if (__classPrivateFieldGet(this, _Music_session, "f").po_token) { extra_payload.serviceIntegrityDimensions = { poToken: __classPrivateFieldGet(this, _Music_session, "f").po_token }; } const player_response = endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), extra_payload); const next_response = endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC', enablePersistentPlaylistPanel: true, override_endpoint: '/next' }); const cpn = generateRandomString(16); const response = await Promise.all([player_response, next_response]); return new TrackInfo(response, __classPrivateFieldGet(this, _Music_actions, "f"), cpn); }; export default Music; //# sourceMappingURL=Music.js.map