Skip to content

Instantly share code, notes, and snippets.

@Metapyziks
Created December 28, 2014 02:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Metapyziks/f55ccb445ea6f21c2f0d to your computer and use it in GitHub Desktop.
Save Metapyziks/f55ccb445ea6f21c2f0d to your computer and use it in GitHub Desktop.
Adds a button to export a Google Music playlist.
// ==UserScript==
// @name Export Google Music Playlist
// @namespace http://ziks.net/
// @version 0.1
// @description Adds a button to export a Google Music playlist
// @author Ziks
// @match https://play.google.com/music/listen
// @require http://code.jquery.com/jquery-latest.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js
// @grant none
// ==/UserScript==
var pollingPlaylist = false;
function startPollingPlaylist() {
if (!window.location.hash.match(/#\/pl\//g)) return;
if (pollingPlaylist) return;
pollingPlaylist = true;
pollPlaylist();
}
function stopPollingPlaylist() {
if (!pollingPlaylist) return;
pollingPlaylist = false;
}
var scraping = false;
var toScrape = 0;
var scrapedInfo = null;
function startScraping() {
if (!window.location.hash.match(/#\/pl\//g)) return;
if (scraping) return;
scraping = true;
scrapedInfo = [];
$("#music-content")[0].scrollTop = 0;
setTimeout(scrapeInfo, 17);
}
function stopScraping() {
scraping = false;
window.open(exportJSON(), 'Exported Playlist');
}
function exportJSON() {
return "data:application/json," + encodeURIComponent(JSON.stringify(scrapedInfo));
}
function scrapeInfo() {
if (!scraping) return;
if (scrapedInfo.length >= toScrape) {
stopScraping();
return;
}
$("tr.song-row").each(function() {
var index = parseInt($(this).find('td[data-col="index"]').text());
var lengthStr = $(this).find('td[data-col="duration"]').text();
var matches = (/(?:([0-9]+):)?([0-9]+):([0-9]+)/g).exec(lengthStr);
var length = (parseInt(matches[1]) || 0) * 60 * 60 + parseInt(matches[2]) * 60 + parseInt(matches[3]);
scrapedInfo.push({
index: index,
title: $(this).find('td[data-col="title"]').text(),
length: length,
artist: $(this).find('td[data-col="artist"]').text(),
album: $(this).find('td[data-col="album"]').text()
});
});
$("#music-content")[0].scrollTop = $("tr.song-row").height() * scrapedInfo.length;
setTimeout(scrapeInfo, 17);
}
function pollPlaylist() {
if (!pollingPlaylist) return;
var songCountText = $(".song-count").text();
var regex = /([0-9]+)\s+SONGS/g;
var match = regex.exec(songCountText);
if (!match) {
setTimeout(pollPlaylist, 250);
return;
}
toScrape = parseInt(match[1]);
stopPollingPlaylist();
var orig = $(".goog-menuitem:has(.goog-menuitem-content:contains(Export playlist))");
if (orig.length > 0) {
return;
}
var editButton = $(".goog-menuitem:has(.goog-menuitem-content:contains(Edit playlist))");
var exportButton = $('<div class="goog-menuitem" role="menuitem" style="-webkit-user-select: none;" aria-hidden="false">'
+ '<div class="goog-menuitem-content" style="-webkit-user-select: none;">Export playlist</div>'
+ '</div>').insertAfter(editButton);
exportButton.hover(function() {
exportButton.attr("class", "goog-menuitem goog-menuitem-highlight");
}, function() {
exportButton.attr("class", "goog-menuitem");
});
exportButton.click(startScraping);
}
$(function() {
startPollingPlaylist();
});
$(window).on('hashchange', function() {
startPollingPlaylist();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment