This is a very hacky solution to copy Liked songs to a playlist since YTM still doesn't have the functionality. I'm using this to copy songs out of YTM to another service, then unsubscribing. Thus, I won't be maintaining it (or ever using it again). It will only work while the YTM interface is the same as it is today (3/6/21) and will break once they make updates.
Steps to use:
- Create a new playlist
- Go to your Likes page (in chrome, on a desktop or laptop). Scroll to the bottom so all songs are loaded
- Open Chrome's dev tools (F12 on windows), go to the console
- Paste the script below. Edit the first line, replace "YOUR_PLAYLIST_NAME" with your playlist's name
- Press enter
If it's working, you will see it log out the song titles and (1 of x).
let TARGET_PLAYLIST = "YOUR_PLAYLIST_NAME";
let songElements = document.querySelectorAll(
"#contents > .ytmusic-playlist-shelf-renderer"
);
let addToPlaylistXpath = "//yt-formatted-string[text()='Add to playlist']";
let playlistXpath = `//yt-formatted-string[text()='${TARGET_PLAYLIST}']`;
function isHidden(element) {
return element.offsetParent === null;
}
function waitForElementToDisplay(xpath, callback) {
const timeout = 5000
const checkFrequency = 30
var startTimeInMs = Date.now();
(function loopSearch() {
const element = document.evaluate(
xpath,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
if (element && !isHidden(element)) {
callback(element);
return;
} else {
setTimeout(function () {
if (timeout && Date.now() - startTimeInMs > timeout) return;
loopSearch();
}, checkFrequency);
}
})();
}
function copySong(songElement, index) {
const dotsElement = songElement.querySelector("#button");
const songTitle = songElement.querySelector(".yt-simple-endpoint").textContent;
console.log(`Copying: ${songTitle} (${index} of ${songElements.length})`);
// click dot action menu
dotsElement.click();
waitForElementToDisplay(addToPlaylistXpath, (addToPlaylistElement) => {
// click "Add to playlist"
addToPlaylistElement.click();
waitForElementToDisplay(playlistXpath, (playlistElement) => {
// click target playlist
playlistElement.click();
document.body.click();
// call function again
if (index <= songElements.length) {
const nextIndex = index + 1;
copySong(songElements[nextIndex], nextIndex);
}
});
});
}
copySong(songElements[0], 0)