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.

  1. // ==UserScript==
  2. // @name Anilist VA filter
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description Filters the list of characters voiced by a VA to show only the characters from anime in your completed and current watchlists.
  6. // @author Arunato
  7. // @match https://anilist.co/*
  8. // @grant none
  9. // @require https://code.jquery.com/jquery-3.3.1.min.js
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14. var $ = window.jQuery;
  15.  
  16. // Retrieves user name
  17. function getUser(){
  18. const profileLink = document.querySelector(".links").childNodes[2].href;
  19. const re = new RegExp("https://anilist.co/user/(.*)/");
  20. const user = profileLink.match(re)[1];
  21. return user;
  22. }
  23.  
  24. function getWatchlist(user, status){
  25. // Query for completed anime watchlist of the user
  26. var query = `
  27. query ($userName: String, $listStatus: MediaListStatus) { # Define which variables will be used in the query (id)
  28. MediaListCollection(userName: $userName, type: ANIME, status: $listStatus) {
  29. user {
  30. id
  31. }
  32. lists {
  33. name
  34. entries {
  35. media {
  36. id
  37. }
  38. }
  39. }
  40. }
  41. }
  42. `;
  43.  
  44. // Define our query variables and values that will be used in the query request
  45. var variables = {
  46. userName: user,
  47. listStatus: status
  48. };
  49.  
  50. // Define the config we'll need for our Api request
  51. var url = 'https://graphql.anilist.co',
  52. options = {
  53. method: 'POST',
  54. headers: {
  55. 'Content-Type': 'application/json',
  56. 'Accept': 'application/json',
  57. },
  58. body: JSON.stringify({
  59. query: query,
  60. variables: variables
  61. })
  62. };
  63.  
  64. // Make the HTTP Api request
  65. fetch(url, options).then(handleResponse)
  66. .then(handleData)
  67. .catch(handleError);
  68. }
  69.  
  70. function handleResponse(response) {
  71. return response.json().then(function (json) {
  72. return response.ok ? json : Promise.reject(json);
  73. });
  74. }
  75.  
  76. // Extracts anime id's from the data retrieved with the API request
  77. var animeIdList = [];
  78. function handleData(data) {
  79. var animeList = data.data.MediaListCollection.lists[0].entries;
  80. for (let i = 0; i<animeList.length; i++) {
  81. animeIdList.push(animeList[i].media.id);
  82. }
  83. }
  84.  
  85. function handleError(error) {
  86. alert('Error, check console');
  87. console.error(error);
  88. }
  89.  
  90. // Autoscrolls the page to the bottom until all data is loaded, then scrolls to the top
  91. var count = 0;
  92. var lastScrollHeight = 0;
  93. function loadPage(){
  94. var sh = document.documentElement.scrollHeight;
  95. if (sh != lastScrollHeight) {
  96. lastScrollHeight = sh;
  97. document.documentElement.scrollTop = sh;
  98. count = 0;
  99. } else {
  100. count++;
  101. }
  102. if (count === 5){
  103. clearInterval(loadingInterval);
  104. document.documentElement.scrollTop = 0;
  105. filterCharacters(animeIdList);
  106. }
  107.  
  108. }
  109.  
  110. // Filters the characters of a VA to only show characters in media present on the watchlist
  111. function filterCharacters(watchlist) {
  112. var characterList = document.querySelector(".character-roles .grid-wrap").childNodes;
  113. var characterArray = Array.from(characterList);
  114. var characterIdList = [];
  115. // For each character entry, checks if its media is in the watchlist. If it is not, the entry is removed.
  116. characterArray.forEach(function(item){
  117. var animeRe = /^https:\/\/anilist\.co\/anime\/(.*)\/.+/;
  118. var charRe = /^https:\/\/anilist\.co\/character\/(.*)\/.+/;
  119. var animeId = Number(item.querySelector(".media .content").href.match(animeRe)[1]);
  120. var charMatch = item.querySelector(".character .content").href.match(charRe);
  121. if (charMatch) {
  122. var charId = Number(charMatch[1]);
  123. if (animeIdList.indexOf(animeId) >= 0 && characterIdList.indexOf(charId) < 0){
  124. characterIdList.push(charId);
  125. } else {
  126. item.parentNode.removeChild(item);
  127. }
  128. } else {
  129. item.parentNode.removeChild(item);
  130. }
  131. });
  132. }
  133.  
  134. // Creates buttons and displays it on the staff page
  135. var loadingInterval
  136. function addButtons(){
  137. // Create filter button
  138. /*
  139. var filterButton = document.createElement("BUTTON");
  140. filterButton.textContent = 'Filter';
  141. filterButton.setAttribute("style", "float:right");
  142. filterButton.addEventListener("click", function() {
  143. filterCharacters(animeIdList);
  144. }, false);
  145. */
  146.  
  147. // Create loading button
  148. var loadingButton = document.createElement("BUTTON");
  149. loadingButton.textContent = 'Load page & filter';
  150. loadingButton.setAttribute("style", "float:right");
  151. loadingButton.addEventListener("click", function() {
  152. loadingInterval = window.setInterval(loadPage, 100);
  153. loadingButton.disabled = true;
  154. }, false);
  155.  
  156. // Select character-roles header of staff page and add the buttons
  157. var charHeader = document.querySelector(".staff .character-roles h2");
  158. charHeader.appendChild(loadingButton);
  159. // charHeader.appendChild(filterButton);
  160. }
  161.  
  162. // Script which filters the characters of a VA
  163. function filterScript(){
  164. addButtons();
  165. // TODO add sorting buttons
  166. }
  167.  
  168. // Handles which script runs on which page
  169. function handleScripts(url){
  170. if (url.match(/^https:\/\/anilist\.co\/staff\/.+/)) {
  171. filterScript();
  172. };
  173. }
  174.  
  175. $( window ).on( "load", function() {
  176. const user = getUser();
  177. getWatchlist(user, 'COMPLETED');
  178. getWatchlist(user, 'CURRENT');
  179.  
  180. // Checks if the page url has changed and runs scripts accordingly
  181. // TODO change to event trigger if possible
  182. var current = "";
  183. var handle = 0;
  184. setInterval(function(){
  185. if(document.URL != current){
  186. clearTimeout(handle);
  187. current = document.URL;
  188. handle = setTimeout(function(){
  189. handleScripts(current)
  190. }, 1000);
  191. };
  192. },200);
  193. });
  194. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址