convertSpeedUnit

将网速单位从bit转换为Byte

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         convertSpeedUnit
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  将网速单位从bit转换为Byte
// @author       ExcuseLme
// @namespace    https://github.com/ExcuseLme
// @match        *://fast.com/*
// @icon         https://fast.com/assets/new-logo-vert-37861c.svg
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const CONFIG = {
        targets: [
            { id: 'speed-value', type: 'value' },
            { id: 'speed-units', type: 'unit' },
            { id: 'upload-value', type: 'value' },
            { id: 'upload-units', type: 'unit' }
        ]
    };

    /**
     * 核心转换逻辑
     */
    function convertValue(raw) {
        let val = parseFloat(raw) / 8;
        if (isNaN(val)) return raw;
        // 遵循原站 Math.round 步进逻辑
        if (val < 9.95) return (Math.round(10 * val) / 10).toFixed(1);
        if (val < 100) return Math.round(val).toString();
        return (10 * Math.round(val / 10)).toString();
    }

    function convertUnit(raw) {
        return raw.replace('Kbps', 'KB/s')
                  .replace('Mbps', 'MB/s')
                  .replace('Gbps', 'GB/s');
    }

    /**
     * 同步并输出审计日志
     */
    function sync(target, original, shadow) {
        const rawText = original.innerText.trim();
        if (!rawText) return;

        let convertedText = (target.type === 'value') ? convertValue(rawText) : convertUnit(rawText);

        if (shadow.innerText !== convertedText) {
            shadow.innerText = convertedText;

            // 日志审计逻辑:仅在数值更新时输出完整链路,避免单位更新时重复输出
            if (target.type === 'value') {
                const isUpload = target.id.includes('upload');
                const unitId = isUpload ? 'upload-units' : 'speed-units';
                const rawUnit = document.getElementById(unitId)?.innerText.trim() || '';
                const convUnit = convertUnit(rawUnit);

                console.log(`%c[speedConvertScript] %c${rawText}${rawUnit} %c-> %c${convertedText}${convUnit}`,
                    "color: #00ADED; font-weight: bold;",
                    "color: #ff4757;",
                    "color: #2f3542;",
                    "color: #2ed573; font-weight: bold;");
            }
        }
    }

    /**
     * 初始化与 DOM 挂载
     */
    function processElement(target) {
        const original = document.getElementById(target.id);
        if (!original || original.dataset.hasShadow) return;

        const shadow = document.createElement(original.tagName);
        shadow.id = `shadow-${target.id}`;
        shadow.className = original.className;

        // 隐藏原件
        original.style.setProperty('display', 'none', 'important');
        original.parentNode.insertBefore(shadow, original);
        original.dataset.hasShadow = 'true';

        // 监听变动
        const observer = new MutationObserver(() => sync(target, original, shadow));
        observer.observe(original, { characterData: true, childList: true, subtree: true });

        // 初始同步
        sync(target, original, shadow);
    }

    // 全局 DOM 守卫
    const mainObserver = new MutationObserver(() => CONFIG.targets.forEach(processElement));

    const init = () => {
        if (document.body) {
            mainObserver.observe(document.body, { childList: true, subtree: true });
            CONFIG.targets.forEach(processElement);
        } else {
            setTimeout(init, 50);
        }
    };

    init();
})();