MyAnimeList (MAL) Alternative History

Displays anime/manga series history based on RSS feed, instead of default episode/chapter history

当前为 2016-10-29 提交的版本,查看 最新版本

// ==UserScript==
// @name        MyAnimeList (MAL) Alternative History
// @namespace   https://gf.qytechs.cn/users/7517
// @description Displays anime/manga series history based on RSS feed, instead of default episode/chapter history
// @icon        http://i.imgur.com/b7Fw8oH.png
// @version     3.9.1
// @author      akarin
// @include     /^https?:\/\/myanimelist\.net\/history\//
// @run-at      document-start
// @grant       none
// @noframes
// ==/UserScript==

/*jslint fudge, maxerr: 10, browser, devel, this, white, for, single */
/*global jQuery */

(function() {
    'use strict';

var mal = {
    ajaxDelay: 200,
    ajaxTimeout: 10000,
    username: document.URL.match(/^https?:\/\/myanimelist\.net\/history\/([^\/]+)/)[1],
    feedsCount: 0,
    feedsTotal: document.URL.match(/^https?:\/\/myanimelist\.net\/history\/(?!.*\/.)/) ? 2 : 1
};

mal.cache = {
    version: '3.9',
    time: 1000*60*60*36,

    encodeKey: function(key) {
        return 'alternative_history#' + mal.cache.version + '#' + key;
    },

    loadValue: function(key, value) {
        try {
            return JSON.parse(localStorage.getItem(mal.cache.encodeKey(key))) || value;
        } catch(e) {
            console.log(e.name + ': ' + e.message);
            return value;
        }
    },

    saveValue: function(key, value) {
        localStorage.setItem(mal.cache.encodeKey(key), JSON.stringify(value));
    }
};

function main($) {
    $.ajaxSetup({ timeout: mal.ajaxTimeout });
    mal.date = Date.now();

    var content = {
        body: $('<div id="ah_history_content">Loading history...</div>'),
        entries: []
    };

    content.setItem = function(id, type, item) {
        if (item && item.hasOwnProperty('info') && item.info.length > 0 &&
            item.hasOwnProperty('cover') && item.cover.length > 0) {
            $('div[id="info_' + type + '_' + id + '"]', content.body).html(item.info);
            $('img[id="cover_' + type + '_' + id + '"]', content.body).attr('src', item.cover);
            return true;
        }
        return false;
    };

    content.loadItem = function(id, type) {
        var item = mal.cache.loadValue('item.data_' + type + '_' + id, {});
        var date = mal.cache.loadValue('item.time_' + type + '_' + id, 0);
        return content.setItem(id, type, item) ? (mal.date <= parseInt(date)) : false;
    };

    content.saveItem = function(id, type, data) {
        var item = {
            info: '', cover: ''
        };

        var info = {
            first: $('span.dark_text:contains(' +
                (type === 'anime' ? 'Studios' : 'Authors') + ':)', data).parent(),
            genres: $('span.dark_text:contains(Genres:)', data).parent(),
            score: $('span.dark_text:contains(Score:)', data).parent(),
            rank: $('span.dark_text:contains(Ranked:)', data).parent(),
            popularity: $('span.dark_text:contains(Popularity:)', data).parent()
        };

        $('meta, sup, .statistics-info', info.score).remove();
        $('sup, .statistics-info', info.rank).remove();

        item.info = info.first.html() + '<br>' + info.genres.html() + '<br>' +
                    info.score.html() + info.rank.html() + info.popularity.html();

        var cover = $('img[itemprop="image"]', data);
        if (cover.length > 0 && cover[0].hasAttribute('data-src')) {
            item.cover = cover.attr('data-src').replace(/\.(\w+)$/, 't.$1');
        } else if (cover.length > 0 && cover[0].hasAttribute('src')) {
            item.cover = cover.attr('src').replace(/\.(\w+)$/, 't.$1');
        } else {
            item.cover = 'https://myanimelist.cdn-dena.com/images/qm_50.gif';
        }

        mal.cache.saveValue('item.data_' + type + '_' + id, item);
        mal.cache.saveValue('item.time_' + type + '_' + id, mal.date + mal.cache.time);
        return content.setItem(id, type, item);
    };

    content.load = function(url, feed) {
        $.get(url, function(data) {
            content.process(data, feed);
        }, 'xml').fail(function() {
            content.process('', feed);
        });
    };

    content.process = function(data, feed) {
        $('item', data).each(function() {
            content.entries.push({
                gtype: feed,
                title: $('title', this).text().match(/^(.+)(\s-(?!.*\s-))/)[1],
                type: $('title', this).text().match(/(\s-(?!.*\s-))\s?(.*)$/)[2].replace(/^$/, '-'),
                link: $('link', this).text().replace(/^https?:\/\/[^\/]+\//, '/'),
                id: $('link', this).text().match(/\/(\d+)\//)[1],
                status: $('description', this).text().match(/^(.+)\s-\s/)[1]
                    .replace(/(Watch)/, feed === 'manga' ? 'Read' : '$1'),
                progress: $('description', this).text().match(/\s-\s(.+)$/)[1]
                    .replace(/\s(chapters|episodes)/, '')
                    .replace(/\sof\s/, '/')
                    .replace(/^0\//, '-/')
                    .replace(/\?/, '-'),
                date: new Date($('pubDate', this).text())
            });
        });

        mal.feedsCount += 1;
        if (mal.feedsCount < mal.feedsTotal) {
            return;
        }

        content.entries.sort(function(a, b) {
            return b.date - a.date;
        });

        var table = $('<table id="ah_history_table" width="100%" border="0" cellpadding="0" cellspacing="0">').html('<tr>' +
            '<td class="normal_header" width="50">Image</td>' +
            '<td class="normal_header">Title</td>' +
            '<td class="normal_header" width="70">Type</td>' +
            '<td class="normal_header" width="90">Status</td>' +
            '<td class="normal_header" width="70">Progress</td>' +
            '<td class="normal_header" width="125">Date</td>' +
        '</tr>').appendTo(content.body.empty());

        var index = 0;
        $.each(content.entries, function(i, entry) {
            var dateLast = (i < content.entries.length - 1) &&
                (entry.date.getDate() !== content.entries[i + 1].date.getDate());

            $('<tr' + (dateLast ? ' class="date_last"' : '') + '>').html(
                '<td valign="top"><div class="picSurround"><a href="' + entry.link + '">' +
                    '<img id="cover_' + entry.gtype + '_' + entry.id + '" src="/images/spacer.gif" /></a></div></td>' +
                '<td valign="top"><a href="' + entry.link + '"><strong>' + entry.title + '</strong></a>' +
                    '<div id="info_' + entry.gtype + '_' + entry.id + '"></div></td>' +
                '<td>' + entry.type + '</td>' +
                '<td>' + entry.status + '</td>' +
                '<td>' + entry.progress + '</td>' +
                '<td>' + entry.date.toString()
                    .replace(/^\w+\s/, '')
                    .replace(/(\d+)/, '$1,')
                    .replace(/(\d{4})/, '$1,')
                    .replace(/(\d\d:\d\d).+$/, '$1')
                + '</td>'
            ).appendTo(table);

            if (!content.loadItem(entry.id, entry.gtype)) {
                setTimeout(function() {
                    $.get('/' + entry.gtype + '/' + entry.id + '/_/pics', function(data) {
                        content.saveItem(entry.id, entry.gtype, data);
                    });
                }, mal.ajaxDelay * index);
                index += 1;
            }
        });
    };

    var toggle = $('<a href="javascript:void(0);">Show Full History</a>').click(function() {
        var el = $(this).next('div');
        if (el.css('display') === 'none') {
            $(this).text('Hide Full History');
            el.show(200);
        } else {
            $(this).text('Show Full History');
            el.hide(200);
        }
    });

    $('#horiznav_nav + div').hide()
        .before(content.body)
        .before(toggle.css({
            'display': 'block',
            'width': '100%',
            'text-align': 'center'
        })
    );

    if (mal.feedsTotal > 1 || document.URL.match(/^https?:\/\/myanimelist\.net\/history\/[^\/]+\/anime/)) {
        content.load('/rss.php?type=rw&u=' + mal.username, 'anime');
    }

    if (mal.feedsTotal > 1 || document.URL.match(/^https?:\/\/myanimelist\.net\/history\/[^\/]+\/manga/)) {
        content.load('/rss.php?type=rm&u=' + mal.username, 'manga');
    }

    $('<style type="text/css">').html(
        '#ah_history_content { padding: 0 15px 10px 15px; }' +
        '#ah_history_table { min-width: 100%; }' +
        '#ah_history_table td { padding: 4px; border-bottom: 1px solid #ebebeb; text-align: center; }' +
        '#ah_history_table tr.date_last td { border-bottom: 2px solid #dadada; }' +
        '#ah_history_table td.normal_header { border-bottom: 1px solid #bebebe; }' +
        '#ah_history_table td:nth-of-type(2) { text-align: left; }' +
        '#ah_history_table td div[id^="info_"] { padding: 4px 0 2px; font-size: 10px; color: #666; }'
    ).appendTo('head');
}

var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '.page-common #content { display: none !important; }';
document.getElementsByTagName('head')[0].appendChild(style);

var callback = function() {
    try {
        main(jQuery);
    } catch(e) {
        console.log(e.name + ': ' + e.message);
    } finally {
        style.remove();
    }
};

if (document.readyState !== 'loading') {
    callback();
} else {
    document.addEventListener('DOMContentLoaded', callback);
}

}());

QingJ © 2025

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