Greasy Fork is available in English.

Stig's Last.fm Album Linkr

Adding album links and headers to tracks on Last.Fm's recent plays listings

2016-07-08 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

// ==UserScript==
// @name        Stig's Last.fm Album Linkr
// @namespace   dk.rockland.userscript.lastfm.linkr
// @description Adding album links and headers to tracks on Last.Fm's recent plays listings
// @version     2016.07.08.1
// @author      Stig Nygaard, http://www.rockland.dk
// @homepageURL http://www.rockland.dk/userscript/lastfm/linkr/
// @supportURL  http://www.rockland.dk/userscript/lastfm/linkr/
// @match       *://*.last.fm/*
// @match       *://*.lastfm.de/*
// @match       *://*.lastfm.es/*
// @match       *://*.lastfm.fr/*
// @match       *://*.lastfm.it/*
// @match       *://*.lastfm.ja/*
// @match       *://*.lastfm.pl/*
// @match       *://*.lastfm.pt/*
// @match       *://*.lastfm.ru/*
// @match       *://*.lastfm.sv/*
// @match       *://*.lastfm.tr/*
// @match       *://*.lastfm.zh/*
// @grant       none
// @noframes
// ==/UserScript==


var linkr = linkr || {
    // CHANGELOG - The most important updates/versions:
    changelog: [
        {version: '2016.07.08.1', description: 'Fine-tuning album-headers with light red background color and more...'},
        {version: '2016.07.08.0', description: 'Don\'t put album-header at very top of Recent Tracks if the 1st track is scrobbling now.'},
        {version: '2016.07.07.0', description: 'Album "headers" in play listings added.'},
        {version: '2016.07.04.0', description: '1st release'}
    ],
    INFO: true,
    DEBUG: false,
    observed: null,
    linking_running: false,
    log: function(s, info) {
        if ((info && window.console) || (linkr.DEBUG && window.console)) {
            window.console.log('*Linkr* '+s);
        }
    },
    insertStyle: function() {
        if (!document.getElementById('linkrStyle')) {
            var style = document.createElement('style');
            style.type = 'text/css';
            style.id = 'linkrStyle';
            style.innerHTML = 'tr.albumlink-row,  tr.albumlink-row > td {background-color:#f1cccc !important;} tr.albumlink-row:hover,  tr.albumlink-row:hover > td {background-color:#f9d4d4 !important;}';
            document.getElementsByTagName('head')[0].appendChild(style);
            linkr.log('linkrStyle has been ADDED');
        } else {
            linkr.log('linkrStyle was already present');
        }
    },
    linking: function (mutations) {
        if(linkr.linking_running) return;
        linkr.linking_running = true;
        function altvalue(elem) {
            if (elem && elem.firstElementChild) {
                if (elem.firstElementChild.classList.contains('albumlink-row')) {
                    return null;
                } else if (elem.firstElementChild.firstElementChild && elem.firstElementChild.firstElementChild.firstElementChild && elem.firstElementChild.firstElementChild.firstElementChild.firstElementChild) {
                    return elem.firstElementChild.firstElementChild.firstElementChild.firstElementChild.alt;
                }
            }
            return null;
        }
        linkr.log('Running linking()... ', linkr.INFO);
        var l = document.querySelectorAll('table.chartlist tr div > img');
        for (var i=0; i < l.length; i++) {
            linkr.log('iteration '+ i + ' of ' + l.length);
            if (l[i].alt && l[i].alt!='') {
                l[i].title = l[i].alt;
                var tr = parent = l[i].parentNode;
                while (tr.tagName.toUpperCase()!=='TR') tr = tr.parentNode;
                var a = tr.querySelector('span.chartlist-artists a');
                if (a) {
                    linkr.log('Found img.alt='+l[i].alt);
                    var albumlink = a.href + '/' + encodeURIComponent(l[i].alt).replace(/%20/g,'+') + '/';
                    linkr.log('giving albumlink='+albumlink);
                    var link = document.createElement('a');
                    link.setAttribute('href', albumlink);
                    parent.replaceChild(link, l[i]);
                    link.appendChild(l[i]);
                } else {
                    linkr.log('Artist link not found');
                }
            }
        }

        var tlists = document.querySelectorAll('section#recent-tracks-section table.chartlist tbody, section.tracklist-section tbody');
        linkr.log('tlists.length='+tlists.length);
        for (var j=0; j<tlists.length; j++) {
            linkr.log('Loop with tlists['+j+'].');
            if (tlists[j] && tlists[j].children && tlists[j].children.length > 1) {
                linkr.log('tlists['+j+'] has ' + tlists[j].children.length + ' children');
                var loopstart=1;
                if (j===0 && tlists[j].children[0].classList.contains('now-scrobbling')) {
                    loopstart=2; // Don't put album-header at very top of Recent Tracks if the 1st row is a currently scrobbling track
                }
                for (i = loopstart; i < tlists[j].children.length; i++) {
                    linkr.log('for-loop. i=' + i);
                    if (i===1 || !tlists[j].children[i - 2].classList.contains('albumlink-row')) {
                        linkr.log('for-loop. i=' + i + ' og i-2 er IKKE allerede albumlink-row');
                        if (    altvalue(tlists[j].children[i])
                                && altvalue(tlists[j].children[i - 1])
                                && altvalue(tlists[j].children[i]) != ''
                                && altvalue(tlists[j].children[i]) === altvalue(tlists[j].children[i - 1])
                                && (i===1 || altvalue(tlists[j].children[i]) != altvalue(tlists[j].children[i - 2]))) {
                            linkr.log('for-loop. i=' + i + ' og vi har fundet en album-gruppes start', linkr.INFO);
                            var artistlink = tlists[j].children[i].querySelector('td.chartlist-name span.chartlist-artists > a');
                            var albumtitle = altvalue(tlists[j].children[i]);
                            var albumcover = tlists[j].children[i].querySelector('td.chartlist-play a > img');
                            if (albumcover) albumcover=albumcover.src;
                            if (artistlink) {
                                var artistname = artistlink.textContent;
                                artistlink = artistlink.href;
                                albumlink = artistlink + '/' + encodeURIComponent(altvalue(tlists[j].children[i])).replace(/%20/g,'+') + '/';
                                tr = document.createElement("tr");
                                tr.classList.add('albumlink-row','js-link-block','js-lazy-buylinks-focus-container');
                                tr.innerHTML = '<td class="chartlist-play"><div class="chartlist-play-image"><a href="'+albumlink+'"><img title="'+albumtitle+'" src="'+albumcover+'" class="cover-art"></a></div></td><td class="chartlist-loved"><a href="'+albumlink.replace(/\/user\/[^\/]+\/library\//,'/')+'"><img src="http://www.rockland.dk/img/album244c.png" class="cover-art" alt="album" /></a></td><td class="chartlist-name"><span class="chartlist-ellipsis-wrap"><span class="chartlist-artists"><a href="'+artistlink+'" title="'+artistname+'">'+artistname+'</a></span><span class="artist-name-spacer"> — </span><a href="'+albumlink+'" class="link-block-target" title="'+artistname+' — '+albumtitle+'">'+albumtitle+'</a></span></td><td class="chartlist-buylinks"><div class="lazy-buylinks"><button class="disclose-trigger lazy-buylinks-toggle" aria-expanded="false" data-lazy-buylink="" data-lazy-buylink-url="'+albumlink.replace(/\/user\/[^\/]+\/library\//,'/')+'/+partial/buylinks">Buy</button></div></td><td class="chartlist-timestamp"></td><td class="chartlist-delete"></td>';
                                linkr.log('Now trying to add tr...');
                                tlists[j].insertBefore(tr, tlists[j].children[i - 1]);
                                linkr.log('and should be added now!?');
                                i += 2; // or http://stackoverflow.com/questions/8766910/is-there-a-loop-start-over ?
                            }
                        }
                    }
                }

            } else {
                linkr.log('but not enough children found...');
            }
        }
        linkr.linking_running = false;
    },
    setupObserver: function () {
        linkr.log('Running setupObserver()');
        linkr.insertStyle();
        linkr.observed = document.querySelector('table.chartlist > tbody');
        if (!linkr.observed || !linkr.observed.classList) {
            linkr.log('Object to observe NOT found - re-trying later...');
        } else if (linkr.observed.classList.contains('hasObserver')) {
            linkr.log('Everything is okay! - But checking again later...');
        } else {
            linkr.linking();
            linkr.log('Now adding Observer and starting...', linkr.INFO);
            var observer = new MutationObserver(linkr.linking);
            var config = {attributes: false, childList: true, subtree: false, characterData: false};
            observer.observe(linkr.observed, config);
            linkr.observed.classList.add('hasObserver');
            linkr.log('Observer added and running...');
        }
    },
    init: function () {
        linkr.log('Running init() on last.fm');
        linkr.insertStyle();
        linkr.linking();
        setInterval(linkr.setupObserver,2000);
    }
};

linkr.init();