// ==UserScript==
// @name Bilibili直播自动追帧
// @namespace http://tampermonkey.net/
// @version 0.4.2
// @description 自动追帧bilibili直播至设定的buffer length
// @author c_b
// @match https://live.bilibili.com/*
// @icon https://www.bilibili.com/favicon.ico
// @license GPLv3 License
// @supportURL https://space.bilibili.com/521676
// @run-at document-start
// @grant none
// @downloadURL none
// ==/UserScript==
(function() {
const consoleLog = console.log;
'use strict';
let videoElementId;
const getVideoElement = () => {
const e = document.getElementsByTagName('video')[0]
window.videoElement = e || window.videoElement;
videoElementId = window.videoElementId = window.videoElement?.id;
if (!window.videoElement) {
// console.log(videoElementId, window.videoElementId, window.videoElement, e)
}
return window.videoElement;
}
const updatePlaybackRateDisplay = () => {
const e = document.querySelector('.live-status');
const v = getVideoElement();
if (!e || !v) {
setTimeout(updatePlaybackRateDisplay, 100);
} else {
e.innerText = e.innerText.match(/^[^@\d]+/) + '@' + v.playbackRate.toFixed(2)
}
}
window.setRate = function (rate) {
if (Number(rate).toFixed(2) != '1.00') {
console.log('buffer', window.bufferlen().toFixed(3), 'rate', Number(rate).toFixed(2));
}
const e = getVideoElement()
if (!e) return
if (e.playbackRate.toFixed(2) == Number(rate).toFixed(2)) return;
if (Number(rate).toFixed(2) === '1.00') {
console.log('buffer', window.bufferlen().toFixed(3), 'rate', Number(rate).toFixed(2));
}
e.playbackRate = Number(rate).toFixed(2);
updatePlaybackRateDisplay();
}
window.resetRate = function () {
window.setRate(1);
}
const statsBuffLen = () => {
const e = document.querySelector('#p-video-info-bufferLength');
if (!e) return null;
if (document.querySelector('.web-player-video-info-panel').style.display === 'none') return null;
const match = e.innerText.match(/:\s*([\d\.]+)s/);
if (!match) return null;
return Number(match[1]);
}
const videoBuffLen = () => {
const e = getVideoElement();
if (!e) {
//console.log('video not found');
return null;
}
return e.buffered.end(0) - e.currentTime
}
window.bufferlen = function() {
const statsLen = statsBuffLen()
const videoLen = videoBuffLen()
if (statsLen && videoLen) {
if (statsLen - videoLen > 2) {
return statsLen;
}
}
if (videoLen) {
return videoLen;
} else {
return statsBuffLen() || videoBuffLen();
}
}
const getThres = () => {
const e = document.querySelector('#buffer-threshold');
if (!e) return null
const value = Number(document.querySelector('#buffer-threshold').value)
if (!value) return null
return value;
}
const adjustSpeedup = () => {
const thres = getThres()
if (!thres) return;
try {
const bufferLen = window.bufferlen()
if (bufferLen === null) {
return;
}
if (bufferLen - thres > 2) {
window.setRate(1.3);
} else if (bufferLen - thres > 1) {
window.setRate(1.2);
} else if (bufferLen > thres) {
window.setRate(1.1);
} else if (getVideoElement() && getVideoElement().playbackRate > 1) {
window.resetRate();
}
} catch(e) {
console.log(e)
}
}
const adjustSpeeddown = () => {
try {
const bufferLen = window.bufferlen()
if (bufferLen === null) {
return;
}
if (bufferLen < 0.2) {
window.setRate(0.1);
} else if (bufferLen < 0.3) {
window.setRate(0.3);
} else if (bufferLen < 0.6) {
window.setRate(0.6);
} else if (getVideoElement() && getVideoElement().playbackRate < 1) {
window.resetRate();
}
} catch(e) {
console.log(e)
}
}
window.speedUpIntervalId = setInterval(()=>{adjustSpeedup()}, 1000)
window.speedDownIntervalId = setInterval(()=>{adjustSpeeddown()}, 50)
const checkIsLiveReload = (timeout) => {
if (!window.__NEPTUNE_IS_MY_WAIFU__) return;
const e = document.querySelector('#auto-reload');
if (!e?.checked) return;
const status = document.querySelector('.live-status');
if (status && status.innerText.match(/^(闲置|轮播)/)) {
fetch("https://api.bilibili.com/x/space/acc/info?jsonp=jsonp&mid=" + window.__NEPTUNE_IS_MY_WAIFU__.roomInitRes.data.uid)
.then(r => r.json())
.then(r => {
if (r.code === 0 && r.data.live_room.liveStatus) {
if (timeout) {
setTimeout(()=>{checkIsLiveReload()}, timeout);
} else {
window.location.reload();
}
}
});
}
}
const checkErrorReload = (timeout) => {
const e = document.querySelector('#auto-reload');
if (!e?.checked) return
const error = document.querySelector('.web-player-error-panel');
if (error) {
if (timeout) {
setTimeout(()=>{checkErrorReload()}, timeout);
} else {
window.location.reload()
}
}
}
window.checkReloadIntervalId = setInterval(()=>{checkIsLiveReload(5000)}, 3000);
window.checkReloadIntervalId = setInterval(()=>{checkErrorReload(1000)}, 180000);
const origFetch = window.fetch;
window.fetch = function() {
let url = arguments[0];
if (url.match('api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo')) {
const e = document.querySelector('#force-flv');
if (e?.checked) {
url = url.replace(/protocol=0,[^&]+&/, 'protocol=0&');
url = url.replace(/codec=0,[^&]+&/, 'codec=0&');
arguments[0] = url;
}
}
console.log('fetch request', arguments);
return origFetch.apply(this, arguments);
}
const waitForElement = (checker, exec) => {
if (checker()) {
exec();
} else {
setTimeout(() => waitForElement(checker, exec), 100)
}
}
window.saveConfig = () => {
console.log('config changed');
let e;
e = document.querySelector('#auto-reload');
if (e) localStorage.setItem('auto-reload', e.checked);
e = document.querySelector('#force-flv');
if (e) localStorage.setItem('force-flv', e.checked);
e = document.querySelector('#buffer-threshold');
if (e) localStorage.setItem('buffer-threshold', e.value);
}
if (localStorage.getItem('auto-reload') === null) localStorage.setItem('auto-reload', true);
if (localStorage.getItem('force-flv') === null) localStorage.setItem('force-flv', true);
if (localStorage.getItem('buffer-threshold') === null) localStorage.setItem('buffer-threshold', 1.5);
if (JSON.parse(localStorage.getItem('force-flv'))) {
Object.defineProperty(window, '__NEPTUNE_IS_MY_WAIFU__', {
get: function() { return this._init_data_neptune },
set: function(newdata) {
if (newdata.roomInitRes.data?.playurl_info?.playurl?.stream) {
const playurl = newdata.roomInitRes.data.playurl_info.playurl;
const filteredStream = playurl.stream.filter( i => i.protocol_name !== "http_hls" );
if (filteredStream.length) playurl.stream = filteredStream;
playurl.stream.forEach( i => {
i.format.forEach( j => {
const filteredCodec = j.codec.filter( k => k.codec_name !== "hevc" );
if (filteredCodec.length) j.codec = filteredCodec;
})
});
}
this._init_data_neptune = newdata;
console.log(newdata)
}
});
}
waitForElement(()=>document.querySelector('#head-info-vm .right-ctnr .p-relative'), () => {
const e = document.createElement("span");
e.innerHTML = '
';
e.style = 'text-shadow: 1px 0 4px white;text-align: right;';
document.querySelector('#head-info-vm .right-ctnr .p-relative').appendChild(e);
document.querySelector('#hide_stats').onchange = (e) => {
if (!document.querySelector('.web-player-video-info-panel')) {
e.target.checked = false
return
}
if (e.target.checked) {
if (document.querySelector('.web-player-video-info-panel').style.display === 'none') {
e.target.checked = false
document.querySelector('.web-player-video-info-panel').style.setProperty('opacity', 1)
return
} else {
Array.prototype.filter.call(document.querySelector('.web-player-video-info-panel').querySelectorAll('div'), i=>i.innerText==='[x]').forEach(i=>{i.style.setProperty('display','none')});
document.querySelector('.web-player-video-info-panel').style.setProperty('opacity', 0)
document.querySelector('.web-player-video-info-panel').style.setProperty('user-select', 'none')
}
} else {
document.querySelector('.web-player-video-info-panel').style.setProperty('user-select', 'text')
document.querySelector('.web-player-video-info-panel').style.setProperty('opacity', 1)
Array.prototype.filter.call(document.querySelector('.web-player-video-info-panel').querySelectorAll('div'), i=>i.innerText==='[x]').forEach(i=>{i.style.removeProperty('display')});
}
}
document.querySelector('#auto-reload').checked = JSON.parse(localStorage.getItem('auto-reload'));
document.querySelector('#force-flv').checked = JSON.parse(localStorage.getItem('force-flv'));
document.querySelector('#buffer-threshold').value = JSON.parse(localStorage.getItem('buffer-threshold'));
window.saveConfig();
})
})();