Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save sandorex/6bdd51ef467a079c87f19ba469a8bc7c to your computer and use it in GitHub Desktop.
Save sandorex/6bdd51ef467a079c87f19ba469a8bc7c to your computer and use it in GitHub Desktop.
Clean YouTube Playlist Videos
// ==UserScript==
// @name Clean Youtube Playlist
// @match *://www.youtube.com/playlist*
// @grant GM_registerMenuCommand
// @grant GM_getValue
// @homepageURL https://gist.github.com/sandorex/6bdd51ef467a079c87f19ba469a8bc7c
// @downloadURL https://gist.githubusercontent.com/sandorex/6bdd51ef467a079c87f19ba469a8bc7c/raw/clean_youtube_playlist.user.js
// @noframes
// @version 1.1.5
// @author Sandorex
// @description Removes all videos from a youtube playlist
// ==/UserScript==
//
// Original code made by qoomon, but has been heavily modified should work for any language
// https://gist.github.com/qoomon/a3f71ff2b2a18ee297c9435d539d5247
GM_registerMenuCommand('Clean Playlist', function() {
if (!confirm("This will erase your playlist permanently, are you sure you want to proceed?"))
return;
// FOR STANDALONE USE COPY THIS BLOCK
(async function() {
const sleep = (timeout) => new Promise(res => setTimeout(res, timeout))
const untilDefined = async (factory, timeout = 100) => {
while (true) {
let value = factory()
if (value != null) return value
await sleep(timeout)
}
}
// note that
let playlistName = document.querySelector('.metadata-wrapper #container #text')?.textContent || null;
if (!playlistName) {
console.error("Playlist name could not be read, aborting..");
alert("Could not read the playlist name, aborting..");
return;
}
// TODO test if the playlist name is good, by checking the remove from X button, maybe get playlist from that too?
console.info(`Removing videos from playlist '${playlistName}'`);
while (true) {
let videos = document.querySelectorAll('#primary ytd-playlist-video-renderer')
if(videos.length == 0) break
for (let videoElement of videos) {
let videoTitle = videoElement.querySelector('a#video-title')
console.info("Removing: " + videoTitle.innerText)
console.info(" " + videoTitle.href)
let actionMenu = videoElement.querySelector('#menu')
let actionMenuButton = actionMenu.querySelector('#button')
console.debug("Opening menu", actionMenuButton)
actionMenuButton.click()
let removeButton = await untilDefined(() =>
document.evaluate(`//tp-yt-paper-listbox/ytd-menu-service-item-renderer[./tp-yt-paper-item/yt-formatted-string/span[text() = '${playlistName}']]`,
document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue);
console.debug("Click the remove button", removeButton)
removeButton.click()
await sleep(GM_getValue("sleep_duration", null) || 200)
}
}
console.info("done!")
})();
// FOR STANDALONE USE COPY THIS BLOCK
})
@sandorex
Copy link
Author

I made it a violentmonkey script, it should work with all languages but do tell me if it does not and ill fix it
If it's too fast for you then set sleep_duration to your desired time in violentmonkey settings

@sandorex
Copy link
Author

sandorex commented Aug 8, 2023

It seems the XPath to the playlist title changed, but i switched to using only the id hopefully they wont change that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment