Greasy Fork is available in English.

B站直播低延迟

降低B站直播延迟

// ==UserScript==
// @name         B站直播低延迟
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  降低B站直播延迟
// @author       TGSAN
// @match        *://live.bilibili.com/*
// @run-at       document-start
// @grant        unsafeWindow
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// ==/UserScript==

let hasLoaded = false;
(function () {
    'use strict';

    let windowCtx = self.window;
    if (self.unsafeWindow) {
        console.log("[Live Buffer Skip] use unsafeWindow mode");
        windowCtx = self.unsafeWindow;
    } else {
        console.log("[Live Buffer Skip] use window mode (your userscript extensions not support unsafeWindow)");
    }

    if (window.BILILIVEBUFFERSKIPHasLoaded) {
        return;
    }
    window.BILILIVEBUFFERSKIPHasLoaded = true;

    let menuCommandList = [];
    function doSkipBuffer() {
        let selected = GM_getValue("BILI_LIVE_BUFFERSKIP");
        let videoElement = windowCtx?.livePlayer?.getVideoEl();
        let bufferSec = 0.0;
        switch (selected) {
            case "YouTube-LL":
                bufferSec = 4.0;
                break;
            case "YouTube-ULL":
                bufferSec = 2.0;
                break;
            case "SuperLL":
                bufferSec = 1.0;
                break;
        }
        if (videoElement && bufferSec > 0) {
            let curBuffer = videoElement.buffered.end(videoElement.buffered.length - 1) - videoElement.currentTime;
            if (curBuffer > bufferSec + 1.0) {
                videoElement.currentTime += curBuffer - bufferSec;
                console.warn("降低延迟");
            }
        }

    }
    function checkSelected(type) {
        let selected = GM_getValue("BILI_LIVE_BUFFERSKIP");
        if (type == "YouTube-LL" && !selected) {
            return true;
        }
        return type == selected;
    }
    function registerSelectableVideoProcessingMenuCommand(name, type) {
        return GM_registerMenuCommand((checkSelected(type) ? "✅" : "🔲") + " " + name, function () {
            GM_setValue("BILI_LIVE_BUFFERSKIP", type);
            doSkipBuffer();
            updateMenuCommand();
        });
    }
    async function updateMenuCommand() {
        for (let command of menuCommandList) {
            await GM_unregisterMenuCommand(command);
        }
        menuCommandList = [];
        menuCommandList.push(await registerSelectableVideoProcessingMenuCommand("YouTube 低延迟模式", "YouTube-LL"));
        menuCommandList.push(await registerSelectableVideoProcessingMenuCommand("YouTube 超低延迟模式", "YouTube-ULL"));
        menuCommandList.push(await registerSelectableVideoProcessingMenuCommand("暴力低延迟(如果网络不好会卡)", "SuperLL"));
    }
    setInterval(() => {
        doSkipBuffer();
    }, 2500);
    windowCtx.document.addEventListener("readystatechange", (event) => {
        // 防止双重载入(第二次一般不会有interactive,直接complete)
        if (event.target.readyState === "interactive") {
            // 防止在框架内再次载入
            if (!windowCtx.frameElement) {
                if (!hasLoaded) {
                    hasLoaded = true;
                    updateMenuCommand();
                }
            }
        }
    });

})();