I do not recommend playing music from the same tab that you're doing this process in as some of the time it seemed to cause problems for me.
Instructions:
- Go to your Playlists page.
- Press F12 to open your console.
- Paste in the JavaScript code below into your console.
- Surprisingly important: Close your console. I've found that YouTube Music works wayyy slower with the console open for some reason.
- Click on a playlist that you want to save to text.
- If the playlist is longer than 100 songs, scroll to the bottom. Continue until the last batch is loaded (YouTube Music seems to load batches of 100 songs, so if there are 317 songs in your playlist you'll need to scroll to the bottom and wait for it to load 3 times).
- Once all songs have loaded, navigate back to the playlists page (same as in step 1.) using the menu or your browsers back button.
- Repeat steps 5-7 for all playlists you want to save to text.
- Once you've done this for all the playlists you want to save to text, open your console (F12) and either type
JSON.stringify(tracklistObj, null, '\t')
(change the '\t'
to ' '
if you want minimal indentation) or tracklistObj
if you just want the JavaScript object to manipulate it your own way. If you want it sorted, run the command Object.values(tracklistObj).forEach(a => a.sort())
before calling the JSON.stringify
command.
Be careful to not refresh the page before you've completed all that you want to do or else you'll have to restart from step 1.
// Setup
var tracklistObj = {},
currentPlaylist,
checkIntervalTime = 100,
lastTime;
function processString(str) {
str = str.trim();
if (!stripFileExtension) return str;
return str.replaceAll(".mp3", "");
}
function convertDurationtoSeconds(duration) {
const hasHours = duration.split(':').length - 1 === 2;
if (hasHours) {
const [hours, minutes, seconds] = duration.split(':');
return 360 * Number(hours) + 60 + Number(minutes) + Number(seconds);
} else {
const [minutes, seconds] = duration.split(':');
return 60 * Number(minutes) + Number(seconds);
}
};
// Process the visible tracks
function getVisibleTracks() {
var playlistTrackContainers = document.querySelectorAll('#header + #contents > ytmusic-playlist-shelf-renderer ytmusic-responsive-list-item-renderer');
for(var i = 0; i < playlistTrackContainers.length ; i++) {
var trackContainer = playlistTrackContainers[i];
var title = trackContainer.querySelector('.title-column');
if(title !== null) {
title = processString(title.textContent);
} else return;
var artist = trackContainer.querySelector('.secondary-flex-columns > yt-formatted-string:first-child');
if(artist !== null) {
artist = processString(artist.textContent);
} else return;
var duration = trackContainer.children[trackContainer.children.length - 1];
if(duration !== null) {
duration = processString(duration.textContent);
if (durationInSeconds) {
duration = convertDurationtoSeconds(duration);
}
}
var album = trackContainer.querySelector('.secondary-flex-columns > yt-formatted-string:nth-child(2)');
if(album !== null)
album = processString(album.textContent);
// Add it if it doesn't exist already
var key = artist + " - " + title;
if(tracklistObj[currentPlaylist] && !tracklistObj[currentPlaylist].includes(key)) {
tracklistObj[currentPlaylist].push(key);
if(printTracksToConsole) {
console.log(key);
}
}
}
}
// Listen for page changes
window.history.pushState = new Proxy(window.history.pushState, {
apply: (target, thisArg, argArray) => {
currentPlaylist = null;
var doneLoading = setInterval(function() {
var playListName = document.querySelector('ytmusic-responsive-header-renderer .title');
if(playListName != null) {
currentPlaylist = playListName.innerText;
if(tracklistObj[currentPlaylist] === undefined) {
tracklistObj[currentPlaylist] = [];
}
console.log("===================================");
console.log("Adding to playlist " + currentPlaylist);
getVisibleTracks();
clearInterval(doneLoading);
}
}, 100);
return target.apply(thisArg, argArray);
},
});
// Check for new tracks every so often
setInterval(function() {
getVisibleTracks();
}, checkIntervalTime);
// Whether or not to print the tracks obtained to the console
var printTracksToConsole = false;
var stripFileExtension = true;
var durationInSeconds = false;
You can also print out track names to the console as you go by changing printTracksToConsole
to true
(you should do this between steps 2 and 3).
By default this script will strip any file format off of the end of the track names. Set stripFileExtension
to false
if you don't want to do this.
You can also convert the duration to seconds by setting durationInSeconds
to true
.
Note that currently it's setup only to give Artist - Track name
, but you can easily edit the line that has tracklistObj[currentPlaylist].push(artist + " - " + title);
with album
or duration
, and/or whatever formatting you want (including CSV format if you so please). Do this before step 3.
Example output (all YouTube Music playlists I currently have) with default settings. It took about 5 minutes in total to navigate to each of the 52 playlists, scroll down them, and then convert the result to text.
Feel free to modify the above script. For example, to output a .m3u
file,
I changed key
to:
var key = '#EXTINF:' + duration + ', ' + artist + ' - ' + title + '\n' + String.raw`E:\music` + '\\' + title + '.mp3\n\n';
Then set durationInSeconds
to true
, then did console.log(tracklistObj['My Playlist'].join(''))
. I copy-pasted this into a new file and put #EXTM3U
at the top, then saved as My Playlist.m3u
! Make sure to change the file path in the key
variable to the path that you need.