(Mis à jour le 2016-05-09, plus robuste que la réponse principale actuelle)
Si vous avez juste besoin de sauvegarder quelques playlists, vous pouvez simplement utiliser mon extrait de code Javascript ci-dessous. Cet extrait de code peut enregistrer toutes les listes telles qu'elles apparaissent sur la page Web. Il fonctionne donc également pour toutes les vues de bibliothèque de chansons / albums / artistes. J'ai énuméré deux autres alternatives à la fin de cette réponse.
Accédez à la page https://play.google.com/music/listen#/all (ou à votre playlist).
Ouvrez une console de développeur (F12 pour Chrome). Collez le code ci-dessous dans la console.
Toutes les chansons grattées sont stockées dans l' allsongs
objet et une version texte de la liste est copiée dans le Presse-papiers. Je recommande de courir
songsToText("all",true)
après pour obtenir toutes les informations CSV. Exécuter copy(outText)
manuellement si la copie du presse-papiers ne fonctionnait pas du premier coup.
Code (dernière version le 10 mai 2016, rév. 30):
var allsongs = []
var outText = "";
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");
}
outText = "";
if (csv) {
if (style == "all") {
//extra line
outText = "artist,album,title,duration,playcount,rating,rating_interpretation" + "\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 = "";
var properTitle = allsongs[i].title.replace(/[\n\r!]/g, '').trim();
if (!likedonly || (likedonly && allsongs[i].rating >= 5)){
if (csv) {
if (style == "all") {
//extra line
curr += '"' + allsongs[i].artist.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].album.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + properTitle.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].duration.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].playcount.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].rating.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].rating_interpretation.replace(/"/g, '""').trim() + '"';
} else if (style == "artist") {
curr += '"' + allsongs[i].artist.replace(/"/g, '""').trim() + '"';
} else if (style == "artistsong") {
curr += '"' + allsongs[i].artist.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + properTitle.replace(/"/g, '""').trim() + '"';
} else if (style == "artistalbum") {
curr += '"' + allsongs[i].artist.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].album.replace(/"/g, '""').trim() + '"';
} else if (style == "artistalbumsong") {
curr += '"' + allsongs[i].artist.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + allsongs[i].album.replace(/"/g, '""').trim() + '"' + ",";
curr += '"' + properTitle.replace(/"/g, '""').trim() + '"';
} else {
console.log("style not defined");
}
} else {
if (style == "all"){
curr = allsongs[i].artist + " - " + allsongs[i].album + " - " + properTitle + " [[playcount: " + allsongs[i].playcount + ", rating: " + allsongs[i].rating_interpretation + "]]" ;
} 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 + " - " + properTitle;
} else if (style == "artistalbumsong"){
curr = allsongs[i].artist + " - " + allsongs[i].album + " - " + properTitle;
} 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);
}
}
}
console.log("=============================================================");
console.log(outText);
console.log("=============================================================");
try {
copy(outText);
console.log("copy(outText) to clipboard succeeded.");
} catch (e) {
console.log(e);
console.log("copy(outText) to clipboard failed, please type copy(outText) on the console or copy the log output above.");
}
console.log("Done! " + numEntries + " lines in output. Used " + numEntries + " unique entries 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 = "";
document.querySelector("#mainContainer").scrollTop = 0; //scroll to top
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") == "play-count" ? 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("#mainContainer");
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("artistalbumsong", 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].getAttribute("data-rating") : ""),
rating_interpretation: "",
}
if(curr.rating == "undefined") {
curr.rating_interpretation = "never-rated"
}
if(curr.rating == "0") {
curr.rating_interpretation = "not-rated"
}
if(curr.rating == "1") {
curr.rating_interpretation = "thumbs-down"
}
if(curr.rating == "5") {
curr.rating_interpretation = "thumbs-up"
}
if (!seen.hasOwnProperty(curr.dataid)){ // hashset
total.push(curr);
seen[curr.dataid] = 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);
Le dernier code sur Github (Gist) ici: https://gist.github.com/jmiserez/c9a9a0f41e867e5ebb75
Si vous souhaitez que la sortie soit au format texte, vous pouvez appeler la fonction songsToText (). Vous pouvez sélectionner un style, choisir le format et, si seuls les morceaux appréciés / optimisés doivent être exportés. La liste résultante sera ensuite collée dans le presse-papier. Les styles sont all
, artist
, artistalbum
, artistsong
,
artistalbumsong
. CSV donnera un fichier CSV et peut être omis (faux par défaut). Likedonly peut être omis (par défaut, false) ou défini sur true, et filtrera toutes les chansons ayant une note supérieure ou égale à 5. Par exemple:
songsToText("all",true,false)
exportera toutes les chansons au format csv.
songsToText("all",true,true)
exportera uniquement les chansons préférées au format csv.
songsToText("artistsong",false,false)
exportera toutes les chansons sous forme de texte.
Vous pouvez ensuite coller les données où bon vous semble, par exemple http://www.ivyishere.org/ si vous souhaitez ajouter les chansons ou les albums à votre compte Spotify. Pour que Ivy reconnaisse les albums complets, utilisez le style "artistalbum". Pour les chansons, utilisez le style "artistes".
À propos de l'extrait:
Ceci est basé sur la réponse originale de Michael Smith, mais est un peu plus robuste. J'ai apporté les améliorations suivantes:
Fonctionne sur les playlists ainsi que sur la bibliothèque. Toutes les colonnes manquantes sont ignorées et l'ordre est déterminé. Il devrait donc fonctionner sur presque toutes les listes de chansons de Google Music.
Il s'arrête lorsqu'il atteint le bas (détecte la position du défilement) ou après le délai spécifié. Le délai d'attente est là pour empêcher une boucle sans fin au cas où le code de détection de défilement serait désactivé de quelques pixels.
Il est beaucoup plus rapide (intervalle toutes les 1 ms), mais attend si les données ne sont pas prêtes (jusqu’à l’expiration du délai spécifié, actuellement de 3 secondes).
Effectue la déduplication en cours de fonctionnement et sur la sortie.
Gathers ratings: "undefined" n'est jamais noté, "0" n'est pas noté (c.-à-d. Une fois noté, puis supprimé), "1" correspond au pouce vers le bas et "5" au pouce levé.
En plus des améliorations de base, il formate également le texte et le copie dans le presse-papiers. Vous pouvez également obtenir les données au format CSV si vous le souhaitez, en exécutant la songsToText
fonction une seconde fois.
Alternatives:
Si vous avez besoin d'une API Python, consultez le projet non officiel d'API Google Music .
Si vous avez des tonnes de listes de lecture et que vous souhaitez toutes les exporter en une seule fois, essayez l' exportateur de listes de lecture gmusic-scripts qui peut le faire (Python utilise le projet d'API non officiel).