在chrome和firefox浏览器测过,其他未知...
使用方法:安装Tampermonkey(篡改猴),添加新脚本,复制并粘贴脚本后按Ctrl+s保存,打开详细统计信息。
迷你版 v2.4 优化

// ==UserScript==
// @name YouTube Stats for Mini 迷你版 v2.4
// @namespace http://tampermonkey.net/
// @version 2.4
// @description YouTube Stats for Mini(直播/拖动进度条稳定|同步压制 pl_i / pbs)
// @author jian
// @match *://www.youtube.com/*
// @match *://m.youtube.com/*
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
/* ================= 样式 ================= */
GM_addStyle(`
.html5-video-info-panel.ytp-sfn {
position: absolute !important;
top: 10px !important;
left: 10px !important;
z-index: 99999 !important;
opacity: 0.82 !important;
font-size: 11px !important;
width: 410px !important;
max-width: 410px !important;
background: rgba(0, 0, 0, 0.68) !important;
border-radius: 4px;
overflow: hidden !important;
}
.html5-video-info-panel.ytp-sfn
.html5-video-info-panel-content > div {
white-space: normal !important;
min-height: 0 !important;
}
.yt-mbps {
margin-left: auto;
margin-right: 18px;
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 = new Set([
'Viewport / Frames',
'Current / Optimal Res',
'Connection Speed',
'Network Activity',
'Buffer Health'
]);
/* ================= 行级清理(物理移除) ================= */
function cleanRows(panel) {
const content = panel.querySelector('.html5-video-info-panel-content');
if (!content) return;
[...content.children].forEach(row => {
const label = row.querySelector(':scope > div:first-child');
const title = label?.textContent?.trim() || '';
// 无标题 / 非白名单 / 明确垃圾行
if (
!ALLOWED.has(title) ||
title === 'Color' ||
title === '' ||
/pl_i:|pbs:|SABR|Mystery/i.test(row.textContent)
) {
row.remove(); // 🔥 直接移除,永不参与 layout
} else {
row.style.display = 'flex';
}
});
}
/* ================= MB/s 注入 ================= */
function setupSpeed(panel) {
const rows = panel.querySelectorAll('.html5-video-info-panel-content > div');
let kbpsSpan, resRow;
rows.forEach(row => {
const label = row.querySelector(':scope > div:first-child');
if (!label) return;
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 m = kbpsSpan.textContent.match(/([\d.]+)\s*Kbps/i);
if (!m) return;
mbpsSpan.textContent = `${(m[1] / 8 / 1024).toFixed(2)} MB/s`;
};
update();
new MutationObserver(update).observe(kbpsSpan, {
childList: true,
characterData: true,
subtree: true
});
}
/* ================= 内容级同步 Observer ================= */
function observeContent(panel) {
if (panel._contentObserver) return;
const content = panel.querySelector('.html5-video-info-panel-content');
if (!content) return;
const observer = new MutationObserver(() => cleanRows(panel));
observer.observe(content, { childList: true, subtree: true });
panel._contentObserver = observer;
}
/* ================= 高频短时压制 ================= */
function reinforceClean(panel, duration = 2000, interval = 60) {
const start = performance.now();
const timer = setInterval(() => {
if (!document.body.contains(panel)) {
clearInterval(timer);
return;
}
cleanRows(panel);
setupSpeed(panel);
if (performance.now() - start > duration) clearInterval(timer);
}, interval);
}
/* ================= Stats 出现监听 ================= */
function setupPanelObserver() {
new MutationObserver(muts => {
for (const m of muts) {
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) continue;
const player =
document.querySelector('#movie_player') ||
document.querySelector('.html5-video-player');
if (player && panel.parentElement !== player) {
player.appendChild(panel);
}
setTimeout(() => {
cleanRows(panel);
setupSpeed(panel);
observeContent(panel);
reinforceClean(panel, 2500, 60);
}, 30);
}
}
}).observe(document.body, { childList: true, subtree: true });
}
/* ================= 播放器事件兜底 ================= */
function setupVideoListener() {
const video = document.querySelector('video');
if (!video) return;
video.addEventListener('seeked', () => {
const panel = document.querySelector('.html5-video-info-panel.ytp-sfn');
if (!panel) return;
cleanRows(panel);
reinforceClean(panel, 1200, 60);
});
video.addEventListener('timeupdate', () => {
const panel = document.querySelector('.html5-video-info-panel.ytp-sfn');
if (panel) cleanRows(panel);
});
}
function init() {
setupPanelObserver();
setupVideoListener();
}
document.readyState === 'loading'
? document.addEventListener('DOMContentLoaded', init)
: init();
})();
单行可拖动版

// ==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);
})();
在加一个两行版

// ==UserScript==
// @name YouTube Stats 两行版
// @namespace http://tampermonkey.net/
// @version 2.0
// @description YouTube Stats 两行版
// @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 lastMBps = "0.00";
let lastBuffer = "0.00";
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: 4px 8px;
border-radius: 4px;
background: rgba(0,0,0,0.55);
color: #fff;
font-size: 12px;
font-family: 'Consolas', 'Monaco', monospace;
white-space: nowrap;
cursor: move;
user-select: none;
line-height: 1.4;
z-index: 999999;
display: none;
align-items: center;
border: 1px solid rgba(255,255,255,0.1);
}
#${OVERLAY_ID} .stats-text {
display: block;
white-space: pre;
}
#${OVERLAY_ID} .close-btn {
margin-left: 8px;
font-size: 16px;
color: rgba(255,255,255,0.3);
cursor: pointer;
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.className = 'stats-text';
textSpan.id = `${OVERLAY_ID}-text`;
box.appendChild(textSpan);
const closeBtn = document.createElement('span');
closeBtn.className = 'close-btn';
closeBtn.textContent = '×';
closeBtn.onclick = (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, startX, startY, startLeft, startTop;
el.addEventListener('mousedown', 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();
});
document.addEventListener('mousemove', 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';
});
document.addEventListener('mouseup', () => {
if (dragging) {
dragging = false;
localStorage.setItem(POS_KEY, JSON.stringify({ left: el.style.left, top: el.style.top }));
}
});
}
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) lastMBps = calcMBps(parseInt(sMatch[1]));
const bMatch = raw.match(/([\d.]+)\s*s/);
if (bMatch) lastBuffer = parseFloat(bMatch[1]).toFixed(2);
}
}
function updateOverlay() {
const box = createOverlay();
if (!box || !panelActive || userClosed) {
if(box) box.style.display = 'none';
return;
}
const textSpan = box.querySelector(`#${OVERLAY_ID}-text`);
// 核心修改:为数值设置 5 位最小宽度占位,单位后紧跟末尾
// padStart(5) 确保像 "1.23" 这种 4 位数前面补一个空格对齐 "12.34"
const speedVal = lastMBps.padStart(5, ' ');
const bufferVal = lastBuffer.padStart(5, ' ');
textSpan.textContent = `速度:${speedVal}MB/s\n缓冲:${bufferVal}s`;
box.style.display = 'flex';
}
setInterval(() => {
fetchData();
updateOverlay();
}, 300);
})();
太好用了
感谢分享
good!
https://www.nodeseek.com/post-566559-1
这个看起来更直观
不错不错
@匿名 #4 我喜欢一直显示和速度蓝白条的,所以就按自己喜欢的搓