Metapyziks / Export 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();
});