您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Allows to find missing relations and entries with wrong chapter/episode count.
当前为
// ==UserScript== // @name MyAnimeList (MAL) Track Missing Relations // @namespace https://greasyfork.org/users/7517 // @description Allows to find missing relations and entries with wrong chapter/episode count. // @icon http://i.imgur.com/b7Fw8oH.png // @version 2.1.0 // @author akarin // @include /^http\:\/\/myanimelist\.net\/editlist\.php\?type=anime$/ // @include /^http\:\/\/myanimelist\.net\/panel\.php\?go=editmanga$/ // @grant none // @noframes // ==/UserScript== ;(function() { if ($('#malLogin').length > 0) { return; } const AJAX_DELAY = 300; const AJAX_TIMEOUT = 5000; const ANIME_T = 'anime', MANGA_T = 'manga'; const LIST_TYPE = document.URL.match(/\?type=anime$/) ? ANIME_T : MANGA_T; const ANIME_CACHE = '2.0', MANGA_CACHE = '2.0'; const LIST_CACHE = LIST_TYPE === ANIME_T ? ANIME_CACHE : MANGA_CACHE; const NICKNAME = $('ul#nav li:first > ul > li > a:contains(Profile)').prop('href').match(/(?!.*\/).*$/)[0]; const MAL_PREFIX = 'http://myanimelist.net/'; $.ajaxSetup({timeout: AJAX_TIMEOUT}); checkCache(LIST_CACHE); applyCss(); var total, calc = -1, fail = 0; var left = [], right = [], ignore = loadValue('ignoreList', '').split(';'); for (var i = 0; i < ignore.length; ++i) { if (ignore[i].trim().length === 0) { ignore.splice(i--, 1); } } var $relList = $('<div id="mr_list"></div>'); var $relBody = $('<div id="mr_body"></div>') .append($('<h2 id="mr_body_title">Missing Relations</h2>') .append(' ') .append($('<span></span>') .append('<a href="http://greasyfork.org/scripts/9261" target="_blank">v' + GM_info.script.version + '</a>') ) ) .append($relList); var content = loadValue('relList', ''); if (content.length > 0) { $relList.html(content); hideIgnoredRelations(); } else { $relList.html('<br><small>No missing relations found.</small>'); } $('<div style="display: none;"></div>').append($relBody).insertAfter('#content'); $('<span id="mr_link"></span>') .append('<span> | </span>') .append($('<a href="#mr_body">Missing Relations</a>').fancybox({ 'hideOnContentClick': false, 'hideOnOverlayClick': true, 'titleShow': false, 'transitionIn': 'none', 'transitionOut': 'none', 'scrolling': 'no' })) .append(' (') .append($('<small></small>').append( $('<a href="javascript:void(0);">refresh</a>').click(function() { if (true === confirm('Are you sure you want to recalculate missing relations?')) { recalculate($('#content')); } }) )) .append(') ').append('<span id="mr_status_done" style="color: #3c2;"></span>') .append(' ').append('<span id="mr_status_fail" style="color: #c32;"></span>') .appendTo('#content > div:first'); function checkCache(ver) { var v = loadValue('cache', null); if (v === null || v !== ver) { saveValue('cache', ver); saveValue('relList', ''); } } function recalculate(context) { if (calc > -1) { return; } var $links = $('strong + a', context); total = $links.length; if (total === 0) { $relList.html('<br><small>No missing relations found.</small>'); return; } calc = total; fail = 0; $('span#mr_status_done').text('done: ' + (total - calc) + '/' + total); $('span#mr_status_fail').text(''); $('span[id^="mr_id_"]').empty(); left = []; right = []; $relList.html('<br><small>Calculating missing relations...</small>'); $links.each(function(index) { var id = $(this).prop('href').match(/\d+$/)[0]; var $span = $(this).parent().find('span[id="mr_' + id + '"]'); if ($span.length === 0) { $('<span id="mr_id_' + id + '" style="float: right; margin: 0 7px;"></span>').appendTo($(this).parent()); } left.push(parseInt(id)); setTimeout(function() { $.ajax('/' + LIST_TYPE + '/' + id) .done(function(data) { --calc; checkRelations(id, data); checkCorrectInfo(id, data); $('span[id="mr_id_' + id + '"]').html('<small style="color: green;">done</small>'); $('span#mr_status_done').text('done: ' + (total - calc) + '/' + total); finalize(); }) .fail(function(data) { --calc; $('span[id="mr_id_' + id + '"]').html('<small style="color: red;">fail</small>'); $('span#mr_status_fail').text('fail: ' + (++fail)); finalize(); }); }, AJAX_DELAY * index); }); } function checkRelations(id, data) { var title = data.match(/<div id="contentWrapper">[\s\S]*?<h1><div [\s\S]*?<\/div>(.+?)</)[1].trim(); var cnEx = LIST_TYPE === ANIME_T ? /<h2>Related Anime<\/h2>[\s\S]*?<h2>/ : /<h2>Related Manga<\/h2>[\s\S]*?<h2>/; var idEx = LIST_TYPE === ANIME_T ? /\/anime\/(\d+)\// : /\/manga\/(\d+)\//; $('a', '<context>' + data.match(cnEx) + '</context>').each(function() { var idData = $(this).prop('href').match(idEx); if (idData !== null && idData.length > 1) { right.push({ lId: parseInt(id), lTitle: title, rId: parseInt(idData[1]), rTitle: $(this).text().trim() }); } }); } function checkCorrectInfo(id, data) { var correct = true; var title = data.match(/<div id="contentWrapper">[\s\S]*?<h1><div [\s\S]*?<\/div>(.+?)</)[1].trim(); do { var status = data.match(/>Status:<\/span>([\s\S]*?)<\/div>/)[1].trim(); var myStatus = data.match(/selected>(.*?)<\/option/)[1].trim(); if (status === (LIST_TYPE === ANIME_T ? 'Not yet aired' : 'Not yet published')) { if (myStatus !== (LIST_TYPE === ANIME_T ? 'Plan to Watch' : 'Plan to Read')) { correct = false; break; } } else if (status === (LIST_TYPE === ANIME_T ? 'Currently Airing' : 'Publishing')) { if (myStatus === 'Completed') { correct = false; break; } } if (LIST_TYPE === ANIME_T) { var eps = data.match(/>Episodes:<\/span>([\s\S]*?)<\/div>/)[1].trim(); var myEps = data.match(/name="myinfo_watchedeps"[\s\S]*?value="(\d*)"/)[1].trim(); if (eps !== 'Unknown') { if (parseInt(myEps) > parseInt(eps) || (myStatus === 'Completed' && parseInt(myEps) !== parseInt(eps))) { correct = false; break; } } } else { var vols = data.match(/>Volumes:<\/span>([\s\S]*?)<\/div>/)[1].trim(); var myVols = data.match(/id="myinfo_volumes"[\s\S]*?value="(\d*)"/)[1].trim(); if (vols !== 'Unknown') { if (parseInt(myVols) > parseInt(vols) || (myStatus === 'Completed' && parseInt(myVols) !== parseInt(vols))) { correct = false; break; } } var chap = data.match(/>Chapters:<\/span>([\s\S]*?)<\/div>/)[1].trim(); var myChap = data.match(/id="myinfo_chapters"[\s\S]*?value="(\d*)"/)[1].trim(); if (chap !== 'Unknown') { if (parseInt(myChap) > parseInt(chap) || (myStatus === 'Completed' && parseInt(myChap) !== parseInt(chap))) { correct = false; break; } } } } while (false); if (correct === false) { right.push({ lId: parseInt(id), lTitle: title, rId: -1, rTitle: '' }); } } function finalize() { if (calc > 0) { return; } if (right.length === 0) { $relList.html('<br><small>No missing relations found.</small>'); return; } var $table = $('<table id="relTable" border="0" cellpadding="0" cellspacing="0" width="100%">' + '<tr><td class="mr_table_header">You\'ve watched this…</td>' + '<td class="mr_table_header">…so you might want to check this:</td></tr></table>'); $('<tr id="mr_tr_undelete"></tr>').append($('<td colspan="2"></td>') .append($('<p id="mr_undelete_msg" style="display: none;">There are <span id="mr_undelete_num" style="font-weight: bold;"></span> hidden relations. </p>').append($('<a href="javascript:void(0);" title="Show ignored relations" onclick="window.showIgnoredRelations();">Show them</a>'))) ).prependTo($table); right.sort(function(a, b) { return a.rId > b.rId; }); var prev = -1; for (var i = 0; i < right.length; ++i) { if (right[i].rId < 0) { continue; } if (right[i].rId === prev || left.indexOf(right[i].rId) > -1) { right.splice(i--, 1); } else { prev = right[i].rId; } } right.sort(function(a, b) { if (a.lTitle === b.lTitle) { return b.rId < 0 || a.rTitle > b.rTitle; } return a.lTitle > b.lTitle; }); var $ul = $('<ul></ul>'); var $ulLeft = $ul.clone(), $ulRight = $ul.clone(); $.each(right, function(index, rel) { var $liLeft = $('<li><a title="' + rel.lTitle + '"href="' + MAL_PREFIX + LIST_TYPE + '/' + rel.lId + '" target="_blank">' + rel.lTitle + '</a></li>'); var $liRight = $('<li></li>'); if (rel.rId < 0) { $liRight.html('<div class="mr_warning">Wrong status or ' + (LIST_TYPE === ANIME_T ? 'episode' : 'chapter') + ' count</div>'); } else { $liRight.html('<div class="mr_hide"><small>' + '<a href="javascript:void(0);" title="Hide this relation" onclick="window.hideRelation(' + rel.rId + ');">x</a></small></div>' + '<a title="' + rel.rTitle + '" href="' + MAL_PREFIX + LIST_TYPE + '/' + rel.rId + '" target="_blank">' + rel.rTitle + '</a>'); } if ($ulLeft.children().length === 0) { $ulLeft.append($liLeft); } $ulRight.append($liRight.prop('id', 'mr_li_' + rel.rId)); if (index + 1 === right.length || rel.lId !== right[index + 1].lId) { $('<tr id="mr_tr_' + rel.lId + '"></tr>') .append($('<td class="mr_subject"></td>').append($ulLeft)) .append($('<td class="mr_proposed"></td>').append($ulRight)) .appendTo($table); $ulLeft = $ul.clone(); $ulRight = $ul.clone(); } }); $relList.empty().append($table); saveValue('relList', $relList.html()); hideIgnoredRelations(); calc = -1; } window.hideRelation = function(id) { hideRelation(id, true); }; window.showIgnoredRelations = function() { showIgnoredRelations(); }; function hideRelation(id, save) { var $li = $('li[id="mr_li_' + id + '"]', $relList); if ($li.length === 0) { return; } $li.hide(); var $tr = $li.closest('tr'); var count = 0; $('td.mr_proposed li', $tr).each(function() { if ($(this).css('display') !== 'none') { ++count; } }); $tr.toggle(count > 0); if (save === true && ignore.indexOf(id) < 0) { ignore.push(id); saveValue('ignoreList', ignore.join(';')); } $('#mr_undelete_num', $relList).text(ignore.length); $('#mr_undelete_msg', $relList).toggle(ignore.length > 0); } function hideIgnoredRelations() { $.each(ignore, function(index, id) { hideRelation(id, false); }); } function showIgnoredRelations() { $('#mr_undelete_msg', $relList).hide(); ignore = []; $('tr[id^="mr_tr_"]', $relList).show(); $('li[id^="mr_li_"]', $relList).show(); saveValue('ignoreList', ''); } function applyCss() { $('<style type="text/css" />').html('\ div#mr_body\ { text-align: center; width: 650px; height: auto; }\ div#mr_body #mr_body_title\ { font-size: 1.5em; font-weight: normal; text-align: center; margin: 0 0 1em 0; position: relative; border: 0; }\ div#mr_body #mr_body_title span\ { font-size: 0.8em; font-weight: normal; }\ div#mr_body #mr_body_title:after\ { position: absolute; bottom: -14px; left: 0; width: 100%; height: 8px; border-top: 1px solid #eee; background: center bottom no-repeat radial-gradient(#f6f6f6, #fff 70%); background-size: 100% 16px; content: ""; }\ div#mr_body p#mr_undelete_msg\ { margin: 0 0 10px; font-weight: normal; text-align: center; line-height: 1.6em; font-size: 1.1em; }\ div#mr_list\ { width: auto; height: 680px; overflow-y: auto; margin: 10px auto 0; }\ div#mr_list #relTable tr td\ { background-color: #fff; padding: 0.5em 0 0.5em 6px; font-weight: normal; text-align: left; box-shadow: 0px 1em 1em -1em #ddd inset; vertical-align: top; line-height: 1.6em; font-size: 1.1em; }\ div#mr_list #relTable tr:not([id="mr_tr_undelete"]):hover td\ { background-color: #f5f5f5; }\ div#mr_list #relTable tr td.mr_table_header\ { background-color: #f5f5f5; box-shadow: none; font-weight: bold; }\ div#mr_list #relTable tr#mr_tr_undelete td\ { background-color: #fff; box-shadow: none; padding: 0; margin: 0; }\ div#mr_list #relTable td.mr_proposed div.mr_warning\ { color: #e43; font-weight: bold; }\ div#mr_list #relTable td.mr_proposed div.mr_hide\ { position: absolute; right: 4px; color: #c32; }\ div#mr_list #relTable td ul\ { list-style-type: none; margin: 0; padding: 0; }\ div#mr_list #relTable td ul li\ { width: 285px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; padding-right: 15px; position: relative; }\ div#mr_list a, div#mr_list a:visited\ { color: #1969cb; text-decoration: none; }\ div#mr_list a:hover\ { color: #2d7de0; text-decoration: underline; }\ ').appendTo('head'); } function loadValue(name, defaultValue) { var value = localStorage.getItem(NICKNAME + '#' + LIST_TYPE + '#' + name); return value ? value : defaultValue; } function saveValue(name, value) { localStorage.setItem(NICKNAME + '#' + LIST_TYPE + '#' + name, value); } })();