// Jeremie Miserez , 2015 // // A little bit of Javascript to let you export your Google Music library, playlists, and album track lists :) // // I posted this as an answer here: // // 1. Go to: https://play.google.com/music/listen#/all (or your playlist) // // 2. Open a developer console (F12 for Chrome). Paste // code below into the console. // // 3. All scraped songs are stored in the `allsongs` object // and a text version of the list is copied to the clipboard. I recommend running // `songsToText("all",true)` afterwards to get the full CSV information. // // 4. If you would like the output in a text format, can call // the songsToText() function. You can select a style, choose // the format, and if only liked/thumbed up songs should be exported. // The resulting list will then be pasted into the clipboard. // Styles are ` all`, `artist`, `artistalbum`, `artistsong`, // `artistalbumsong`. // CSV will result in a CSV file and can be left out (defaults to false). // Likedonly can be left out (defaults to // false) or set to true, and will filter all songs with // ratings greater or equal to 5. // E.g: // - `songsToText("all",true,false)` will export all songs in csv format. // - `songsToText("all",true,true)` will export only liked songs in csv format. // - `songsToText("artistsong",false,false)` will export all songs as text. // // 5. You can then paste the data anywhere you like, for // example http://www.ivyishere.org/ if you want to add the // songs or albums to your Spotify account. To make Ivy // recognize full albums, use the "artistalbum" style. For // songs, use the "artistsong" style. // see my answer here for questions: http://webapps.stackexchange.com/a/73791/77056 var allsongs = [] var songsToText = function(style, csv, likedonly){ if (style === undefined){ console.log("style is undefined."); return; } var csv = csv || false; // defaults to false var likedonly = likedonly || false; // defaults to false if (likedonly) { console.log("Only selecting liked songs"); } if (style == "all" && !csv){ console.log("Duration, ratings, and playcount will only be exported with the CSV flag"); } var outText = ""; if (csv) { if (style == "all") { //extra line outText = "index,artist,album,title,duration,playcount,rating" + "\n"; } else if (style == "artist") { } else if (style == "artistsong") { } else if (style == "artistalbum") { } else if (style == "artistalbumsong") { } else { console.log("style not defined"); } } var numEntries = 0; var seen = {}; for (var i = 0; i < allsongs.length; i++) { var curr = ""; if (!likedonly || (likedonly && allsongs[i].rating >= 5)){ if (csv) { if (style == "all") { //extra line curr = allsongs[i].artist + ","; curr += allsongs[i].album + ","; curr += allsongs[i].title + ","; curr += allsongs[i].duration + ","; curr += allsongs[i].index + ","; curr += allsongs[i].playcount + ","; curr += allsongs[i].rating; } else if (style == "artist") { curr = allsongs[i].artist; } else if (style == "artistsong") { curr = allsongs[i].artist + ","; curr += allsongs[i].title; } else if (style == "artistalbum") { curr = allsongs[i].artist + ","; curr += allsongs[i].album; } else if (style == "artistalbumsong") { curr = allsongs[i].artist + ","; curr += allsongs[i].album + ","; curr += allsongs[i].title; } else { console.log("style not defined"); } } else { if (style == "all"){ curr = ""; curr += (allsongs[i].index.length > 0 ? allsongs[i].index + ". " : ""); curr += allsongs[i].artist + " - "; curr += allsongs[i].album + " - "; curr += allsongs[i].title; } else if (style == "artist"){ curr = allsongs[i].artist; } else if (style == "artistalbum"){ curr = allsongs[i].artist + " - " + allsongs[i].album; } else if (style == "artistsong"){ curr = allsongs[i].artist + " - " + allsongs[i].title; } else if (style == "artistalbumsong"){ curr = allsongs[i].artist + " - " + allsongs[i].album + " - " + allsongs[i].title; } else { console.log("style not defined"); } } if (!seen.hasOwnProperty(curr)){ // hashset outText = outText + curr + "\n"; numEntries++; seen[curr] = true; } else { console.log("Skipping (duplicate) " + curr); } } } copy(outText); console.log("Done! " + numEntries + " lines copied to clipboard. Used " + numEntries + " songs out of " + allsongs.length + "."); }; var scrapeSongs = function(){ var intervalms = 1; //in ms var timeoutms = 3000; //in ms var retries = timeoutms / intervalms; var total = []; var seen = {}; var topId = ""; var interval = setInterval(function(){ var songs = document.querySelectorAll("table.song-table tbody tr.song-row"); if (songs.length > 0) { // detect order var colNames = { index: -1, title: -1, duration: -1, artist: -1, album: -1, playcount: -1, rating: -1 }; for (var i = 0; i < songs[0].childNodes.length; i++) { colNames.index = songs[0].childNodes[i].getAttribute("data-col") == "index" ? i : colNames.index; colNames.title = songs[0].childNodes[i].getAttribute("data-col") == "title" ? i : colNames.title; colNames.duration = songs[0].childNodes[i].getAttribute("data-col") == "duration" ? i : colNames.duration; colNames.artist = songs[0].childNodes[i].getAttribute("data-col") == "artist" ? i : colNames.artist; colNames.album = songs[0].childNodes[i].getAttribute("data-col") == "album" ? i : colNames.album; colNames.playcount = songs[0].childNodes[i].getAttribute("data-col") == "playcount" ? i : colNames.playcount; colNames.rating = songs[0].childNodes[i].getAttribute("data-col") == "rating" ? i : colNames.rating; } // check if page has updated/scrolled var currId = songs[0].getAttribute("data-id"); if (currId == topId){ // page has not yet changed retries--; scrollDiv = document.querySelector("div#music-content"); isAtBottom = scrollDiv.scrollTop == (scrollDiv.scrollHeight - scrollDiv.offsetHeight) if (isAtBottom || retries <= 0) { clearInterval(interval); //done allsongs = total; console.log("Got " + total.length + " songs and stored them in the allsongs variable."); console.log("Calling songsToText with style all, csv flag true, likedonly false: songsToText(\"all\", false)."); songsToText("all", false, false); } } else { retries = timeoutms / intervalms; topId = currId; // read page for (var i = 0; i < songs.length; i++) { var curr = { dataid: songs[i].getAttribute("data-id"), index: (colNames.index != -1 ? songs[i].childNodes[colNames.index].textContent : ""), title: (colNames.title != -1 ? songs[i].childNodes[colNames.title].textContent : ""), duration: (colNames.duration != -1 ? songs[i].childNodes[colNames.duration].textContent : ""), artist: (colNames.artist != -1 ? songs[i].childNodes[colNames.artist].textContent : ""), album: (colNames.album != -1 ? songs[i].childNodes[colNames.album].textContent : ""), playcount: (colNames.playcount != -1 ? songs[i].childNodes[colNames.playcount].textContent : ""), rating: (colNames.rating != -1 ? songs[i].childNodes[colNames.rating].textContent : ""), } if (!seen.hasOwnProperty(curr.dataid)){ // hashset total.push(curr); seen[curr.id] = true; } } songs[songs.length-1].scrollIntoView(true); // go to next page } } }, intervalms); }; scrapeSongs(); // for the full CSV version you can now call songsToText("all", true);