您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A userscript to display the average bitrate of Bilibili live streams.
当前为
// ==UserScript== // @name 哔哩哔哩直播显示平均码率 // @namespace bili_live_average_bitrate_display // @version 1.0.0 // @author Raven-tu // @description A userscript to display the average bitrate of Bilibili live streams. // @license MIT // @icon https://live.bilibili.com/favicon.ico // @match *://live.bilibili.com/* // @connect live.bilibili.com // @grant unsafeWindow // @run-at document-start // ==/UserScript== (function () { 'use strict'; var __defProp = Object.defineProperty; var __typeError = (msg) => { throw TypeError(msg); }; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); var _bitrateRecord, _panel, _VideoMetricsMonitor_instances, addBitrateSampleAndRecalculateAverage_fn; const name = "bili_live_average_bitrate_display"; const version = "1.0.0"; const Package = { name, version }; const PROJECT_NAME = Package.name; const PROJECT_VERSION = Package.version; var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)(); const BITRATE_RECORD_MAX_LENGTH = 60; const BYTES_TO_KBPS_FACTOR = 8 / 1024; class VideoMetricsMonitor { /** * `VideoMetricsMonitor` 类的构造函数。 * @param {VideoPanel} panel - 视频面板对象。 */ constructor(panel) { __privateAdd(this, _VideoMetricsMonitor_instances); /** * 视频源 URL。 * @type {string} */ __publicField(this, "videoSrc", ""); /** * 平均码率,单位:千比特每秒 (Kbps)。 * @type {number} */ __publicField(this, "averageBitrate", 0); /** * 码率记录数组,存储最近的码率样本,单位:千比特每秒 (Kbps)。 * @type {number[]} */ __privateAdd(this, _bitrateRecord, []); /** * 用于调试的 `VideoPanel` 实例引用。 * @type {VideoPanel | null} */ __privateAdd(this, _panel, null); __privateSet(this, _panel, panel); panel.updateVideoTemplate = new Proxy(panel.updateVideoTemplate, { apply: (target, thisArg, args) => { const streamInfo = args[0]; const currentBitrate = streamInfo.realtimeInfo.videoNetworkActivity * BYTES_TO_KBPS_FACTOR; const currentVideoSrc = streamInfo.mediaInfo.videoSrc; __privateMethod(this, _VideoMetricsMonitor_instances, addBitrateSampleAndRecalculateAverage_fn).call(this, currentBitrate, currentVideoSrc); streamInfo.mediaInfo.fps = `[${__privateGet(this, _bitrateRecord).length}s] ${this.averageBitrate} Kbps. ${streamInfo.mediaInfo.fps}`; return Reflect.apply(target, thisArg, [streamInfo]); } }); } } _bitrateRecord = new WeakMap(); _panel = new WeakMap(); _VideoMetricsMonitor_instances = new WeakSet(); /** * 添加新的码率样本并重新计算平均码率。 * @param {number} newBitrate - 新的码率样本,单位:Kbps。 */ addBitrateSampleAndRecalculateAverage_fn = function(newBitrate, newVideoSrc) { if (this.videoSrc.length === 0 || this.videoSrc !== newVideoSrc) { this.videoSrc = newVideoSrc; __privateSet(this, _bitrateRecord, []); console.debug(`视频源已更改: ${this.videoSrc}`); } __privateGet(this, _bitrateRecord).unshift(newBitrate); if (__privateGet(this, _bitrateRecord).length > BITRATE_RECORD_MAX_LENGTH) { __privateGet(this, _bitrateRecord).pop(); } this.averageBitrate = (__privateGet(this, _bitrateRecord).reduce((sum, bitrate) => sum + bitrate, 0) / __privateGet(this, _bitrateRecord).length).toFixed(2); }; function initializeScriptHook() { console.log(`${PROJECT_NAME} ${PROJECT_VERSION} - 已加载。`); const originalWeakMapSet = WeakMap.prototype.set; let isHookActive = true; const isVideoPanelCandidate = (obj) => { return obj && typeof obj === "object" && "updateVideoTemplate" in obj && "createTemplateProxy" in obj; }; WeakMap.prototype.set = new Proxy(originalWeakMapSet, { apply(target, thisArg, args) { if (isHookActive) { const [key, value] = args; let panelObject = null; if (isVideoPanelCandidate(key)) { panelObject = key; } else if (isVideoPanelCandidate(value)) { panelObject = value; } if (panelObject) { isHookActive = false; console.debug("成功捕获到 VideoPanel 实例。"); try { _unsafeWindow.debugVideoMetrics = new VideoMetricsMonitor(panelObject); console.debug("VideoMetricsMonitor 初始化成功,可通过 unsafeWindow.debugVideoMetrics 访问调试信息。"); } catch (e) { console.error("VideoMetricsMonitor 初始化失败:", e); } } } return Reflect.apply(target, thisArg, args); } }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initializeScriptHook); } else { initializeScriptHook(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址