Greasy Fork

PTH Artist Aliases Filter

Add a box on artist page to filter based on aliases

目前为 2016-12-13 提交的版本。查看 最新版本

// ==UserScript==
// @name        PTH Artist Aliases Filter
// @namespace   PTH Artist Aliases Filter
// @description Add a box on artist page to filter based on aliases
// @include     https://passtheheadphones.me/artist.php?id=*
// @version     1.2.2
// @grant       none
// ==/UserScript==

/* Avoid using jQuery in this userscript, prioritize vanilla javascript as a matter of performance on big pages */

var STORAGE_KEY = "pth_artists_aliases_filter";

var get_artist_id = function() {
    var artist_id = window.location.href.match(/id=(\d+)/)[1];
    return artist_id;
}

var create_box_aliases = function() {
    var box_aliases =
        "<div class='box box_aliases'>" +
            "<div class='head'><strong>Aliases (Click to filter)</strong></div>" +
            "<ul class='stats nobullet'>" +
                "<li>Loading...</li>" +
            "</ul>" +
        "</div>";
    var box_search = document.getElementsByClassName("box_search")[0]
    box_search.insertAdjacentHTML('beforebegin', box_aliases);    
}

// Display an error message if the API query goes wrong
var set_error_message = function() {
    document.querySelector(".sidebar .box_aliases li").innerHTML = "An error occured";
};

// Parse JSON response after having queried the API and extract interesting data
var get_aliases_and_groups = function(json_data) {
    var aliases = {}; // `aliasid` => `name`
    var groups = {}; // `groupid` => `aliasid`

    var response = json_data.response;
    var main_id = response.id;
    var name = response.name;

    var main_aliasid = undefined;

    // Iterate through each artists of each group to find those correct (`id` === `main_id`)
    var torrentgroup = response.torrentgroup;
    for (var i = 0; i < torrentgroup.length; i++) {
        var group = torrentgroup[i];

        var extendedArtists = group.extendedArtists;
        var found = false;
        for (var id in extendedArtists) {
            var artists = extendedArtists[id];
            if (artists) {
                for (var k = 0; k < artists.length; k++) {
                    var artist = artists[k];
                    if (artist.id === main_id) {
                        // This is not perfect:
                        // If a release contains references to multiple aliases of the same artist, it keeps only the first one
                        // For example, see group 72607761 of Snoop Dogg
                        // However, it is better for performance not to have to iterate through an array
                        // So let's say 1 group release => 1 artist alias
                        aliases[artist.aliasid.toString()] = artist.name;
                        groups[group.groupId.toString()] = artist.aliasid.toString();

                        if ((main_aliasid === undefined) && (artist.name === name)) {
                            // Sometimes, the aliasid associated with the artist main id differs, see artist 24926
                            // But we need it to not display "as Alias" besides releases of main artist name
                            main_aliasid = artist.aliasid.toString();
                        }
                        found = true;
                        break;
                    }
                }
            }
            if (found) break;
        }
        // Sometimes, release does not contain any artist because of an issue with the API
        // See: https://what.cd/forums.php?action=viewthread&threadid=192517&postid=5290204
        // In such a case, the release is not linked to any alias, just the default "[Show All]"
        if (!found) groups[group.groupId.toString()] = "-1";
    }
    return {aliases: aliases, groups: groups, main_aliasid: main_aliasid};
};

// Get dom elements corresponding to each alias, it will be stored once and for all (except on reload), usefull for performances
// These elements are those which will be shown or hidden (torrents rows, release groups rows and tables categories)
// During the iteration, a text node is also appended to the release row if the artist alias is different from main artist name
var get_dom_elements_per_alias = function(artist_data) {
    var aliases = artist_data.aliases;
    var groups = artist_data.groups;
    var main_aliasid = artist_data.main_aliasid;

    var dom_elements_per_alias = {"-1": []};
    for (var aliasid in aliases) {
        dom_elements_per_alias[aliasid] = [];
    }

    var torrent_tables = document.getElementsByClassName("torrent_table");

    for (var i = 0; i < torrent_tables.length; i++) {
        var torrent_table = torrent_tables[i];
        var discogs = torrent_table.getElementsByClassName("discog");
        var current_aliasid = undefined;
        var table_aliases = {};
        for (var j = 0; j < discogs.length; j++) {
            var discog = discogs[j];
            // The groupid of each torrent row is the same that the previous encountered main release row
            // This avoid having to extract groupid value at each iteration
            if (discog.classList.contains("group")) {
                var current_groupid = discog.getElementsByClassName("hide_torrents")[0].id.split("_")[1];
                current_aliasid = groups[current_groupid];
                table_aliases[current_aliasid] = 1;
                if ((current_aliasid !== main_aliasid) && (current_aliasid != "-1")) {
                    var strong = discog.getElementsByClassName("group_info")[0].getElementsByTagName("strong")[0];
                    var alias_text =
                        "<text class='artist_alias_name'>" +
                            " <i>as</i> " +
                            "<a href='#content' aliasid='" + current_aliasid + "'>" +
                                aliases[current_aliasid] +
                            "</a>" +
                        "</text>";
                    strong.insertAdjacentHTML("beforeend", alias_text);
                    var artist_alias_name = strong.getElementsByClassName("artist_alias_name")[0];
                    var artist_alias_link = artist_alias_name.getElementsByTagName("a")[0];
                    artist_alias_link.addEventListener("click", filter_releases);
                }
            }
            dom_elements_per_alias[current_aliasid].push(discog);
        }

        for (var table_alias in table_aliases) {
            dom_elements_per_alias[table_alias].unshift(torrent_table);
        }
    }

    return dom_elements_per_alias;

};

var hide_elements = function(dom_elements) {
    var nb_elems = dom_elements.length;
    for (var i = 0; i < nb_elems; i++) {
        dom_elements[i].style.display = "none";
    }
};

var show_elements = function(dom_elements) {
    var nb_elems = dom_elements.length;
    for (var i = 0; i < nb_elems; i++) {
        dom_elements[i].style.display = "";
    }
};

var hide_aliases_names = function() {
    var aliases_text = document.getElementsByClassName("artist_alias_name");

    for (var i = 0; i < aliases_text.length; i++) {
        aliases_text[i].style.display = "none";
    }
};

var show_aliases_names = function() {
    var aliases_text = document.getElementsByClassName("artist_alias_name");

    for (var i = 0; i < aliases_text.length; i++) {
        aliases_text[i].style.display = "";
    }
};

var filter_releases = function(event) {
    if (this.getAttribute("href") === "#") {
        event.preventDefault();
    }
    var new_alias_filter = this.getAttribute("aliasid");
    if (new_alias_filter === current_alias_filter) return;

    var box_aliases = document.getElementsByClassName("box_aliases")[0];
    var current_alias_link = box_aliases.querySelector("[aliasid='" + current_alias_filter + "']");
    var new_alias_link = box_aliases.querySelector("[aliasid='" + new_alias_filter + "']");

    current_alias_link.style.fontWeight = "";
    new_alias_link.style.fontWeight = "bold";

    if (current_alias_filter === "-1") {
        for (var aliasid in dom_elements_per_alias) {
            if (aliasid !== new_alias_filter) {
                hide_elements(dom_elements_per_alias[aliasid]);
            }
        }
        show_elements(dom_elements_per_alias[new_alias_filter]);
        hide_aliases_names();
    } else if (new_alias_filter === "-1") {
        for (var aliasid in dom_elements_per_alias) {
            if (aliasid !== current_alias_filter) {
                show_elements(dom_elements_per_alias[aliasid]);
            }
        }
        show_aliases_names();
    } else {
        hide_elements(dom_elements_per_alias[current_alias_filter]);
        show_elements(dom_elements_per_alias[new_alias_filter]);
    }

    current_alias_filter = new_alias_filter;
};

var set_aliases_box = function(artist_data) {
    var aliases = artist_data.aliases;
    var groups = artist_data.groups;
    var box_aliases_dom = document.getElementsByClassName("box_aliases")[0];

    // If there is only one alias, hide the box
    if (Object.keys(aliases).length < 2) {
        box_aliases_dom.style.display = "none";
        return;
    }

    // First, create the associative array of corresponding dom elements
    dom_elements_per_alias = get_dom_elements_per_alias(artist_data);

    // Then, fill the aliases box
    var list = box_aliases_dom.getElementsByTagName("ul")[0];
    list.getElementsByTagName("li")[0].innerHTML = "<a style='font-size:80%;font-weight:bold' href='#' aliasid='-1'>[Show All]</a>";
    for (var aliasid in aliases) {
        var name = aliases[aliasid];
        var li = "<li><a href='#' aliasid='" + aliasid + "'>" + name + "</a></li>";
        list.insertAdjacentHTML('beforeend', li);
    }

    // Finally, bind filtering on aliases links
    var links = list.getElementsByTagName("a");
    for (var i = 0; i < links.length; i++) {
        links[i].addEventListener("click", filter_releases);
    }
};

// Get cache (associative array {`artist_id` => {groups_ids, aliases, groups}})
var get_cache = function() {
    var storage = sessionStorage.getItem(STORAGE_KEY) || "{}";
    var cache = JSON.parse(storage);
    return cache;
};

// Get an array `groups_ids` of all groupid on the current artist page to ensure that cache is still valid (no new group since last visit)
var get_cache_hash = function() {
    var dom_groups = document.getElementsByClassName("group");
    var groups_ids = [];
    for (var i = 0; i < dom_groups.length; i++) {
        var group_id = dom_groups[i].getElementsByClassName("hide_torrents")[0].id.split("_")[1];
        groups_ids.push(group_id);
    }
    groups_ids.sort();
    var hash = groups_ids.toString();
    return hash;
};

var main = function() {
    create_box_aliases();

    var cache = get_cache();
    var hash = get_cache_hash();

    var artist_id = get_artist_id();
    var artist_cache = cache[artist_id];

    // If cache is not yet set or if it is no longer valid, query the API
    if (artist_cache === undefined || (artist_cache.hash !== hash)) {
        var api_req = "/ajax.php?action=artist&id=" + artist_id;
        $.ajax({
            url: api_req,
            success: function(data) {
                if (data.status === "success") {
                    var aliases_and_groups = get_aliases_and_groups(data);
                    artist_cache = {
                        main_aliasid: aliases_and_groups.main_aliasid,
                        aliases: aliases_and_groups.aliases,
                        groups: aliases_and_groups.groups,
                        hash: hash
                    };
                    cache[artist_id] = artist_cache;
                    sessionStorage.setItem(STORAGE_KEY, JSON.stringify(cache));
                    set_aliases_box(artist_cache);
                } else {
                    set_error_message();
                }
            },
            error: set_error_message,
        });
    } else {
        set_aliases_box(artist_cache);
    }
}

var current_alias_filter = "-1";
var dom_elements_per_alias = {};

main();