Anilist VA filter

Filters the list of characters voiced by a VA to show only the characters from anime in your completed and current watchlists.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Anilist VA filter
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  Filters the list of characters voiced by a VA to show only the characters from anime in your completed and current watchlists.
// @author       Arunato
// @match        https://anilist.co/*
// @grant        none
// @require      https://code.jquery.com/jquery-3.3.1.min.js
// ==/UserScript==

(function() {
    'use strict';
    var $ = window.jQuery;

    // Retrieves user name
    function getUser(){
        const profileLink = document.querySelector(".links").childNodes[2].href;
        const re = new RegExp("https://anilist.co/user/(.*)/");
        const user = profileLink.match(re)[1];
        return user;
    }

    function getWatchlist(user, status){
        // Query for completed anime watchlist of the user
var query = `
query ($userName: String, $listStatus: MediaListStatus) { # Define which variables will be used in the query (id)
  MediaListCollection(userName: $userName, type: ANIME, status: $listStatus) {
    user {
      id
    }
    lists {
      name
      entries {
        media {
          id
        }
      }
    }
  }
}
`;

        // Define our query variables and values that will be used in the query request
        var variables = {
            userName: user,
            listStatus: status
        };

        // Define the config we'll need for our Api request
        var url = 'https://graphql.anilist.co',
            options = {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json',
                },
                body: JSON.stringify({
                    query: query,
                    variables: variables
                })
            };

        // Make the HTTP Api request
        fetch(url, options).then(handleResponse)
            .then(handleData)
            .catch(handleError);
    }

    function handleResponse(response) {
        return response.json().then(function (json) {
            return response.ok ? json : Promise.reject(json);
        });
    }

    // Extracts anime id's from the data retrieved with the API request
    var animeIdList = [];
    function handleData(data) {
        var animeList = data.data.MediaListCollection.lists[0].entries;
        for (let i = 0; i<animeList.length; i++) {
            animeIdList.push(animeList[i].media.id);
        }
    }

    function handleError(error) {
        alert('Error, check console');
        console.error(error);
    }

    // Autoscrolls the page to the bottom until all data is loaded, then scrolls to the top
    var count = 0;
    var lastScrollHeight = 0;
    function loadPage(){
        var sh = document.documentElement.scrollHeight;
        if (sh != lastScrollHeight) {
            lastScrollHeight = sh;
            document.documentElement.scrollTop = sh;
            count = 0;
        } else {
            count++;
        }
        if (count === 5){
            clearInterval(loadingInterval);
            document.documentElement.scrollTop = 0;
            filterCharacters(animeIdList);
        }

    }

    // Filters the characters of a VA to only show characters in media present on the watchlist
    function filterCharacters(watchlist) {
        var characterList = document.querySelector(".character-roles .grid-wrap").childNodes;
        var characterArray = Array.from(characterList);
        var characterIdList = [];
        // For each character entry, checks if its media is in the watchlist. If it is not, the entry is removed.
        characterArray.forEach(function(item){
            var animeRe = /^https:\/\/anilist\.co\/anime\/(.*)\/.+/;
            var charRe = /^https:\/\/anilist\.co\/character\/(.*)\/.+/;
            var animeId = Number(item.querySelector(".media .content").href.match(animeRe)[1]);
            var charMatch = item.querySelector(".character .content").href.match(charRe);
            if (charMatch) {
                var charId = Number(charMatch[1]);
                if (animeIdList.indexOf(animeId) >= 0 && characterIdList.indexOf(charId) < 0){
                    characterIdList.push(charId);
                } else {
                    item.parentNode.removeChild(item);
                }
            } else {
                item.parentNode.removeChild(item);
            }
        });
    }

    // Creates buttons and displays it on the staff page
    var loadingInterval
    function addButtons(){
        // Create filter button
        /*
        var filterButton = document.createElement("BUTTON");
        filterButton.textContent = 'Filter';
        filterButton.setAttribute("style", "float:right");
        filterButton.addEventListener("click", function() {
            filterCharacters(animeIdList);
        }, false);
        */

        // Create loading button
        var loadingButton = document.createElement("BUTTON");
        loadingButton.textContent = 'Load page & filter';
        loadingButton.setAttribute("style", "float:right");
        loadingButton.addEventListener("click", function() {
            loadingInterval = window.setInterval(loadPage, 100);
            loadingButton.disabled = true;
        }, false);

        // Select character-roles header of staff page and add the buttons
        var charHeader = document.querySelector(".staff .character-roles h2");
        charHeader.appendChild(loadingButton);
        // charHeader.appendChild(filterButton);
    }

    // Script which filters the characters of a VA
    function filterScript(){
        addButtons();
        // TODO add sorting buttons
    }

    // Handles which script runs on which page
    function handleScripts(url){
        if (url.match(/^https:\/\/anilist\.co\/staff\/.+/)) {
            filterScript();
        };
    }

    $( window ).on( "load", function() {
        const user = getUser();
        getWatchlist(user, 'COMPLETED');
        getWatchlist(user, 'CURRENT');

        // Checks if the page url has changed and runs scripts accordingly
        // TODO change to event trigger if possible
        var current = "";
        var handle = 0;
        setInterval(function(){
            if(document.URL != current){
                clearTimeout(handle);
                current = document.URL;
                handle = setTimeout(function(){
                    handleScripts(current)
                }, 1000);
            };
        },200);
    });
})();