(Atualizado em 2016-05-09, mais robusto do que a principal resposta atual)
Se você só precisa salvar algumas listas de reprodução, basta usar meu snippet Javascript abaixo. Esse snippet pode salvar todas as listas conforme mostrado na página da web, e também funciona para todas as visualizações de biblioteca de músicas / álbuns / artistas. Eu listei duas outras alternativas no final desta resposta.
Acesse: https://play.google.com/music/listen#/all (ou sua playlist)
Abra um console do desenvolvedor (F12 para Chrome). Cole o código abaixo no console.
Todas as músicas allsongs
copiadas são armazenadas no objeto e uma versão em texto da lista é copiada para a área de transferência. Eu recomendo executar
songsToText("all",true)
depois para obter as informações completas sobre CSV. Execute copy(outText)
manualmente se a cópia da área de transferência não funcionou na primeira tentativa.
Código (versão mais recente em 10 de maio de 2016, Rev. 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);
Código mais recente no Github (Gist) aqui: https://gist.github.com/jmiserez/c9a9a0f41e867e5ebb75
Se você deseja obter a saída em formato de texto, pode chamar a função songsToText (). Você pode selecionar um estilo, escolher o formato e se apenas as músicas curtidas / curtidas devem ser exportadas. A lista resultante será colada na área de transferência. Os estilos são all
, artist
, artistalbum
, artistsong
,
artistalbumsong
. O CSV resultará em um arquivo CSV e pode ser deixado de fora (o padrão é falso). O Likedonly pode ser deixado de fora (o padrão é false) ou definido como true, e filtrará todas as músicas com classificações maiores ou iguais a 5. Por exemplo:
songsToText("all",true,false)
exportará todas as músicas no formato csv.
songsToText("all",true,true)
exportará apenas músicas curtidas no formato csv.
songsToText("artistsong",false,false)
exportará todas as músicas como texto.
Você pode colar os dados em qualquer lugar que desejar, por exemplo, http://www.ivyishere.org/, se desejar adicionar as músicas ou álbuns à sua conta do Spotify. Para fazer Ivy reconhecer álbuns completos, use o estilo "album de artista". Para músicas, use o estilo "artistsong".
Sobre o trecho:
é baseado na resposta original de Michael Smith, mas é um pouco mais robusto. Fiz as seguintes melhorias:
Funciona em listas de reprodução e na biblioteca. Todas as colunas ausentes são ignoradas e a ordem é calculada, por isso deve funcionar em quase todas as listas de músicas do Google Music.
Ele para quando atinge o fundo (detecta a posição de rolagem) ou após o tempo limite especificado. O tempo limite está aí para evitar um loop sem fim, caso o código de detecção de rolagem esteja desativado em alguns pixels.
É muito mais rápido (intervalo a cada 1 ms), mas aguarda se os dados não estiverem prontos (até o tempo limite especificado, atualmente 3s).
Deduplicação durante a operação e na saída.
Reúne classificações: "indefinido" nunca é classificado, "0" não é classificado (ou seja, uma vez classificado, mas removido), "1" apresenta o polegar para baixo e "5" é o polegar para cima (curtido).
Além das melhorias básicas, ele também formata bem o texto e o copia para a área de transferência. Você também pode obter os dados como CSV, se desejar, executando a songsToText
função uma segunda vez.
Alternativas:
Se você precisar de uma API Python, confira o projeto não oficial da API do Google Music .
Se você possui várias listas de reprodução e deseja exportar todas de uma só vez, experimente o exportador de listas de músicas gmusic-scripts que pode fazer isso (Python, usa o projeto não oficial da API).