哔哩哔哩直播显示平均码率

A userscript to display the average bitrate of Bilibili live streams.

目前為 2025-05-24 提交的版本,檢視 最新版本

// ==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或关注我们的公众号极客氢云获取最新地址