Greasy Fork

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 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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();