UNPKG

@np-dev/youtubei-js

Version:

A wrapper around YouTube's private API. Supports YouTube, YouTube Music, YouTube Kids and YouTube Studio (WIP).

294 lines 15.5 kB
var _Music_instances, _Music_session, _Music_actions, _Music_fetchInfoFromVideoId, _Music_fetchInfoFromListItem; import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet } from "tslib"; import Album from '../../parser/ytmusic/Album.js'; import Artist from '../../parser/ytmusic/Artist.js'; import Explore from '../../parser/ytmusic/Explore.js'; import HomeFeed from '../../parser/ytmusic/HomeFeed.js'; import Library from '../../parser/ytmusic/Library.js'; import Playlist from '../../parser/ytmusic/Playlist.js'; import Recap from '../../parser/ytmusic/Recap.js'; import Search from '../../parser/ytmusic/Search.js'; import TrackInfo from '../../parser/ytmusic/TrackInfo.js'; import AutomixPreviewVideo from '../../parser/classes/AutomixPreviewVideo.js'; import Message from '../../parser/classes/Message.js'; import MusicCarouselShelf from '../../parser/classes/MusicCarouselShelf.js'; import MusicDescriptionShelf from '../../parser/classes/MusicDescriptionShelf.js'; import MusicQueue from '../../parser/classes/MusicQueue.js'; import MusicTwoRowItem from '../../parser/classes/MusicTwoRowItem.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 * as Proto from '../../proto/index.js'; import { InnertubeError, generateRandomString, throwIfMissing } from '../../utils/Utils.js'; import { BrowseEndpoint, NextEndpoint, PlayerEndpoint, SearchEndpoint } from '../endpoints/index.js'; import { GetSearchSuggestionsEndpoint } from '../endpoints/music/index.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_fetchInfoFromListItem).call(this, target); } else if (typeof target === 'string') { return __classPrivateFieldGet(this, _Music_instances, "m", _Music_fetchInfoFromVideoId).call(this, target); } throw new InnertubeError('Invalid target, expected either a video id or a valid MusicTwoRowItem', target); } /** * Searches on YouTube Music. * @param query - Search query. * @param filters - Search filters. */ search(query, filters = {}) { return __awaiter(this, void 0, void 0, function* () { throwIfMissing({ query }); const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(SearchEndpoint.PATH, SearchEndpoint.build({ query, client: 'YTMUSIC', params: filters.type && filters.type !== 'all' ? Proto.encodeMusicSearchFilters(filters) : undefined })); return new Search(response, __classPrivateFieldGet(this, _Music_actions, "f"), Reflect.has(filters, 'type') && filters.type !== 'all'); }); } /** * Retrieves the home feed. */ getHomeFeed() { return __awaiter(this, void 0, void 0, function* () { const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(BrowseEndpoint.PATH, BrowseEndpoint.build({ browse_id: 'FEmusic_home', client: 'YTMUSIC' })); return new HomeFeed(response, __classPrivateFieldGet(this, _Music_actions, "f")); }); } /** * Retrieves the Explore feed. */ getExplore() { return __awaiter(this, void 0, void 0, function* () { const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(BrowseEndpoint.PATH, BrowseEndpoint.build({ client: 'YTMUSIC', browse_id: 'FEmusic_explore' })); return new Explore(response); // TODO: return new Explore(response, this.#actions); }); } /** * Retrieves the library. */ getLibrary() { return __awaiter(this, void 0, void 0, function* () { const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(BrowseEndpoint.PATH, BrowseEndpoint.build({ client: 'YTMUSIC', browse_id: 'FEmusic_library_landing' })); return new Library(response, __classPrivateFieldGet(this, _Music_actions, "f")); }); } /** * Retrieves artist's info & content. * @param artist_id - The artist id. */ getArtist(artist_id) { return __awaiter(this, void 0, void 0, function* () { throwIfMissing({ artist_id }); if (!artist_id.startsWith('UC') && !artist_id.startsWith('FEmusic_library_privately_owned_artist')) throw new InnertubeError('Invalid artist id', artist_id); const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(BrowseEndpoint.PATH, BrowseEndpoint.build({ client: 'YTMUSIC', browse_id: artist_id })); return new Artist(response, __classPrivateFieldGet(this, _Music_actions, "f")); }); } /** * Retrieves album. * @param album_id - The album id. */ getAlbum(album_id) { return __awaiter(this, void 0, void 0, function* () { throwIfMissing({ album_id }); if (!album_id.startsWith('MPR') && !album_id.startsWith('FEmusic_library_privately_owned_release')) throw new InnertubeError('Invalid album id', album_id); const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(BrowseEndpoint.PATH, BrowseEndpoint.build({ client: 'YTMUSIC', browse_id: album_id })); return new Album(response); }); } /** * Retrieves playlist. * @param playlist_id - The playlist id. */ getPlaylist(playlist_id) { return __awaiter(this, void 0, void 0, function* () { throwIfMissing({ playlist_id }); if (!playlist_id.startsWith('VL')) { playlist_id = `VL${playlist_id}`; } const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(BrowseEndpoint.PATH, BrowseEndpoint.build({ client: 'YTMUSIC', browse_id: playlist_id })); return new Playlist(response, __classPrivateFieldGet(this, _Music_actions, "f")); }); } /** * Retrieves up next. * @param video_id - The video id. * @param automix - Whether to enable automix. */ getUpNext(video_id, automix = true) { var _a, _b, _c; return __awaiter(this, void 0, void 0, function* () { throwIfMissing({ video_id }); const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(NextEndpoint.PATH, Object.assign(Object.assign({}, NextEndpoint.build({ video_id, client: 'YTMUSIC' })), { parse: true })); const tabs = (_a = response.contents_memo) === null || _a === void 0 ? void 0 : _a.getType(Tab); const tab = tabs === null || tabs === void 0 ? void 0 : tabs.first(); if (!tab) throw new InnertubeError('Could not find target tab.'); const music_queue = (_b = tab.content) === null || _b === void 0 ? void 0 : _b.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 = yield ((_c = automix_preview_video.playlist_video) === null || _c === void 0 ? void 0 : _c.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).first(); } return playlist_panel; }); } /** * Retrieves related content. * @param video_id - The video id. */ getRelated(video_id) { var _a; return __awaiter(this, void 0, void 0, function* () { throwIfMissing({ video_id }); const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(NextEndpoint.PATH, Object.assign(Object.assign({}, NextEndpoint.build({ video_id, client: 'YTMUSIC' })), { parse: true })); const tabs = (_a = response.contents_memo) === null || _a === void 0 ? void 0 : _a.getType(Tab); const tab = tabs === null || tabs === void 0 ? void 0 : tabs.matchCondition((tab) => { var _a, _b; return ((_b = (_a = tab.endpoint.payload.browseEndpointContextSupportedConfigs) === null || _a === void 0 ? void 0 : _a.browseEndpointContextMusicConfig) === null || _b === void 0 ? void 0 : _b.pageType) === 'MUSIC_PAGE_TYPE_TRACK_RELATED'; }); if (!tab) throw new InnertubeError('Could not find target tab.'); const page = yield tab.endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC', parse: true }); if (!page.contents) throw new InnertubeError('Unexpected response', page); const shelves = page.contents.item().as(SectionList).contents.as(MusicCarouselShelf, MusicDescriptionShelf); return shelves; }); } /** * Retrieves song lyrics. * @param video_id - The video id. */ getLyrics(video_id) { var _a; return __awaiter(this, void 0, void 0, function* () { throwIfMissing({ video_id }); const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(NextEndpoint.PATH, Object.assign(Object.assign({}, NextEndpoint.build({ video_id, client: 'YTMUSIC' })), { parse: true })); const tabs = (_a = response.contents_memo) === null || _a === void 0 ? void 0 : _a.getType(Tab); const tab = tabs === null || tabs === void 0 ? void 0 : tabs.matchCondition((tab) => { var _a, _b; return ((_b = (_a = tab.endpoint.payload.browseEndpointContextSupportedConfigs) === null || _a === void 0 ? void 0 : _a.browseEndpointContextMusicConfig) === null || _b === void 0 ? void 0 : _b.pageType) === 'MUSIC_PAGE_TYPE_TRACK_LYRICS'; }); if (!tab) throw new InnertubeError('Could not find target tab.'); const page = yield 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); }); } /** * Retrieves recap. */ getRecap() { return __awaiter(this, void 0, void 0, function* () { const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(BrowseEndpoint.PATH, BrowseEndpoint.build({ client: 'YTMUSIC_ANDROID', browse_id: 'FEmusic_listening_review' })); return new Recap(response, __classPrivateFieldGet(this, _Music_actions, "f")); }); } /** * Retrieves search suggestions for the given query. * @param query - The query. */ getSearchSuggestions(query) { return __awaiter(this, void 0, void 0, function* () { const response = yield __classPrivateFieldGet(this, _Music_actions, "f").execute(GetSearchSuggestionsEndpoint.PATH, Object.assign(Object.assign({}, GetSearchSuggestionsEndpoint.build({ input: query })), { parse: true })); if (!response.contents_memo) return []; const search_suggestions_sections = response.contents_memo.getType(SearchSuggestionsSection); return search_suggestions_sections; }); } } _Music_session = new WeakMap(), _Music_actions = new WeakMap(), _Music_instances = new WeakSet(), _Music_fetchInfoFromVideoId = function _Music_fetchInfoFromVideoId(video_id) { var _a; return __awaiter(this, void 0, void 0, function* () { const player_payload = PlayerEndpoint.build({ video_id, sts: (_a = __classPrivateFieldGet(this, _Music_session, "f").player) === null || _a === void 0 ? void 0 : _a.sts, client: 'YTMUSIC' }); const next_payload = NextEndpoint.build({ video_id, client: 'YTMUSIC' }); const player_response = __classPrivateFieldGet(this, _Music_actions, "f").execute(PlayerEndpoint.PATH, player_payload); const next_response = __classPrivateFieldGet(this, _Music_actions, "f").execute(NextEndpoint.PATH, next_payload); const response = yield Promise.all([player_response, next_response]); const cpn = generateRandomString(16); return new TrackInfo(response, __classPrivateFieldGet(this, _Music_actions, "f"), cpn); }); }, _Music_fetchInfoFromListItem = function _Music_fetchInfoFromListItem(list_item) { var _a; return __awaiter(this, void 0, void 0, function* () { if (!list_item) throw new InnertubeError('List item cannot be undefined'); if (!list_item.endpoint) throw new Error('This item does not have an endpoint.'); const player_response = list_item.endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC', playbackContext: { contentPlaybackContext: Object.assign({ signatureTimestamp: (_a = __classPrivateFieldGet(this, _Music_session, "f").player) === null || _a === void 0 ? void 0 : _a.sts }) } }); const next_response = list_item.endpoint.call(__classPrivateFieldGet(this, _Music_actions, "f"), { client: 'YTMUSIC', enablePersistentPlaylistPanel: true, override_endpoint: '/next' }); const cpn = generateRandomString(16); const response = yield Promise.all([player_response, next_response]); return new TrackInfo(response, __classPrivateFieldGet(this, _Music_actions, "f"), cpn); }); }; export default Music; //# sourceMappingURL=Music.js.map