convertSpeedUnit

将网速单位从bit转换为Byte

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

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

(I already have a user script manager, let me install it!)

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.

(I already have a user style manager, let me install it!)

// ==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();
})();