convertSpeedUnit

将网速单位从bit转换为Byte

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

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