UNPKG

@antoniojps/youtubei.js

Version:

A JavaScript client for YouTube's private API, known as InnerTube.

234 lines 10.2 kB
import { InnertubeError, throwIfMissing } from '../../utils/Utils.js'; import Playlist from '../../parser/youtube/Playlist.js'; import NavigationEndpoint from '../../parser/classes/NavigationEndpoint.js'; export default class PlaylistManager { #actions; constructor(actions) { this.#actions = actions; } /** * Creates a playlist. * @param title - The title of the playlist. * @param video_ids - An array of video IDs to add to the playlist. */ async create(title, video_ids) { throwIfMissing({ title, video_ids }); if (!this.#actions.session.logged_in) throw new InnertubeError('You must be signed in to perform this operation.'); const create_playlist_endpoint = new NavigationEndpoint({ createPlaylistServiceEndpoint: { title, videoIds: video_ids } }); const response = await create_playlist_endpoint.call(this.#actions); return { success: response.success, status_code: response.status_code, playlist_id: response.data.playlistId, data: response.data }; } /** * Deletes a given playlist. * @param playlist_id - The playlist ID. */ async delete(playlist_id) { throwIfMissing({ playlist_id }); if (!this.#actions.session.logged_in) throw new InnertubeError('You must be signed in to perform this operation.'); const delete_playlist_endpoint = new NavigationEndpoint({ deletePlaylistServiceEndpoint: { sourcePlaylistId: playlist_id } }); const response = await delete_playlist_endpoint.call(this.#actions); return { playlist_id, success: response.success, status_code: response.status_code, data: response.data }; } /** * Adds a given playlist to the library of a user. * @param playlist_id - The playlist ID. */ async addToLibrary(playlist_id) { throwIfMissing({ playlist_id }); if (!this.#actions.session.logged_in) throw new InnertubeError('You must be signed in to perform this operation.'); const like_playlist_endpoint = new NavigationEndpoint({ likeEndpoint: { status: 'LIKE', target: playlist_id } }); return await like_playlist_endpoint.call(this.#actions); } /** * Remove a given playlist to the library of a user. * @param playlist_id - The playlist ID. */ async removeFromLibrary(playlist_id) { throwIfMissing({ playlist_id }); if (!this.#actions.session.logged_in) throw new InnertubeError('You must be signed in to perform this operation.'); const remove_like_playlist_endpoint = new NavigationEndpoint({ likeEndpoint: { status: 'INDIFFERENT', target: playlist_id } }); return await remove_like_playlist_endpoint.call(this.#actions); } /** * Adds videos to a given playlist. * @param playlist_id - The playlist ID. * @param video_ids - An array of video IDs to add to the playlist. */ async addVideos(playlist_id, video_ids) { throwIfMissing({ playlist_id, video_ids }); if (!this.#actions.session.logged_in) throw new InnertubeError('You must be signed in to perform this operation.'); const playlist_edit_endpoint = new NavigationEndpoint({ playlistEditEndpoint: { playlistId: playlist_id, actions: video_ids.map((id) => ({ action: 'ACTION_ADD_VIDEO', addedVideoId: id })) } }); const response = await playlist_edit_endpoint.call(this.#actions); return { playlist_id, action_result: response.data.actions // TODO: implement actions in the parser }; } /** * Removes videos from a given playlist. * @param playlist_id - The playlist ID. * @param video_ids - An array of video IDs to remove from the playlist. * @param use_set_video_ids - Option to remove videos using set video IDs. */ async removeVideos(playlist_id, video_ids, use_set_video_ids = false) { throwIfMissing({ playlist_id, video_ids }); if (!this.#actions.session.logged_in) throw new InnertubeError('You must be signed in to perform this operation.'); const playlist = await this.#getPlaylist(playlist_id); if (!playlist.info.is_editable) throw new InnertubeError('This playlist cannot be edited.', playlist_id); const payload = { playlistId: playlist_id, actions: [] }; const getSetVideoIds = async (pl) => { const key_id = use_set_video_ids ? 'set_video_id' : 'id'; const videos = pl.videos.filter((video) => video_ids.includes(video.key(key_id).string())); videos.forEach((video) => payload.actions.push({ action: 'ACTION_REMOVE_VIDEO', setVideoId: video.key('set_video_id').string() })); if (payload.actions.length < video_ids.length) { const next = await pl.getContinuation(); return getSetVideoIds(next); } }; await getSetVideoIds(playlist); if (!payload.actions.length) throw new InnertubeError('Given video ids were not found in this playlist.', video_ids); const playlist_edit_endpoint = new NavigationEndpoint({ playlistEditEndpoint: payload }); const response = await playlist_edit_endpoint.call(this.#actions); return { playlist_id, action_result: response.data.actions // TODO: implement actions in the parser }; } /** * Moves a video to a new position within a given playlist. * @param playlist_id - The playlist ID. * @param moved_video_id - The video ID to move. * @param predecessor_video_id - The video ID to move the moved video before. */ async moveVideo(playlist_id, moved_video_id, predecessor_video_id) { throwIfMissing({ playlist_id, moved_video_id, predecessor_video_id }); if (!this.#actions.session.logged_in) throw new InnertubeError('You must be signed in to perform this operation.'); const playlist = await this.#getPlaylist(playlist_id); if (!playlist.info.is_editable) throw new InnertubeError('This playlist cannot be edited.', playlist_id); const payload = { playlistId: playlist_id, actions: [] }; let set_video_id_0, set_video_id_1; const getSetVideoIds = async (pl) => { const video_0 = pl.videos.find((video) => moved_video_id === video.key('id').string()); const video_1 = pl.videos.find((video) => predecessor_video_id === video.key('id').string()); set_video_id_0 = set_video_id_0 || video_0?.key('set_video_id').string(); set_video_id_1 = set_video_id_1 || video_1?.key('set_video_id').string(); if (!set_video_id_0 || !set_video_id_1) { const next = await pl.getContinuation(); return getSetVideoIds(next); } }; await getSetVideoIds(playlist); payload.actions.push({ action: 'ACTION_MOVE_VIDEO_AFTER', setVideoId: set_video_id_0, movedSetVideoIdPredecessor: set_video_id_1 }); const playlist_edit_endpoint = new NavigationEndpoint({ playlistEditEndpoint: payload }); const response = await playlist_edit_endpoint.call(this.#actions); return { playlist_id, action_result: response.data.actions // TODO: implement actions in the parser }; } async #getPlaylist(playlist_id) { if (!playlist_id.startsWith('VL')) { playlist_id = `VL${playlist_id}`; } const browse_endpoint = new NavigationEndpoint({ browseEndpoint: { browseId: playlist_id } }); const browse_response = await browse_endpoint.call(this.#actions, { parse: true }); return new Playlist(this.#actions, browse_response, true); } /** * Sets the name for the given playlist. * @param playlist_id - The playlist ID. * @param name - The name / title to use for the playlist. */ async setName(playlist_id, name) { throwIfMissing({ playlist_id, name }); if (!this.#actions.session.logged_in) throw new InnertubeError('You must be signed in to perform this operation.'); const payload = { playlist_id, actions: [] }; payload.actions.push({ action: 'ACTION_SET_PLAYLIST_NAME', playlistName: name }); const playlist_edit_endpoint = new NavigationEndpoint({ playlistEditEndpoint: payload }); const response = await playlist_edit_endpoint.call(this.#actions); return { playlist_id, action_result: response.data.actions }; } /** * Sets the description for the given playlist. * @param playlist_id - The playlist ID. * @param description - The description to use for the playlist. */ async setDescription(playlist_id, description) { throwIfMissing({ playlist_id, description }); if (!this.#actions.session.logged_in) throw new InnertubeError('You must be signed in to perform this operation.'); const payload = { playlistId: playlist_id, actions: [] }; payload.actions.push({ action: 'ACTION_SET_PLAYLIST_DESCRIPTION', playlistDescription: description }); const playlist_edit_endpoint = new NavigationEndpoint({ playlistEditEndpoint: payload }); const response = await playlist_edit_endpoint.call(this.#actions); return { playlist_id, action_result: response.data.actions }; } } //# sourceMappingURL=PlaylistManager.js.map