Advertisement
Guest User

Remove YouTube playlists (desktop website)

a guest
Jan 14th, 2025
69
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. (function () {
  2.     const selectors = {
  3.         playlists: '.yt-lockup-view-model-wiz__content-image:not(.unplaylisted)',
  4.         badge: '.yt-thumbnail-overlay-badge-view-model-wiz',
  5.         badgeText: '.badge-shape-wiz__text',
  6.         ancestor: '.ytd-rich-item-renderer.lockup',
  7.         title: '.yt-lockup-metadata-view-model-wiz__title',
  8.         subtitle: '.yt-lockup-metadata-view-model-wiz__metadata',
  9.         thumbnail: '.yt-thumbnail-view-model__image img',
  10.     };
  11.     const classNames = {
  12.         unplaylisted: 'unplaylisted',
  13.         unplaylistedCSS: 'unplaylisted-styles',
  14.         thumbnailLoaded: 'yt-core-image--loaded',
  15.     };
  16.  
  17.     function removeStackedThumbnails () {
  18.         const style = document.createElement('style');
  19.         style.innerHTML = `div[class^='collections-stack'] { display: none; }`;
  20.         style.className = classNames.unplaylistedCSS,
  21.         document.head.appendChild(style);
  22.     }
  23.  
  24.     function unPlaylist (playListElement) {
  25.         const playlists = document.querySelectorAll(selectors.playlists);
  26.  
  27.         if (!playListElement && playlists.length === 0) return;
  28.  
  29.         if (playListElement) {
  30.             updatePlaylist(playListElement);
  31.         } else {
  32.             playlists.forEach(function (playlist) {
  33.                 updatePlaylist(playlist);
  34.             });
  35.         }
  36.     }
  37.    
  38.     function updatePlaylist(playlist) {
  39.         if (!playlist) throw new Error('Error: no playlist passed to this function!');
  40.  
  41.         const oldUrl = playlist.getAttribute('href'),
  42.             newUrl = oldUrl.split('&')[0],
  43.             badge = playlist.querySelector(selectors.badge),
  44.             ancestor = playlist.closest(selectors.ancestor),
  45.             badgeText = playlist.querySelector(selectors.badgeText).innerText,
  46.             videoTitleLink = playlist.nextElementSibling.querySelector(selectors.title),
  47.             videoTitle = videoTitleLink.lastChild,
  48.             oldVideoTitle = videoTitle.innerText,
  49.             newVideoTitle = oldVideoTitle.replace('Mix - ', ''),
  50.             videoSubtitle = playlist.nextElementSibling.querySelector(selectors.subtitle),
  51.             videoThumbnail = playlist.querySelector(selectors.thumbnail);
  52.  
  53.         // Remove playlist query param from video links.
  54.         playlist.setAttribute('href', newUrl);
  55.         videoTitleLink.setAttribute('href', newUrl);
  56.  
  57.         // The script is executed after the playlist's thumbnail's "src" attribute
  58.         // has been populated. However, the class required to actually display it
  59.         // has not been added yet, so we add it here.
  60.         videoThumbnail.classList.add(classNames.thumbnailLoaded);
  61.  
  62.         // Remove most of the mix-exclusive design (badge and text).
  63.         badge.remove();
  64.         if (videoTitle) videoTitle.innerHTML = newVideoTitle;
  65.         if (videoSubtitle) videoSubtitle.remove();
  66.  
  67.         playlist.classList.add(classNames.unplaylisted);
  68.  
  69.         // Remove all event listeners from the video which would send you to the
  70.         // playlist despite the link href not having the playlist query params.
  71.         const  newAncestor = ancestor.cloneNode(true);
  72.  
  73.         ancestor.parentNode.replaceChild(newAncestor, ancestor);
  74.     }
  75.  
  76.     // Dynamically remove playlists as they appear in the DOM.
  77.     // @note: There may be a better way to do this than listening for the entire
  78.     // <body> element. However I don't feel like doing that rn. :)
  79.     const pageObserver = new MutationObserver(function (mutations) {
  80.         mutations.forEach(function (mutation) {
  81.             mutation.addedNodes.forEach(function (node) {
  82.                 if (node.nodeType !== Node.ELEMENT_NODE) return;
  83.  
  84.                 const matchingElements = node.querySelectorAll(selectors.playlists);
  85.  
  86.                 if (matchingElements.length === 0) return;
  87.    
  88.                 matchingElements.forEach(function (elem) {
  89.                     // Wait for the image to load before running the unPlaylist function.
  90.                     const videoThumbnail = elem.querySelector(selectors.thumbnail);
  91.                     const thumbnailObserver = new MutationObserver(function (thumbnailMutations) {
  92.                         thumbnailMutations.forEach(function (thumbnailMutation) {
  93.                             unPlaylist(elem);
  94.  
  95.                             thumbnailObserver.disconnect();
  96.                         });
  97.                     });
  98.  
  99.                     thumbnailObserver.observe(videoThumbnail, { attributeFilter: ['src'] });
  100.                 });
  101.             });
  102.         });
  103.     });
  104.  
  105.     pageObserver.observe(document.body, { childList: true, subtree: true });
  106.  
  107.     removeStackedThumbnails();
  108.     unPlaylist();
  109. }());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement