playlist-utils.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. // Shared playlist utility functions
  2. // Used by both popup.js and background.js
  3. const PlaylistUtils = {
  4. /**
  5. * Extract video ID from YouTube URL
  6. * @param {string} url - YouTube URL
  7. * @returns {string|null} - Video ID or null if not found
  8. */
  9. extractVideoId(url) {
  10. try {
  11. const urlObj = new URL(url);
  12. return urlObj.searchParams.get("v");
  13. } catch (e) {
  14. return null;
  15. }
  16. },
  17. /**
  18. * Normalize a YouTube watch URL down to just the `v` (video ID) param,
  19. * stripping queue/playlist params like `list`, `index`, and `pp` so that
  20. * navigating to the stored URL later doesn't drop the user back into
  21. * whatever queue or playlist they originally watched it from.
  22. * @param {string} url - YouTube URL
  23. * @returns {string} - Clean watch URL, or the original url if no video ID is found
  24. */
  25. cleanVideoUrl(url) {
  26. const videoId = this.extractVideoId(url);
  27. return videoId ? `https://www.youtube.com/watch?v=${videoId}` : url;
  28. },
  29. /**
  30. * Find a video in all playlists by URL
  31. * @param {Object} playlists - All playlists object
  32. * @param {string} url - Video URL to search for
  33. * @returns {Array|false} - [playlistName, videoIndex, isLastVideo] or false if not found
  34. */
  35. findVideoInPlaylists(playlists, url) {
  36. const videoId = this.extractVideoId(url);
  37. if (!videoId) {
  38. return false;
  39. }
  40. // Check each playlist
  41. for (const playlistName in playlists) {
  42. const videos = playlists[playlistName];
  43. // Check each video in the playlist
  44. for (let i = 0; i < videos.length; i++) {
  45. const itemVideoId = this.extractVideoId(videos[i].url);
  46. // If the video IDs match, return playlist info
  47. if (itemVideoId === videoId) {
  48. const isLastVideo = i === videos.length - 1;
  49. return [playlistName, i, isLastVideo];
  50. }
  51. }
  52. }
  53. return false;
  54. },
  55. /**
  56. * Find which playlist contains a video by URL
  57. * @param {Object} playlists - All playlists object
  58. * @param {string} url - Video URL to search for
  59. * @returns {string|false} - Playlist name or false if not found
  60. */
  61. findPlaylist(playlists, url) {
  62. const result = this.findVideoInPlaylists(playlists, url);
  63. return result ? result[0] : false;
  64. },
  65. /**
  66. * Add a video to a playlist if it's not already present in any playlist
  67. * @param {string} playlistName - Name of playlist to add to
  68. * @param {Object} video - Video object with url and title properties
  69. * @returns {Promise<boolean>} - True if added, false if already exists
  70. */
  71. async addVideoToPlaylist(playlistName, video) {
  72. const { playlists: currentPlaylists } =
  73. await browser.storage.local.get("playlists");
  74. const cleanedVideo = { ...video, url: this.cleanVideoUrl(video.url) };
  75. const alreadyExists = this.findPlaylist(
  76. currentPlaylists,
  77. cleanedVideo.url,
  78. );
  79. if (alreadyExists) {
  80. console.log("Video already exists in playlist:", alreadyExists);
  81. return false;
  82. }
  83. // Add video to the specified playlist
  84. const updatedPlaylist = [...currentPlaylists[playlistName], cleanedVideo];
  85. const updatedPlaylists = {
  86. ...currentPlaylists,
  87. [playlistName]: updatedPlaylist,
  88. };
  89. await browser.storage.local.set({ playlists: updatedPlaylists });
  90. console.log("Added video to playlist:", playlistName);
  91. return true;
  92. },
  93. /**
  94. * Legacy function for backward compatibility with context menu
  95. * @param {string} playlistName - Name of playlist to add to
  96. * @param {Object} item - Context menu item with linkUrl and linkText
  97. * @returns {Promise<boolean>} - True if added, false if already exists
  98. */
  99. async addLinkToPlaylist(playlistName, item) {
  100. const video = {
  101. url: item.linkUrl,
  102. title: item.linkText,
  103. };
  104. return await this.addVideoToPlaylist(playlistName, video);
  105. },
  106. };