Skip to content

Instantly share code, notes, and snippets.

@spyesx
Last active August 8, 2026 12:52
Show Gist options
  • Select an option

  • Save spyesx/27cecbe9483c507b9b098d942e98d62b to your computer and use it in GitHub Desktop.

Select an option

Save spyesx/27cecbe9483c507b9b098d942e98d62b to your computer and use it in GitHub Desktop.
Get URLs of your youtube watch later playlist
// Execute this in the console of on your own playlist
var videos = document.querySelectorAll('.yt-simple-endpoint.style-scope.ytd-playlist-video-renderer');
var r = [];
var json = [];
r.forEach.call(videos, function(video) {
var url = 'https://www.youtube.com' + video.getAttribute('href');
url = url.split('&list=WL&index=');
url = url[0];
json.push(url);
});
console.log(json)
## As @jwillmer said in the comment, we want to use bash, not sh
# chmod 744 youtube_watch_later.sh
# ./youtube_watch_later.sh
#!/bin/bash
declare -a videos=(
"https://www.youtube.com/watch?v=5dsGWM5XGdg"
"https://www.youtube.com/watch?v=2J5GzHoKl1Q"
)
for i in "${videos[@]}"
do
youtube-dl -i -c --write-thumbnail -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best' "$i"
done
@jwillmer

jwillmer commented Nov 19, 2020 •

Copy link
Copy Markdown

watch_later.sh: 3: watch_later.sh: Syntax error: "(" unexpected

Solution from StackExchange:

When you use ./scriptname.sh it executes with /bin/bash as in the first line with #!. But when you use sh scriptname.sh it executes sh, not bash.

@spyesx

spyesx commented Nov 20, 2020

Copy link
Copy Markdown
Author

Thx @jwillmer. I'm on MacOS I didn't face this issue. Anyway, now it's in a comment just in case.

@neon-dev

neon-dev commented Jun 8, 2023

Copy link
Copy Markdown

Thanks for this. I've simplified the JS to a oneliner that works on any playlist and outputs a block of text matching the expected format of your shell script variable:

console.log([...document.querySelectorAll('a.ytd-playlist-video-renderer')].map(e => '"' + e.href.split('&list=')[0] + '"').join('\n'))

@mckubisiak

mckubisiak commented Jun 28, 2023 •

Copy link
Copy Markdown

I've added title prior to URL

Original

var videos = document.querySelectorAll('.yt-simple-endpoint.style-scope.ytd-playlist-video-renderer');
var r = [];
var json = [];

r.forEach.call(videos, function(video) {
	var url = video.getAttribute('title') + "   " + 'https://www.youtube.com' + video.getAttribute('href') ;
	url = url.split('&list=WL&index=');
	url = url[0];
	json.push(url);
});
console.log(json)

Oneliner

console.log([...document.querySelectorAll('a.ytd-playlist-video-renderer')].map(e => e.title + '  ' + e.href.split('&list=')[0]  ).join('\n'))

@nielsbom

Copy link
Copy Markdown

I got duplicate URLs from the last script. I also didn't want to see the title, so here's my version.
(I added a block so it's easier to run multiple times)

{
  let videos = document.querySelectorAll('.yt-simple-endpoint.style-scope.ytd-playlist-video-renderer');
  let r = [];
  let result = new Set();

  r.forEach.call(videos, function (video) {
    let url = 'https://www.youtube.com' + video.getAttribute('href');
    url = url.split('&list=WL&index=')[0];
    result.add(url)
  });
  console.log(Array.from(result))
}

@zi3dAouidene

Copy link
Copy Markdown

I change it to a set of objects, each objects contains the title and url separately rather than just one string

  let videos = document.querySelectorAll('.yt-simple-endpoint.style-scope.ytd-playlist-video-renderer');
  let r = [];
  let result = new Set();

  r.forEach.call(videos, function (video) {
    let title = video.getAttribute('title') 
    let url = 'https://www.youtube.com' + video.getAttribute('href')
    url = url.split('&list=WL&index=')[0];

    result.add({title:title, url:url})

  });
  console.log(Array.from(result))

@voyager2saturn

Copy link
Copy Markdown

Will it also take unavailable video?

eg. a video not taken down but geo-blocked so the name will be visible but the playlist toggle has to be set.

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