MyAnimeList(MAL) - Anime/Manga Entries Compare

This script compares anime and manga entries from your userlists with entries from anime detail page and bold similar ones

// ==UserScript==
// @name           MyAnimeList(MAL) - Anime/Manga Entries Compare
// @description    This script compares anime and manga entries from your userlists with entries from anime detail page and bold similar ones
// @version        1.3.1
// @author         Cpt_mathix
// @namespace      https://gf.qytechs.cn/users/16080
// @match          https://myanimelist.net/anime/*
// @match          https://myanimelist.net/manga/*
// @match          https://myanimelist.net/anime.php?*
// @match          https://myanimelist.net/manga.php?*
// @exclude        /^https?:\/\/myanimelist\.net\/(anime|manga)\/[^0-9]+/
// @exclude        /^https?:\/\/myanimelist\.net\/(anime|manga)\/\d+\/.+\/.+/
// @license        GPL-2.0-or-later
// @run-at         document-end
// @grant          none
// @noframes
// ==/UserScript==

/* jshint esversion: 11 */

const version = "1.3.0";

const userName = window.MAL.USER_NAME;
if (!userName) return;

let initializing = true;
let animeList, mangaList;

init();

async function init() {
    animeList = await getUserList("anime");
    mangaList = await getUserList("manga");

    var linksOnPage = document.querySelectorAll("#content .related-entries a");

    for (let link of linksOnPage) {
        const match = link.href.match(/\/myanimelist.net\/(anime|manga)\/(\d+)\//);
        if (!match) continue;
        const [, type, id] = match;

        if (type === "anime" && isAnimeOnList(id)) {
            link.style.fontWeight = "bold";
        }

        if (type === "manga" && isMangaOnList(id)) {
            link.style.fontWeight = "bold";
        }
    }

    initializing = false;
}

function isAnimeOnList(id) {
    return animeList.hasOwnProperty(id);
}

function isMangaOnList(id) {
    return mangaList.hasOwnProperty(id);
}

async function getUserList(type, forceRefresh = false) {
    let userlistWrapper = getSetting(type + 'list', false);

    // Fetch userlist if it is older than 1 hour
    if (forceRefresh || (!(userlistWrapper?.fetchDate && ((new Date() - new Date(userlistWrapper.fetchDate)) / (60*60*1000) < 1)))) {
        const userlist = trimUserList(await fetchUserList(type));
        userlistWrapper = {
            "userlist": userlist,
            "fetchDate": new Date()
        };
        saveSetting(type + 'list', userlistWrapper, false);
    }

    return flatten(userlistWrapper.userlist);
}

async function fetchUserList(type, userlist = [], page = 1) {
    await fetch('https://myanimelist.net/' + type + 'list/' + userName + '/load.json?offset=' + ((page - 1) * 300)).then(function(response) {
        return response.json();
    }).then(async function(json) {
        userlist = userlist.concat(json);

        if (json.length !== 0) {
            await timeout(300);
            userlist = await fetchUserList(type, userlist, ++page);
        }
    });

    return userlist;
}

function trimUserList(userlist) {
    return userlist.map(entry => ({
        "id": entry.anime_id ?? entry.manga_id,
        "status": entry.status,
    }));
}

function saveSetting(key, value, hasVersion = true) {
    localStorage.setItem('MAL#' + key + (hasVersion ? '_' + version : ''), JSON.stringify(value));
}

function getSetting(key, hasVersion = true) {
    const value = localStorage.getItem('MAL#' + key + (hasVersion ? '_' + version : ''));
    if (value) {
        return JSON.parse(value);
    } else {
        return null;
    }
}

function flatten(list) {
    const map = {};
    for (let item of list) {
        map[item.id] = item;
    }
    return map;
}

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

QingJ © 2025

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