Skip to content

Instantly share code, notes, and snippets.

@Acendro
Last active June 15, 2023 15:03
Show Gist options
  • Save Acendro/098f82e014a21ea18cad25af77d0bcff to your computer and use it in GitHub Desktop.
Save Acendro/098f82e014a21ea18cad25af77d0bcff to your computer and use it in GitHub Desktop.
Removes watched videos from playlist either by %watched or removes all videos of that playlist // tampermonkey
// ==UserScript==
// @name Youtube Playlist Cleanser 1.0
// @namespace Youtube Playlist Cleanser 1.0
// @version 1.0
// @description Removes watched videos from playlist either by %watched or all
// @author Acendro
// @include http*://*.youtube.com/*
// @include http*://youtube.com/*
// @include http*://*.youtu.be/*
// @include http*://youtu.be/*
// @run-at document-end
// ==/UserScript==
// Config
var show_on_all = false; //Set to true do display buttons on all playlists; overwrites next option
var playlists = ["MyPlaylist1", "AnotherOne"]; //If option above == false then display buttons on those playlists only
var threshold = 90; //Delete if watched more or equal to % of video
var sleeptimer = 600; //Sleep interval for cleanse function (seems to need to be quite high for consistent results)
var sleeptimer_short = 50; //Sleep interval for Delete ALL function
// Config
const sleep = (timeout) => new Promise(res => setTimeout(res, timeout))
function CreateButton(){
var buttonDiv = document.createElement("div");
buttonDiv.id = "CleanseButton";
var cleanseButton = document.createElement("button");
cleanseButton.textContent = 'Cleanse';
//cleanseButton.appendChild(document.createTextNode("Cleanse"));
cleanseButton.style.width = "75px";
cleanseButton.style.height = "100%";
cleanseButton.style.backgroundColor = "#181717";
cleanseButton.style.color = "white";
cleanseButton.style.textAlign = "center";
cleanseButton.style.fontSize = "14px";
cleanseButton.style.border = "0";
cleanseButton.style.cursor = "pointer";
cleanseButton.style.borderRadius = "2px";
cleanseButton.style.fontFamily = "Roboto, Arial, sans-serif";
cleanseButton.style.borderRadius = "2px";
cleanseButton.style.marginLeft = "10px";
buttonDiv.appendChild(cleanseButton);
buttonDiv.addEventListener("click", function() {
playlist_cleanser();
});
var targetElement = document.querySelectorAll(".dropdown-trigger")[1];
insertAfter(targetElement, buttonDiv);
var buttonDivTwo = document.createElement("div");
buttonDivTwo.id = "CleanseButtonTwo";
var cleanseButtonTwo = document.createElement("button");
//cleanseButtonTwo.appendChild(document.createTextNode("All"));
cleanseButtonTwo.textContent = 'ALL';
cleanseButtonTwo.style.width = "75px";
cleanseButtonTwo.style.height = "100%";
cleanseButtonTwo.style.backgroundColor = "#ff0000";
cleanseButtonTwo.style.color = "white";
cleanseButtonTwo.style.textAlign = "center";
cleanseButtonTwo.style.fontSize = "14px";
cleanseButtonTwo.style.border = "0";
cleanseButtonTwo.style.cursor = "pointer";
cleanseButtonTwo.style.borderRadius = "2px";
cleanseButtonTwo.style.fontFamily = "Roboto, Arial, sans-serif";
cleanseButtonTwo.style.borderRadius = "2px";
cleanseButtonTwo.style.marginLeft = "10px";
buttonDivTwo.appendChild(cleanseButtonTwo);
buttonDivTwo.addEventListener("click", function() {
console.log("clicked");
delete_ALL();
});
insertAfter(buttonDiv, buttonDivTwo);
}
function insertAfter(referenceNode, newNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
async function delete_ALL() {
console.log("delete_ALL");
while(true){
var logger = '';
var playlist_title = document.querySelector('#display-dialog').querySelector('#text-displayed').innerHTML;
var elementList = document.querySelectorAll('#contents');
var playlist = elementList[1];
var real_playlist = playlist.querySelector('#contents');
//console.log(real_playlist);
var video_one = document.querySelector('#index-container');
//console.log(video_one);
if(!video_one) break;
var descendents = real_playlist.getElementsByTagName('*');
var deleter = true;
for(var i=0; i<descendents.length; i++){
var element = descendents[i];
if(deleter == true){
if (element.id == 'video-title'){
var namer = element.innerHTML;
var link = element.href.split('&list')[0]
console.log("Deleting: " + namer + " -> " + link);
}
if(element.id == 'button'){
await new Promise((resolve) => {
setTimeout(() => {
element.click();
resolve();
}, sleeptimer_short);
});
//sleep(sleeptimer_short);
var menu = document.querySelectorAll('#contentWrapper')[2];
var items = menu.getElementsByTagName('*');
for(var y=0; y<items.length; y++){
if(items[y].classList.contains("ytd-menu-popup-renderer")) var menu_deleteoption = items[y];
if(items[y].innerHTML == playlist_title){
await new Promise((resolve) => {
setTimeout(() => {
menu_deleteoption.click();
deleter = false;
resolve();
}, sleeptimer_short);
});
//sleep(sleeptimer_short);
}
if(deleter == false){
//console.log("break");
break;
}
}
}
}
if(deleter == false){
//console.log("break");
break;
}
}
}
}
async function playlist_cleanser() {
console.log("playlist_cleanser");
while(true){
console.log("--------------------");
var all_progress = document.querySelectorAll('#progress');
var continue_on = false;
var value_check;
for (var z = 0; z < all_progress.length; z++) {
value_check = all_progress[z].style.cssText.substring(0, all_progress[z].style.cssText.length - 2).split('width: ')[1];
//console.log(value_check);
if(value_check && value_check >= threshold){
continue_on = true;
break;
}
}
if(continue_on == false){
console.log("@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
console.log(" DONE CLEANSING");
console.log("@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
console.log("--------------------");
break;
}
console.log("Found videos in playlist that match threshold criteria");
console.log("--------------------");
var logger = '';
var playlist_title = document.querySelector('#display-dialog').querySelector('#text-displayed').innerHTML;
var elementList = document.querySelectorAll('#contents');
var playlist = elementList[1];
var real_playlist = playlist.querySelector('#contents');
var descendents = real_playlist.getElementsByTagName('*');
var deleter = false;
for(var i=0; i<descendents.length; i++){
var element = descendents[i];
if(element.id == 'progress'){
logger += "At least partly watched, checking if threshhold met";
//console.log("At least partly watched, checking if threshhold met");
var value = element.style.cssText.substring(0, element.style.cssText.length - 2).split('width: ')[1];
//console.log("Watched for " + value + "%");
logger += " | Watched for " + value + "%";
if(value >= threshold){
deleter = true;
logger += " | Threshhold met -> delete";
//console.log(logger);
logger = '';
//console.log("Threshhold met -> delete");
}
else{
deleter = false;
logger += " | Leave in watchlist";
//console.log(logger);
logger = '';
}
}
if(deleter == true){
if (element.id == 'video-title'){
var namer = element.innerHTML;
var link = element.href.split('&list')[0]
console.log("Deleting: " + namer + " -> " + link);
}
if(element.id == 'button'){
await new Promise((resolve) => {
setTimeout(() => {
element.click();
resolve();
}, sleeptimer);
});
sleep(sleeptimer);
var menu = document.querySelectorAll('#contentWrapper')[2];
var items = menu.getElementsByTagName('*');
for(var y=0; y<items.length; y++){
if(items[y].classList.contains("ytd-menu-popup-renderer")) var menu_deleteoption = items[y];
if(items[y].innerHTML == playlist_title){
await new Promise((resolve) => {
setTimeout(() => {
menu_deleteoption.click();
deleter = false;
resolve();
}, sleeptimer);
});
sleep(sleeptimer*2);
}
if(deleter == false){
//console.log("break");
break;
}
}
}
}
}
}
}
document.addEventListener("DOMContentLoaded", function(event) {
if(document.getElementById("polymer-app") || document.getElementById("masthead") || window.Polymer){
setInterval(function(){
if(window.location.href.indexOf("playlist?list=") < 0) return false;
var playlist_titler = document.querySelector('#display-dialog').querySelector('#text-displayed').innerHTML;
if(show_on_all == true){
if(document.getElementById("CleanseButton") === null) CreateButton();
}
else{
if(playlists.includes(playlist_titler)){
if(document.getElementById("CleanseButton") === null) CreateButton();
}
else return false;
}
}, 100);
}
});
@js6pak
Copy link

js6pak commented Jun 15, 2023

Cleaned it up and updated for latest YouTube version here: https://gist.github.com/js6pak/33bdefdefac09c387f55d08c5b9526fa

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