// Shared playlist utility functions // Used by both popup.js and background.js const PlaylistUtils = { /** * Extract video ID from YouTube URL * @param {string} url - YouTube URL * @returns {string|null} - Video ID or null if not found */ extractVideoId(url) { try { const urlObj = new URL(url); return urlObj.searchParams.get("v"); } catch (e) { return null; } }, /** * Normalize a YouTube watch URL down to just the `v` (video ID) param, * stripping queue/playlist params like `list`, `index`, and `pp` so that * navigating to the stored URL later doesn't drop the user back into * whatever queue or playlist they originally watched it from. * @param {string} url - YouTube URL * @returns {string} - Clean watch URL, or the original url if no video ID is found */ cleanVideoUrl(url) { const videoId = this.extractVideoId(url); return videoId ? `https://www.youtube.com/watch?v=${videoId}` : url; }, /** * Find a video in all playlists by URL * @param {Object} playlists - All playlists object * @param {string} url - Video URL to search for * @returns {Array|false} - [playlistName, videoIndex, isLastVideo] or false if not found */ findVideoInPlaylists(playlists, url) { const videoId = this.extractVideoId(url); if (!videoId) { return false; } // Check each playlist for (const playlistName in playlists) { const videos = playlists[playlistName]; // Check each video in the playlist for (let i = 0; i < videos.length; i++) { const itemVideoId = this.extractVideoId(videos[i].url); // If the video IDs match, return playlist info if (itemVideoId === videoId) { const isLastVideo = i === videos.length - 1; return [playlistName, i, isLastVideo]; } } } return false; }, /** * Find which playlist contains a video by URL * @param {Object} playlists - All playlists object * @param {string} url - Video URL to search for * @returns {string|false} - Playlist name or false if not found */ findPlaylist(playlists, url) { const result = this.findVideoInPlaylists(playlists, url); return result ? result[0] : false; }, /** * Add a video to a playlist if it's not already present in any playlist * @param {string} playlistName - Name of playlist to add to * @param {Object} video - Video object with url and title properties * @returns {Promise} - True if added, false if already exists */ async addVideoToPlaylist(playlistName, video) { const { playlists: currentPlaylists } = await browser.storage.local.get("playlists"); const cleanedVideo = { ...video, url: this.cleanVideoUrl(video.url) }; const alreadyExists = this.findPlaylist( currentPlaylists, cleanedVideo.url, ); if (alreadyExists) { console.log("Video already exists in playlist:", alreadyExists); return false; } // Add video to the specified playlist const updatedPlaylist = [...currentPlaylists[playlistName], cleanedVideo]; const updatedPlaylists = { ...currentPlaylists, [playlistName]: updatedPlaylist, }; await browser.storage.local.set({ playlists: updatedPlaylists }); console.log("Added video to playlist:", playlistName); return true; }, /** * Legacy function for backward compatibility with context menu * @param {string} playlistName - Name of playlist to add to * @param {Object} item - Context menu item with linkUrl and linkText * @returns {Promise} - True if added, false if already exists */ async addLinkToPlaylist(playlistName, item) { const video = { url: item.linkUrl, title: item.linkText, }; return await this.addVideoToPlaylist(playlistName, video); }, };