// ==UserScript==
// @name 恢复B站直播视频码率显示
// @version 0.6
// @namespace https://genteure.github.io/userscripts
// @description 恢复哔哩哔哩直播播放器视频统计信息里的视频码率信息显示,并添加了当前画质显示。使用方法:右键播放区域、点击 “视频统计信息”。
// @license MIT
// @author Genteure
// @match https://live.bilibili.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
// @grant none
// ==/UserScript==
(function () {
'use strict';
// 完整的原始信息会复制到 window.pstreaminfo 可在 console 里获取。
function formatStreamQualityText(url) {
const match = /\/live-bvc\/\d{6}\/live_\d+_(.+?)(?:\/index\.m3u8|\.flv)/.exec(url);
if (!match) return "未知";
const tail = match[1];
if (/^\d+$/.test(tail) || (/^\d+_[\da-z]{8}$/.test(tail) && !/^\d+_[a-z]{8}$/.test(tail))) {
return "真原画";
}
const suffix = tail.split('_').pop()
return ({
'bluray': '二压原画',
'prohevc': 'H.265 "原画" (二压)',
'4000': '蓝光',
'hevc': 'H.265 蓝光',
'2500': '超清',
'minihevc': 'H.265 超清',
'1500': '高清',
'uhd': '4K',
'maxhevc': 'H.265 4K',
'dolbyAvc': 'H.264 杜比',
})[suffix] ?? `未知画质 (${suffix})`
}
function unitTestForStreamQualityText() {
const cases = [
["/live-bvc/542653/live_11073_332_c521e483/index.m3u8", "真原画"],
["/live-bvc/542653/live_11073_332_c521e483_1500/index.m3u8", "高清"],
["/live-bvc/542653/live_11073_332_c521e483_minihevc/index.m3u8", "H.265 超清"],
["/live-bvc/338474/live_43536_1316344_minihevc/index.m3u8", "H.265 超清"],
["/live-bvc/338474/live_43536_1316344_1500/index.m3u8", "高清"],
["/live-bvc/338474/live_43536_1316344/index.m3u8", "真原画"],
["/live-bvc/542653/live_11073_332_c521e483.flv", "真原画"],
["/live-bvc/542653/live_11073_332_c521e483_1500.flv", "高清"],
["/live-bvc/542653/live_11073_332_c521e483_minihevc.flv", "H.265 超清"],
["/live-bvc/338474/live_43536_1316344_minihevc.flv", "H.265 超清"],
["/live-bvc/338474/live_43536_1316344_1500.flv", "高清"],
["/live-bvc/338474/live_43536_1316344.flv", "真原画"],
];
let success = true;
for (const [url, expected] of cases) {
const actual = formatStreamQualityText(url);
if (actual !== expected) {
success = false;
console.error(`[直播码率信息UserScript] formatStreamQualityText(${url}) = ${actual}, expected ${expected}`);
}
}
if (success) {
console.log("[直播码率信息UserScript] formatStreamQualityText test passed");
}
}
// nice test
// window.userscript_unitTestForStreamQualityText = unitTestForStreamQualityText;
function replaceFunction(ptype) {
let original_updateVideoTemplate = ptype.updateVideoTemplate;
if (!original_updateVideoTemplate) {
console.log('[直播码率信息UserScript] 没有找到 updateVideoTemplate', window.location.href);
} else {
let new_updateVideoTemplate = function () {
window.pstreaminfo = arguments[0];
let i = arguments[0].mediaInfo;
let result = original_updateVideoTemplate.apply(this, arguments);
try {
let newText = i.width + "x" + i.height + ", " + i.fps + "FPS, " + this.computeBps(i.videoDataRate);
newText += ", " + formatStreamQualityText(i.videoSrc);
document.querySelector('#p-video-info-videoInfo > .web-player-line-data').textContent = newText;
} catch (error) { }
return result;
};
ptype.updateVideoTemplate = new_updateVideoTemplate;
console.log('[直播码率信息UserScript] updateVideoTemplate 替换完成', window.location.href);
}
}
function noUndefindErrorAllowed(obj, propertyName) {
try {
return obj.exports.default.prototype[propertyName]
} catch (error) {
return undefined;
}
}
function findBase(prequire) {
let level = 0;
while (prequire) {
for (const k in prequire.cache) {
const cachedModule = prequire.cache[k];
if (!!noUndefindErrorAllowed(cachedModule, 'updateVideoTemplate') && !!noUndefindErrorAllowed(cachedModule, 'computeBps')) {
console.log('[直播码率信息UserScript] 在 window.parcelRequire' + new Array(level).fill('.parent').join('') + ' 里找到了 module id: ' + k, window.location.href)
return cachedModule.exports.default.prototype;
}
}
prequire = prequire.parent;
level++;
}
}
const ptype = findBase(window.parcelRequire);
if (!ptype) {
console.log('[直播码率信息UserScript] 没有找到 updateVideoTemplate 所在的 prototype', window.location.href);
} else {
replaceFunction(ptype);
}
})();