-
-
Save lesthack/875c9682d0f2d0a6c107eb33201c7a52 to your computer and use it in GitHub Desktop.
Export your Google Music Library and Playlists (Google Play Music All Access) (see http://webapps.stackexchange.com/questions/50311/print-playlist-from-google-play-music for more)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Jeremie Miserez <[email protected]>, 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: http://webapps.stackexchange.com/questions/50311/print-playlist-from-google-play-music | |
// | |
// 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 + "\t"; | |
curr += allsongs[i].album + "\t"; | |
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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment