Greasy Fork

osu! remove CL and change Hit Labels

Changes "Great", "ok", "meh" back to 300,100,50 and removes CL mod from top plays

当前为 2025-06-06 提交的版本,查看 最新版本

// ==UserScript==
// @name         osu! remove CL and change Hit Labels
// @namespace    https://osu.ppy.sh
// @version      1.2
// @description  Changes "Great", "ok", "meh" back to 300,100,50 and removes CL mod from top plays
// @author       MrTerror
// @match        https://osu.ppy.sh/scores/*
// @match        https://osu.ppy.sh/beatmapsets/*
// @match        https://osu.ppy.sh/community/matches/*
// @match        https://osu.ppy.sh/users/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
    'use strict';

    // Replace hit labels (great/ok/meh → 300/100/50)
    const replacements = {
        'great': '300',
        'ok': '100',
        'meh': '50'
    };

    function replaceTextInElements(selector) {
        document.querySelectorAll(selector).forEach(el => {
            const text = el.textContent.trim().toLowerCase();
            if (replacements[text]) {
                el.textContent = replacements[text];
            }
        });
    }

    // Remove Classic (CL) mod
    function removeClassicMod() {
        document.querySelectorAll('.mod--CL').forEach(mod => mod.remove());
    }

    // Run replacements and mod removal based on current page
    function processCurrentPage() {
        const url = window.location.href;

        // Handle hit label replacements
        if (url.includes('/scores/')) {
            replaceTextInElements('.score-stats__stat-row--label');
        }
        if (url.includes('/beatmapsets/')) {
            replaceTextInElements('.beatmap-score-top__stat-header');
            replaceTextInElements('.beatmap-scoreboard-table__header--hitstat');
        }
        if (url.includes('/community/matches/')) {
            replaceTextInElements('.score-stats__stat-row--label');
            replaceTextInElements('.mp-history-player-score__stat-label');
        }
        // Handle Classic mod removal
        if (url.includes('/users/')) {
            removeClassicMod();
        }
    }

    // Initial run
    processCurrentPage();

    // Delayed check to ensure changes are applied after dynamic content loads
    setTimeout(processCurrentPage, 1500); // Run check 1.5 second after page load

    // Observe specific container for relevant content
    const getTargetContainer = () => (
        document.querySelector('.js-react--score-page') ||
        document.querySelector('.js-react--beatmapset-page') ||
        document.querySelector('.js-react--match-page') ||
        document.querySelector('.js-react--user-page') ||
        document.querySelector('.user-profile') ||
        document.querySelector('.score-stats') ||
        document.querySelector('.beatmap-scoreboard') ||
        document.querySelector('.score-list') ||
        document.querySelector('.js-profile-page-top-scores') ||
        document.body
    );

    let targetContainer = getTargetContainer();
    const observer = new MutationObserver(() => {
        processCurrentPage();
        // Re-check container in case it changes during async loading
        const newContainer = getTargetContainer();
        if (newContainer !== targetContainer) {
            targetContainer = newContainer;
            observer.observe(targetContainer, { childList: true, subtree: true, attributes: true, attributeFilter: ['class'] });
        }
    });

    observer.observe(targetContainer, { childList: true, subtree: true, attributes: true, attributeFilter: ['class'] });

    // Update observer on navigation
    function updateObserver() {
        const newContainer = getTargetContainer();
        if (newContainer !== targetContainer) {
            targetContainer = newContainer;
            observer.observe(targetContainer, { childList: true, subtree: true, attributes: true, attributeFilter: ['class'] });
        }
        processCurrentPage();
        // Additional delayed check after navigation
        setTimeout(processCurrentPage, 1000);
    }

    // Handle navigation
    window.addEventListener('popstate', updateObserver);
    const originalPushState = history.pushState;
    history.pushState = function (...args) {
        originalPushState.apply(this, args);
        updateObserver();
    };
    const originalReplaceState = history.replaceState;
    history.replaceState = function (...args) {
        originalReplaceState.apply(this, args);
        updateObserver();
    };
})();