Skip to content

Instantly share code, notes, and snippets.

@qoomon
Last active August 24, 2026 15:04
Show Gist options
  • Select an option

  • Save qoomon/a3f71ff2b2a18ee297c9435d539d5247 to your computer and use it in GitHub Desktop.

Select an option

Save qoomon/a3f71ff2b2a18ee297c9435d539d5247 to your computer and use it in GitHub Desktop.
Clean YouTube Watch Later Videos
// Version 2.0.2
// This script will remove all videos from watch later list
//
// Usage
//
// #1 go to https://www.youtube.com/playlist?list=WL
// #2 run following script in your browser console
(async function() {
const playlistName = document.querySelector('.metadata-wrapper #container #text')?.textContent || document.querySelector('#text')?.textContent
if(!playlistName) {
alert(`Couldn't determine playlist name!`)
return
}
if(!confirm(`Are you sure to delete ALL videos from ${playlistName}?`)) {
return
}
console.info("start...")
while(true) {
const videos = document.querySelectorAll('#primary ytd-playlist-video-renderer')
if(videos.length == 0) break
for (let videoElement of videos) {
const videoTitle = videoElement.querySelector('a#video-title')
console.info(`Remove Video\n`
+ ` Title: ${videoTitle.innerText}\n`
+ ` URL: ${videoTitle.href}`)
const actionMenuButton = videoElement.querySelector('#menu #button')
console.debug("click actionMenuButton", actionMenuButton)
actionMenuButton.click()
const removeButton = await untilDefined(() => document.evaluate(
`//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 removeButton", removeButton)
console.log("click removeButton", removeButton)
// removeButton.click()
await sleep(200)
}
}
console.info("done!")
// === util functions ========================================================
async function sleep (timeout) {
return new Promise(res => setTimeout(res, timeout))
}
async function untilDefined(factory, checkInterval = 100) {
while (true) {
const value = factory()
if (value != null) return value
await sleep(checkInterval)
}
}
})();
@sutkovyi

sutkovyi commented Apr 3, 2025

Copy link
Copy Markdown

thank you

@lonerider543

lonerider543 commented Aug 20, 2026 •

Copy link
Copy Markdown

Thanks! The XPATH for the remove button no longer works as the text is not just the playlist name, but was able to amend that using Gemini and it worked great.

Below is what I changed it to. Maybe there is a better way, I am not fluent in XPATH and just used what Gemini gave me:

`//ytd-menu-service-item-renderer[.//yt-formatted-string[contains(text(), "Remove")]] | //tp-yt-paper-item[contains(., "Remove")]`

@qoomon

qoomon commented Aug 24, 2026

Copy link
Copy Markdown
Author

@lonerider543 thanks for mentioning I fixed it without the need of any language specific string again.

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