MyAnimeList(MAL) - Extra

Show anime/manga info inside your animelist/mangalist

// ==UserScript==
// @name         MyAnimeList(MAL) - Extra
// @version      5.2.0
// @description  Show anime/manga info inside your animelist/mangalist
// @author       Cpt_mathix
// @match        *://myanimelist.net/animelist/*
// @match        *://myanimelist.net/mangalist/*
// @license      GPL-2.0-or-later
// @namespace    https://gf.qytechs.cn/users/16080
// ==/UserScript==

(function ($) {
	var store = (function () {
		var map = {};

		return {
			set: function (key, value) {
				map[key] = value;
			},
			get: function (key) {
				return map[key];
			}
		};
	})();

	var mal = {
		href: document.location.href,
		modern: false,
		user: '',
		type: []
	};

	init();

	function init() {
		mal.user = mal.href.match(/[^/]*?(?=\?|$)/)[0];

		if (mal.href.indexOf("mangalist") > -1) {
			mal.type = ["manga", 65];
		} else {
			mal.type = ["anime", 64];
		}

		mal.modern = $('.header .header-menu .btn-menu > .username').length > 0;

		if (mal.modern) {
			initGlobalScrollListener();
			try {
				setTimeout(initModernList, 1000);
			} catch (ex) {
				console.log(ex);
			}
		} else {
			initOldList();
		}
	}

	function initOldList() {
		$('#list_surround > table > tbody > tr > td[class*=td]:nth-child(2)').each(function () {
			var id = $(this).find('> a').attr('href').match(/\/(\d+)\//)[1];
			var tdtype = $(this).attr('class').match(/\d/)[0];
			var moreEl = $(this).find('> div > small > a:nth-child(2)');
			var memberId = $('#listUserId').val();
			var moreObject = $("#more" + id);

			$(moreEl).removeAttr('onclick');
			$(moreEl).attr("href", "javascript:;");
			$(moreEl).on('click', displayTable(id, tdtype, moreObject, memberId));
		});
	}

	function initModernList() {
		$('#list-container > div.list-block > div > table > tbody[class=list-item] > tr.list-table-data > td.data.title').each(function () {
			if ($(this).attr('id') === 'list-extra') {
				return true;
			} else {
				$(this).attr('id', 'list-extra');
			}
			var id = $(this).find('> a').attr('href').match(/\/(\d+)\//)[1];
			var moreEl = $(this).find('> div > span.more > a');
			var memberId = $('body')[0].dataset.ownerId;
			var moreObject = $("#more-" + id);

			var el = $(moreEl)[0],
				elClone = el.cloneNode(true);
			el.parentNode.replaceChild(elClone, el);
			$(elClone).attr("href", "javascript:;");
			$(elClone).on('click', displayTable(id, 1, moreObject, memberId));
		});
	}

	function initGlobalScrollListener() {
		document.addEventListener("scroll", function scroll(event) {
			// temporarily remove scroll listener to prevent multiple events
			event.currentTarget.removeEventListener(event.type, scroll);

			initModernList();

			setTimeout(function () {
				initGlobalScrollListener();
			}, 1000);
		});
	}

	//--------------------------------//
	//       Get Data Functions       //
	//--------------------------------//

	function getAnimeInfo(animeid, table) {
		var dataMap = store.get(animeid + 'MAP');

		$.get('/' + mal.type[0] + '/' + animeid, function (data) {
			$(data).find('#content .leftside > div.spaceit_pad:nth-child(n) > span:first-child').each(function () {
				var item = this.textContent.replace(/:/g, "");
				if (isNaN(item) && this.nextSibling) {
                    if (item == "Score") {
                        dataMap[item] = this.parentNode.querySelector('.score-label').textContent;
                    } else {
                        var props = this.parentNode.querySelectorAll('[itemprop]');
                        if (props.length > 0) {
                            dataMap[item] = [...props].map(prop => prop.nextElementSibling.outerHTML).join(", ");
                        } else if (this.nextSibling.textContent.trim() === "") {
                            dataMap[item] = this.nextElementSibling.outerHTML;
                        } else {
                            dataMap[item] = this.nextSibling.textContent;
                        }
                    }
				}
			});
			$(data).find('#content .rightside > table [itemprop=description]').each(function () {
				dataMap.Synopsis = this.innerHTML;
			});
			dataMap.Image = $(data).find("#content .leftside a > img").attr('data-src');

			table.innerHTML = displayAnimeInfo(animeid);

			if (mal.modern) {
				$(table).find('a').each(function () {
					$(this).hover(function () {
						$(this).css("text-decoration", "underline");
					}, function () {
						$(this).css("text-decoration", "none");
					});
				});
			}
		});
	}

	function getDataFromOriginalMore(preData, animeid) {
		var dataMap = store.get(animeid + 'MAP');

		// Time Spent Watching
		var start = preData.indexOf('Time Spent Watching');
		var end = preData.indexOf('<small>(');
		dataMap.TimeSpentWatching = preData.substring(start + 21, end);
		start = end;
		end = preData.indexOf('per episode');
		var episodeTime = preData.substring(start + 8, end);
		dataMap.EpisodeTime = episodeTime;
	}

	//--------------------------------//
	//     Display Data Functions     //
	//--------------------------------//

	function displayTable(animeid, tdtype, moreObject, memberId) {
		return function () {
			var table = $(moreObject).find('.td' + tdtype + '.borderRBL')[0];

			if (table && moreObject.hasClass("extraLoaded")) {
				if (moreObject.hasClass("extraShowing")) {
					table.innerHTML = store.get(animeid + 'Original');
					moreObject.toggleClass("extraShowing originalShowing");
				} else if (moreObject.hasClass("originalShowing")) {
					moreObject.removeClass("originalShowing");
					moreObject.hide();
				} else {
					table.innerHTML = displayAnimeInfo(animeid);
					moreObject.addClass("extraShowing");
					moreObject.show();
				}
			} else {
				$.post("/includes/ajax-no-auth.inc.php?t=6", { color: tdtype, id: animeid, memId: memberId, type: mal.type[0] }, function (data) {
					if (mal.modern) {
						moreObject.find(".more-content").html(data.html);
					} else {
						moreObject.html(data.html);
					}
					moreObject.show();
					moreObject.addClass("extraLoaded extraShowing");

					var table = $(moreObject).find('.td' + tdtype + '.borderRBL')[0];

					var dataMap = {};
					store.set(animeid + 'MAP', dataMap);
					store.set(animeid + 'Original', table.innerHTML);
					if (table !== null && mal.type[0] != "manga") {
						getDataFromOriginalMore(table.innerHTML, animeid);
						table.innerHTML = "Fetching data";
						getAnimeInfo(animeid, table);
					} else if (table !== null) {
						table.innerHTML = "Fetching data";
						getAnimeInfo(animeid, table);
					}
				}, "json");
			}
		};
	}

	function getDataValue(animeid, key) {
		var dataMap = store.get(animeid + 'MAP');
		return dataMap[key] || "Unavailable";
	}

    function getDataValues(animeid, key1, key2) {
		var dataMap = store.get(animeid + 'MAP');
		return dataMap[key1] || dataMap[key2] || "Unavailable";
	}

	function calcTimeNeeded(episodeTime, totalEpisodes) {
		if (totalEpisodes.trim() == "0" || totalEpisodes == "Unknown") {
			return 'Unknown';
		} else if (episodeTime.indexOf("0 hours, 0 minutes, and 0 seconds") > -1) {
			return 'Unknown';
		} else if (totalEpisodes.trim() == "1") {
			return episodeTime;
		} else {
			var str = episodeTime.split(' ');

			var totalSeconds = parseInt(str[5]) * totalEpisodes + (parseInt(str[2]) * totalEpisodes * 60) + (parseInt(str[0]) * totalEpisodes * 60 * 60);
			var seconds = totalSeconds % 60;
			var totalMinutes = Math.floor(totalSeconds / 60);
			var minutes = totalMinutes % 60;
			var hours = Math.floor(totalMinutes / 60);

			return hours + " hours, " + minutes + " minutes and " + seconds + " seconds";
		}
	}

	function displayAnimeInfo(animeid) {
		var episodes = getDataValue(animeid, 'Episodes');
		var chapters = getDataValue(animeid, 'Chapters');
		var volumes = getDataValue(animeid, 'Volumes');
		var image = getDataValue(animeid, "Image");
		var genres = getDataValues(animeid, 'Genre', 'Genres');
		var status = getDataValue(animeid, 'Status');
		var broadcast = getDataValue(animeid, 'Broadcast');
		var score = getDataValue(animeid, 'Score');
		var rank = getDataValue(animeid, 'Ranked');
		var popularity = getDataValue(animeid, 'Popularity');
		var studio = getDataValues(animeid, 'Studio', 'Studios');
		var source = getDataValue(animeid, 'Source');
		var premiered = getDataValue(animeid, 'Premiered');
		var aired = getDataValue(animeid, 'Aired');
		var published = getDataValue(animeid, 'Published');
		var type = getDataValue(animeid, 'Type');
		var demographic = getDataValues(animeid, 'Demographic', 'Demographics');
		var themes = getDataValues(animeid, 'Theme', 'Themes');

		var synopsis = getDataValue(animeid, 'Synopsis').replace(/\(Source.*[\s\S]*/g, "").replace(/\[.*\]/g, "").replace(/(<br>|\s+)*$/, "");

		var timeSpentWatching;
		var timeNeeded;
		if (mal.type[0] == "anime") {
			timeSpentWatching = getDataValue(animeid, 'TimeSpentWatching').replace("tes,", "tes");
			timeNeeded = '0';
			if (timeSpentWatching.indexOf("0 hours, 0 minutes and 0 seconds") > -1) {
				timeNeeded = calcTimeNeeded(getDataValue(animeid, 'EpisodeTime'), episodes);
			}
		}

        return `
<style>
.container {  display: grid;
  grid-template-columns: auto 1fr 1fr;
  grid-template-rows: min-content 1fr min-content;
  gap: 5px;
  grid-auto-flow: row;
  grid-template-areas:
    "image details genres"
    "image synopsis synopsis"
    "image discuss time";
}

.image { grid-area: image; }
.details { grid-area: details; }
.genres { grid-area: genres; text-align: right; }
.synopsis { grid-area: synopsis; padding: 5px 0; }
.discuss { grid-area: discuss; }
.time { grid-area: time; text-align: right; }
</style>

<div class="container">
  <div class="image">
    <img src="${image}">
  </div>
  <div class="details">
    ${(mal.type[0] == "anime") ? ("<b>Source: <\/b>" + source + "<br>") : ("<b>Type: <\/b>" + type + "<br>")}
    ${(status.indexOf("Currently Airing") > -1) ? ("<b>Broadcast: <\/b>" + broadcast + "<br>") : ("")}
    ${(mal.type[0] == "anime") ? ("<b>Episodes: <\/b>" + episodes + "<br>") : ("<b>Volumes: <\/b>" + volumes + "<br><b>Chapters: <\/b>" + chapters + "<br>")}
    <b>Score: </b>${score}<br>
    ${(studio.indexOf("add some") == -1 && mal.type[0] != "manga") ? ("<b>Studio: <\/b>" + studio + "<br>") : ("")}
    ${(mal.type[0] == "anime") ? (premiered != "Unavailable" ? ("<b>Premiered: <\/b>" + premiered + "<br>") : ("")) : ("")}
    ${(mal.type[0] == "anime") ? ("<b>Aired: <\/b>" + aired + "<br>") : ("<b>Published: <\/b>" + published + "<br>")}
    ${(themes !== "Unavailable") ? ("<b>Themes: <\/b>" + themes + "<br>") : ("")}
    ${(demographic !== "Unavailable") ? ("<b>Demographic: <\/b>" + demographic + "<br>") : ("")}
  </div>
  <div class="genres">${genres}</div>
  <div class="synopsis">${synopsis}</div>
  <div class="discuss">
    <a href="/forum/?${mal.type[0]}id=${animeid}" target="_blank">
      <small>Discuss ${mal.type[0].charAt(0).toUpperCase() + mal.type[0].slice(1)}</small>
    </a>
  </div>
  <div class="time">
    ${(mal.type[0] == "anime") ? ("<small>" + (timeNeeded == '0' ? ("<b>Time Spent Watching: <\/b>" + timeSpentWatching) : ("<b>Time To Complete: <\/b>" + timeNeeded)) + "<\/small><\/td>") : ("")}
  </div>
</div>
`;
	}
})(jQuery);

QingJ © 2025

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