MAL Views Tracker

Track new views for profile, animelist, and mangalist in the My Statistics panel

目前为 2025-03-27 提交的版本。查看 最新版本

// ==UserScript==
// @name         MAL Views Tracker
// @namespace    Violentmonkey Scripts
// @description  Track new views for profile, animelist, and mangalist in the My Statistics panel
// @match        https://myanimelist.net/*
// @grant        none
// @version      0.1.1
// @license      MIT
// @author       Sab_

// ==/UserScript==

(function() {
    function createViewsTracker(rowText, localStorageKey) {
        // find row in my statistics div
        const row = Array.from(document.querySelectorAll('.right .my_statistics tr'))
            .find(row => row.cells[0].textContent.trim() === rowText);

        if (!row) return;

        const currentViewsCell = row.cells[1];
        const currentViews = parseInt(currentViewsCell.textContent.replace(/,/g, ''), 10);

        // load previous data (default if none exists)
        const previousData = JSON.parse(
            localStorage.getItem(localStorageKey) ||
            '{"views": 0, "timestamp": 0, "lastChangeTimestamp": 0}'
        );
        const { views: previousViews, timestamp: previousTimestamp, lastChangeTimestamp } = previousData;

        const newViews = currentViews - previousViews;
        const currentTime = Date.now();

        // prevents from creating more than 1 indicator
        const existingIndicator = currentViewsCell.querySelector('.views-indicator');
        existingIndicator?.remove();

        // Self explanatory
        const indicator = document.createElement('span');
        indicator.className = 'views-indicator';
        indicator.style.marginLeft = '10px';
        indicator.style.fontWeight = 'bold';
        indicator.style.cursor = 'help';

        if (newViews > 0) {
            indicator.textContent = `+${newViews}`;
            indicator.style.color = 'green';
        } else {
            indicator.textContent = '+0';
            indicator.style.color = 'grey';
        }

        // css when hover
        const tooltip = document.createElement('div');
        tooltip.style.display = 'none';
        tooltip.style.position = 'absolute';
        tooltip.style.background = '#222';
        tooltip.style.color = 'white';
        tooltip.style.padding = '5px';
        tooltip.style.borderRadius = '3px';
        tooltip.style.zIndex = '1000';

        if (newViews > 0) {
            tooltip.textContent = `+${newViews} view${newViews !== 1 ? 's' : ''} since ${formatTimeDifference(currentTime - previousTimestamp)}`;
        } else {
            const lastViewTime = lastChangeTimestamp > 0 ? formatTimeDifference(currentTime - lastChangeTimestamp) : 'never';
            tooltip.textContent = `No new views (last view was ${lastViewTime})`;
        }

        // Show/hide tooltip on hover
        indicator.addEventListener('mouseenter', (e) => {
            tooltip.style.display = 'block';
            tooltip.style.left = `${e.pageX + 10}px`;
            tooltip.style.top = `${e.pageY + 10}px`;
            document.body.appendChild(tooltip);
        });

        indicator.addEventListener('mouseleave', () => {
            tooltip.remove();
        });

        currentViewsCell.appendChild(indicator);

        // updating
        localStorage.setItem(localStorageKey, JSON.stringify({
            views: currentViews,
            timestamp: currentTime,
            lastChangeTimestamp: newViews > 0 ? currentTime : lastChangeTimestamp
        }));
    }


    function formatTimeDifference(milliseconds) {
        const seconds = Math.floor(milliseconds / 1000);
        const minutes = Math.floor(seconds / 60);
        const hours = Math.floor(minutes / 60);
        const days = Math.floor(hours / 24);

        if (days > 0) return `${days} day${days !== 1 ? 's' : ''} ago`;
        if (hours > 0) return `${hours} hour${hours !== 1 ? 's' : ''} ago`;
        if (minutes > 0) return `${minutes} minute${minutes !== 1 ? 's' : ''} ago`;
        return `${seconds} second${seconds !== 1 ? 's' : ''} ago`;
    }

    function trackAllViews() {
        createViewsTracker('Profile Views', 'mal_previous_profile_views');
        createViewsTracker('AnimeList Views', 'mal_previous_animelist_views');
        createViewsTracker('MangaList Views', 'mal_previous_mangalist_views');
    }

    // Run after page loads or instantly if already loaded
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', trackAllViews);
    } else {
        trackAllViews();
    }
})();

QingJ © 2025

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