文章详情

返回首页

用AI搓了两个youtube 详细统计信息 精简版

分享文章 作者: Ws01 创建时间: 2026-01-18 📝 字数: 12,241 字 👁️ 阅读: 42 次

用AI搓了两个youtube 详细统计信息 精简版
转自:NS论坛
在chrome和firefox浏览器测过,其他未知...
使用方法:安装Tampermonkey(篡改猴),添加新脚本,复制并粘贴脚本后按Ctrl+s保存,打开详细统计信息。

1、迷你版

// ==UserScript==
// @name         YouTube Stats for Mini 迷你版v2
// @namespace    http://tampermonkey.net/
// @version      1.0.2
// @description  YouTube Stats for Mini 迷你版v2
// @author       jian
// @match        *://www.youtube.com/*
// @match        *://m.youtube.com/*
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    /* ================= 样式优化:改为 absolute 定位 ================= */
    GM_addStyle(`
        .html5-video-info-panel.ytp-sfn {
            /* 关键修改:改为绝对定位,使其相对于播放器容器滚动 */
            position: absolute !important;
            top: 10px !important;
            left: 10px !important;
            z-index: 99999 !important;
            opacity: 0.85 !important;
            font-size: 11px !important;
            width: 410px !important;
            max-width: 410px !important;
            background: rgba(0, 0, 0, 0.7) !important;
            border-radius: 4px;
            overflow-x: hidden !important;
            overflow-y: auto;
        }

        .html5-video-info-panel.ytp-sfn
        .html5-video-info-panel-content > div {
            white-space: normal !important;
        }

        .yt-mbps {
            margin-left: auto;
            margin-right: 24px;
            padding-left: 8px;
            white-space: nowrap;
            color: #ff8c00;   /* 淡橙色 */
            font-weight: bold;
        }

        .html5-video-info-panel.ytp-sfn
        .ytp-horizonchart {
            width: 190px !important;
            max-width: 190px !important;
        }

        .html5-video-info-panel.ytp-sfn
        .ytp-horizonchart canvas {
            width: 190px !important;
        }
    `);

    const ALLOWED = [
        'Viewport / Frames',
        'Current / Optimal Res',
        'Connection Speed',
        'Network Activity',
        'Buffer Health'
    ];

    function cleanRows(panel) {
        const rows = (panel || document).querySelectorAll('.html5-video-info-panel-content > div');
        rows.forEach(row => {
            const label = row.querySelector(':scope > div:first-child');
            const title = label ? label.textContent.trim() : '';
            if (title === 'Color' || title === '' || !ALLOWED.includes(title)) {
                row.style.display = 'none';
            } else {
                row.style.display = 'flex'; // 确保 flex 布局以对齐 MB/s
            }
        });
    }

    function cleanRowsRepeated(panel, times = 5, intervalMs = 200) {
        let count = 0;
        const timer = setInterval(() => {
            cleanRows(panel);
            count++;
            if (count >= times) clearInterval(timer);
        }, intervalMs);
    }

    function setupSpeed(panel) {
        const rows = panel.querySelectorAll('.html5-video-info-panel-content > div');
        let kbpsSpan = null;
        let resRow = null;

        for (const row of rows) {
            const label = row.querySelector(':scope > div:first-child');
            if (!label) continue;
            const title = label.textContent.trim();
            if (title === 'Connection Speed') {
                kbpsSpan = row.querySelector('span span:last-child, span:last-child');
            }
            if (title === 'Current / Optimal Res') {
                resRow = row;
            }
        }

        if (!kbpsSpan || !resRow) return;

        let mbpsSpan = resRow.querySelector('.yt-mbps');
        if (!mbpsSpan) {
            mbpsSpan = document.createElement('span');
            mbpsSpan.className = 'yt-mbps';
            resRow.appendChild(mbpsSpan);
        }

        const update = () => {
            const text = kbpsSpan.textContent;
            const match = text.match(/([\d.]+)\s*Kbps/i);
            if (!match) return;
            const kbps = parseFloat(match[1]);
            const mbps = (kbps / 8 / 1024).toFixed(2);
            mbpsSpan.textContent = `${mbps} MB/s`;
        };

        update();
        const observer = new MutationObserver(update);
        observer.observe(kbpsSpan, { characterData: true, childList: true, subtree: true });
        panel._speedObserver = observer;
    }

    /* ================= 核心修改:确保面板挂载在播放器内部 ================= */
    function setupPanelObserver() {
        const observer = new MutationObserver(mutations => {
            for (const m of mutations) {
                for (const node of m.addedNodes) {
                    if (node.nodeType !== 1) continue;

                    const panel = node.classList?.contains('html5-video-info-panel')
                        ? node
                        : node.querySelector?.('.html5-video-info-panel');

                    if (panel) {
                        // 强制将面板移动到播放器主容器内,这样它就会随视频滚动
                        const player = document.querySelector('#movie_player') || document.querySelector('.html5-video-player');
                        if (player && panel.parentElement !== player) {
                            player.appendChild(panel);
                        }

                        setTimeout(() => {
                            cleanRows(panel);
                            setupSpeed(panel);
                            cleanRowsRepeated(panel);
                        }, 100);
                    }
                }
            }
        });

        observer.observe(document.body, { childList: true, subtree: true });
    }

    function setupVideoListener() {
        const video = document.querySelector('video');
        if (!video) return;
        const handler = () => cleanRows();
        video.addEventListener('seeked', handler);
        video.addEventListener('timeupdate', handler);
    }

    function init() {
        setupPanelObserver();
        setupVideoListener();
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();

2、单行可拖动版

// ==UserScript==
// @name         YouTube Stats (单行可拖动版 Kbps+MB/s 1.7)
// @namespace    http://tampermonkey.net/
// @version      1.7
// @description  优化字符补位,缩短间距,显示更紧凑。
// @author       jian
// @match        *://www.youtube.com/*
// @match        *://m.youtube.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    const POS_KEY = 'yt-custom-speed-pos';
    const OVERLAY_ID = 'yt-custom-speed-overlay';

    let lastKbps = "0";
    let lastMBps = "0.00";
    let lastBuffer = "0.0";
    let panelActive = false;
    let userClosed = false;
    let lastNativeVisible = false;

    const style = document.createElement('style');
    style.textContent = `
        .html5-video-info-panel { opacity: 0 !important; pointer-events: none !important; visibility: hidden !important; }
        #${OVERLAY_ID} {
            position: absolute;
            padding: 5px 10px;
            border-radius: 6px;
            background: rgba(0,0,0,0.5);
            color: #fff;
            font-size: 12px;
            font-family: 'Consolas', 'Monaco', monospace;
            white-space: pre;
            cursor: move;
            user-select: none;
            line-height: 1.2;
            z-index: 999999;
            display: none;
            /* 进一步减小最小宽度,让框体紧贴文字 */
            min-width: 220px;
            align-items: center;
            justify-content: space-between;
        }
        #${OVERLAY_ID} .close-btn {
            margin-left: 8px;
            font-size: 16px;
            color: rgba(255,255,255,0.4);
            cursor: pointer;
            flex-shrink: 0;
            line-height: 1;
        }
        #${OVERLAY_ID} .close-btn:hover { color: #ff4d4d; }
    `;
    document.documentElement.appendChild(style);

    function calcMBps(kbps) {
        return (kbps / 8 / 1024).toFixed(2);
    }

    function createOverlay() {
        let box = document.getElementById(OVERLAY_ID);
        if (!box) {
            const player = document.querySelector('#movie_player') || document.querySelector('.html5-video-player');
            if (!player) return null;

            box = document.createElement('div');
            box.id = OVERLAY_ID;

            const textSpan = document.createElement('span');
            textSpan.id = `${OVERLAY_ID}-text`;
            box.appendChild(textSpan);

            const closeBtn = document.createElement('span');
            closeBtn.className = 'close-btn';
            closeBtn.textContent = '×';

            closeBtn.addEventListener('click', (e) => {
                e.stopPropagation();
                userClosed = true;
                const nativeCloseBtn = document.querySelector('.html5-video-info-panel button.html5-video-info-panel-close');
                if (nativeCloseBtn) nativeCloseBtn.click();
                box.style.display = 'none';
            });

            box.appendChild(closeBtn);
            const pos = JSON.parse(localStorage.getItem(POS_KEY)) || { left: '20px', top: '20px' };
            Object.assign(box.style, { left: pos.left, top: pos.top });
            enableDrag(box);
            player.appendChild(box);
        }
        return box;
    }

    function enableDrag(el) {
        let dragging = false;
        let startX, startY, startLeft, startTop;
        const start = e => {
            if (e.target.className === 'close-btn') return;
            dragging = true;
            const p = e.touches ? e.touches[0] : e;
            startX = p.clientX; startY = p.clientY;
            startLeft = el.offsetLeft; startTop = el.offsetTop;
            e.stopPropagation();
        };
        const move = e => {
            if (!dragging) return;
            const p = e.touches ? e.touches[0] : e;
            el.style.left = (startLeft + p.clientX - startX) + 'px';
            el.style.top = (startTop + p.clientY - startY) + 'px';
        };
        const end = () => {
            if (dragging) {
                dragging = false;
                localStorage.setItem(POS_KEY, JSON.stringify({ left: el.style.left, top: el.style.top }));
            }
        };
        el.addEventListener('mousedown', start);
        document.addEventListener('mousemove', move);
        document.addEventListener('mouseup', end);
    }

    function fetchData() {
        const nativePanel = document.querySelector('.html5-video-info-panel');
        const isNowVisible = !!(nativePanel && nativePanel.offsetParent !== null);

        if (isNowVisible && !lastNativeVisible) userClosed = false;
        lastNativeVisible = isNowVisible;
        panelActive = isNowVisible;

        if (nativePanel) {
            const raw = nativePanel.textContent || "";
            const sMatch = raw.match(/(\d+)\s*Kbps/i);
            if (sMatch && sMatch[1]) {
                lastKbps = sMatch[1];
                lastMBps = calcMBps(parseInt(lastKbps));
            }
            const bMatch = raw.match(/([\d.]+)\s*s/);
            if (bMatch && bMatch[1]) lastBuffer = bMatch[1];
        }
    }

    function updateOverlay() {
        const box = createOverlay();
        if (!box) return;

        if (!panelActive || userClosed) {
            box.style.display = 'none';
            return;
        }

        const textSpan = box.querySelector(`#${OVERLAY_ID}-text`);

        // --- 紧凑对齐逻辑 ---
        const kbpsStr = lastKbps.padStart(6, ' ');   // 网速 6 位
        const mbpsStr = lastMBps.padStart(5, ' ');   // MB/s 5 位 (xx.xx)
        const bufferStr = lastBuffer.padStart(5, ' '); // 缓冲 5 位 (xx.xx)

        // 缩短符号间的空格
        textSpan.textContent = `速度:${kbpsStr}Kbps → ${mbpsStr}MB/s 缓冲:${bufferStr}s`;
        box.style.display = 'flex';
    }

    setInterval(() => {
        fetchData();
        updateOverlay();
    }, 300);

})();

留言

暂无留言

0 / 100