AniHIDE - Hide Unrelated Episodes

Filter animes in the Home/New-Episodes pages to show only what you are watching or plan to watch based on your anime list on MAL or AL.

当前为 2023-07-05 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name AniHIDE - Hide Unrelated Episodes
  3. // @namespace https://gf.qytechs.cn/en/users/781076-jery-js
  4. // @version 1.1.1
  5. // @description Filter animes in the Home/New-Episodes pages to show only what you are watching or plan to watch based on your anime list on MAL or AL.
  6. // @icon https://image.myanimelist.net/ui/OK6W_koKDTOqqqLDbIoPAiC8a86sHufn_jOI-JGtoCQ
  7. // @author Jery
  8. // @license MIT
  9. // @match https://yugenanime.*/*
  10. // @match https://yugenanime.tv/*
  11. // @match https://gogoanime.*/
  12. // @match https://gogoanime.hu/
  13. // @match https://animepahe.*/
  14. // @match https://animepahe.ru/
  15. // @grant GM_registerMenuCommand
  16. // @grant GM_addStyle
  17. // @grant GM_getValue
  18. // @grant GM_setValue
  19. // @grant GM_notification
  20. // @require https://unpkg.com/axios/dist/axios.min.js
  21. // ==/UserScript==
  22.  
  23.  
  24. /**************************
  25. * Notify new Update
  26. ***************************/
  27. if (GM_getValue("version") != GM_info.script.version) {
  28. // refreshList();
  29. GM_setValue("version", GM_info.script.version);
  30. alert(`
  31. ${GM_info.script.name}:\n
  32. This scipt has been updated!!\n
  33. What's new:
  34. -Added AnimePahe [website]
  35. -Added Timeout for certain sites [workaround]
  36. -Notification shown for list refresh [feature]
  37. -Bug Fixes + Code Cleanup`
  38. );
  39. }
  40.  
  41.  
  42. /**************************
  43. * CONSTANTS
  44. ***************************/
  45. const userSettingsKey = 'userSettings';
  46. const animeListKey = 'animeList';
  47. const manualListKey = 'manualList';
  48. const MALClientId = 'cfdd50f8037e9e8cf489992df497c761';
  49.  
  50.  
  51. /***************************************************************
  52. * ANIME SITES
  53. * -----------
  54. * the timeout variable is a workaround for sites like
  55. * AnimePahe which generate episodes page dynamically.
  56. ***************************************************************/
  57. const animeSites = [
  58. {
  59. name: 'yugenanime',
  60. item: '.ep-grid > li',
  61. title: '.ep-origin-name',
  62. thumbnail: '.ep-thumbnail > img',
  63. timeout: 0
  64. },
  65. {
  66. name: 'gogoanime',
  67. item: '.items > li',
  68. title: '.name > a',
  69. thumbnail: '.img > a > img',
  70. timeout: 0
  71. },
  72. {
  73. name: 'animepahe',
  74. item: '.episode-wrap > .episode',
  75. title: '.episode-title > a',
  76. thumbnail: '.episode-snapshot > img',
  77. timeout: 500
  78. }
  79. ];
  80.  
  81.  
  82. /***************************************************************
  83. * Classes for handling various data like settings, lists,
  84. * services and websites
  85. ***************************************************************/
  86. // User settings
  87. class UserSettings {
  88. constructor(username = '') {
  89. this.username = username;
  90. }
  91.  
  92. save() {
  93. GM_setValue(userSettingsKey, this);
  94. }
  95.  
  96. static load() {
  97. return GM_getValue(userSettingsKey, new UserSettings());
  98. }
  99. }
  100.  
  101. // Anime entry
  102. class AnimeEntry {
  103. constructor(title) {
  104. this.title = title;
  105. }
  106. }
  107.  
  108. // Anime list
  109. class AnimeList {
  110. constructor(key) {
  111. this.entries = GM_getValue(key, []);
  112. }
  113.  
  114. clear() {
  115. this.entries = [];
  116. }
  117.  
  118. removeEntry(entry) {
  119. this.entries = this.entries.filter(e => e.title !== entry.title);
  120. }
  121.  
  122. addEntry(entry) {
  123. this.entries.push(entry);
  124. }
  125.  
  126. isEntryExist(title) {
  127. return this.entries.some(entry => entry.title.toLowerCase() === title.toLowerCase());
  128. }
  129. }
  130.  
  131. // MAL service
  132. class MALService {
  133. icon = "https://image.myanimelist.net/ui/OK6W_koKDTOqqqLDbIoPAiC8a86sHufn_jOI-JGtoCQ";
  134. constructor(clientId) {
  135. this.clientId = clientId;
  136. this.proxyUrl = 'https://corsproxy.io/?';
  137. this.apiBaseUrl = 'https://api.myanimelist.net/v2/users';
  138. }
  139.  
  140. async getAnimeList(username, status) {
  141. const url = `${this.proxyUrl}${this.apiBaseUrl}/${username}/animelist?status=${status}&limit=1000`;
  142. const config = {
  143. headers: {
  144. 'X-MAL-CLIENT-ID': this.clientId,
  145. 'Origin': window.location.href
  146. }
  147. };
  148. const response = await axios.get(url, config);
  149. return response.data.data.map(entry => new AnimeEntry(entry.node.title));
  150. }
  151. }
  152.  
  153. // Website class
  154. class Website {
  155. constructor(site) {
  156. this.site = site;
  157.  
  158. // Apply initial CSS styles
  159. GM_addStyle(`
  160. /* Show eps on Hover */
  161. ${site.item} ${site.thumbnail}:hover {
  162. opacity: 1 !important;
  163. filter: brightness(1) !important;
  164. transition: .2s ease-in-out !important;
  165. }
  166. `);
  167. }
  168.  
  169. getAnimeItems() {
  170. return $(this.site.item);
  171. }
  172.  
  173. getAnimeTitle(animeItem) {
  174. return $(animeItem).find(this.site.title).text().trim();
  175. }
  176.  
  177. undarkenRelatedEps(animeList, manualList) {
  178. const animeItems = this.getAnimeItems();
  179. animeItems.each((_, animeItem) => {
  180. const animeTitle = this.getAnimeTitle(animeItem);
  181. const isRelated = animeList.isEntryExist(animeTitle) || manualList.isEntryExist(animeTitle);
  182. console.log(`Anime "${animeTitle}" is related:`, isRelated);
  183. if (isRelated) {
  184. $(animeItem).find(this.site.thumbnail).css({
  185. opacity: '1',
  186. filter: 'brightness(1)',
  187. transition: '.2s ease-in-out'
  188. });
  189. } else {
  190. $(animeItem).find(this.site.thumbnail).css({
  191. opacity: '0.5',
  192. filter: 'brightness(0.3)',
  193. transition: '.4s ease-in-out'
  194. });
  195. }
  196. });
  197. }
  198. }
  199.  
  200.  
  201. /***************************************************************
  202. * Initialize all data and setup menu commands
  203. ***************************************************************/
  204. // User settings
  205. let userSettings = UserSettings.load();
  206.  
  207. // Anime list and manual list
  208. const animeList = new AnimeList(animeListKey);
  209. const manualList = new AnimeList(manualListKey);
  210.  
  211. // MAL service instance
  212. const malService = new MALService(MALClientId);
  213.  
  214. // Register menu commands
  215. GM_registerMenuCommand('Change MAL Username', changeUsername);
  216. GM_registerMenuCommand('Refresh Anime List', refreshList);
  217. GM_registerMenuCommand('Manually Add/Remove Anime', modifyManualAnime);
  218.  
  219.  
  220. /***************************************************************
  221. * Functions for working of script
  222. ***************************************************************/
  223. // Refresh the anime list from MAL and store it using GM_setValue
  224. async function refreshList() {
  225. try {
  226. if (!userSettings.username) {
  227. alert('Please set your MAL username to continue.');
  228. changeUsername();
  229. return;
  230. }
  231.  
  232. GM_notification("Refreshing your list...", GM_info.script.name, malService.icon)
  233.  
  234. const entriesWatching = await malService.getAnimeList(userSettings.username, 'watching');
  235. const entriesPlanned = await malService.getAnimeList(userSettings.username, 'plan_to_watch');
  236. const entriesManual = manualList.entries;
  237.  
  238. animeList.clear();
  239. entriesWatching.forEach(entry => animeList.addEntry(entry));
  240. entriesPlanned.forEach(entry => animeList.addEntry(entry));
  241. entriesManual.forEach(entry => manualList.addEntry(entry));
  242.  
  243. GM_setValue(animeListKey, animeList.entries);
  244. console.log('Anime list refreshed:', animeList.entries);
  245. alert(`Anime list refreshed (${animeList.entries.length}):\n\n${animeList.entries.map((entry, i) => `${i + 1}. ${entry.title}`).join('\n')}`);
  246. undarkenRelatedEps();
  247. } catch (error) {
  248. console.error('An error occurred while refreshing the anime list:', error);
  249. alert(`An error occurred while refreshing the anime list:\n\n${error}\n\n\nAlternatively, you can try to refresh the list from any other supported site and return here.\n\nSupported sites: ${animeSites.map(site => site.name).join(', ')}`);
  250. }
  251. }
  252.  
  253. // Change MAL username
  254. function changeUsername() {
  255. const newUsername = prompt('Enter your MAL username:');
  256. if (newUsername) {
  257. userSettings.username = newUsername;
  258. userSettings.save();
  259. refreshList();
  260. }
  261. }
  262.  
  263. // Manually add anime
  264. function modifyManualAnime() {
  265. const animeTitle = prompt('This is a fallback mechanism to be used when the anime is not available on MAL or AL.\nFor both- Adding and Removing an anime, just enter the anime name.\n\nWith exact spelling, Enter the anime title:').trim();
  266. if (animeTitle) {
  267. const animeEntry = new AnimeEntry(animeTitle);
  268. if (manualList.isEntryExist(animeTitle)) {
  269. manualList.removeEntry(animeEntry);
  270. alert(`Anime Removed Successfully (reload page to see changes):\n\n${animeEntry.title}`);
  271. } else {
  272. manualList.addEntry(animeEntry);
  273. alert(`Anime Added Successfully:\n\n${animeEntry.title}`);
  274. }
  275. GM_setValue(manualListKey, manualList.entries);
  276. undarkenRelatedEps();
  277. }
  278. }
  279.  
  280. // Undarken related eps based on the anime titles
  281. function undarkenRelatedEps() {
  282. const animeSite = getCurrentSite();
  283. const thisSite = new Website(animeSite);
  284. console.log(animeSite)
  285. // Workaround for sites like AnimePahe which dynamically generate episodes page
  286. setTimeout(() =>{
  287. if (!animeSite) console.error('No matching website found.');
  288. else thisSite.undarkenRelatedEps(animeList, manualList);
  289. }, animeSite.timeout);
  290. }
  291.  
  292. // Get the current website based on the URL
  293. function getCurrentSite() {
  294. const currentUrl = window.location.href.toLowerCase();
  295. return animeSites.find(website => currentUrl.includes(website.name));
  296. }
  297.  
  298. // Run the script
  299. undarkenRelatedEps();

QingJ © 2025

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