哔哩哔哩画中画弹幕

哔哩哔哩画中画支持显示弹幕

目前為 2022-10-14 提交的版本,檢視 最新版本

// ==UserScript==
// @name         哔哩哔哩画中画弹幕
// @namespace    qwq0
// @version      0.16
// @description  哔哩哔哩画中画支持显示弹幕
// @author       QwQ~
// @match        https://www.bilibili.com/video/*
// @match        https://www.bilibili.com/medialist/play/*
// @match        https://www.bilibili.com/bangumi/play/*
// @match        https://live.bilibili.com/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// ==/UserScript==

setTimeout(function() {
    'use strict';
    var videoHolder = null;
    var video = null;

    var width = 0;
    var height = 0;

    var canvas = document.createElement("canvas");
    var context = canvas.getContext("2d");
    var canvasWidth = canvas.width = 0;
    var canvasHeight = canvas.height = 0;
    var danmuFontsize = 0;
    var textCanvas = document.createElement("canvas");
    var textCanvasContext = textCanvas.getContext("2d");
    var nVideo = document.createElement("video");

    var timeoutId = 0;

    var danmuList = [];
    var danmuLine = [];
    var danmuCount = 0;

    var danmuHolder = null;
    async function addDanmu(text, color)
    {
        if(timeoutId && (danmuList.length <= 20 || Math.random() < 20 / danmuList.length))
        {
            danmuCount++;
            var lineNum = 0;
            for(var i=0;i < 5 && danmuLine[lineNum] + 6 >= danmuCount;i++)
            {
                lineNum = Math.floor(Math.random() * 12);
            }
            danmuLine[lineNum] = danmuCount;
            if(!color)
                color = "rgb(255, 255, 255)";
            var textWidth = textCanvasContext.measureText(text).width;
            textCanvasContext.clearRect(0, 0, textWidth, danmuFontsize);
            textCanvasContext.fillStyle = color;
            textCanvasContext.fillText(text, 0, 0);
            danmuList.push({text: text, color: color, x: canvasWidth, y: lineNum * danmuFontsize, w: textWidth,i: await createImageBitmap(textCanvas, 0, 0, textWidth, danmuFontsize) });
        }
    }
    var danmuObserver = new MutationObserver(e => {
        e.forEach(o=>{
            // console.log("danmu(all)", o);
            if(o.type == "childList")
            {
                o.addedNodes.forEach(ele =>{
                    // console.log("danmu(ele)", ele);
                    if(ele.innerText)
                    {
                        let text = ele.innerText;
                        let color = ele.style.color;
                        if(!color)
                            color = ele.style.getPropertyValue("--color");
                        addDanmu(text, color);
                        console.log("danmu(it)", color, text);
                    }
                    else if(ele.textContent)
                    {
                        let text = ele.textContent;
                        addDanmu(text, o.target.style.color);
                        console.log("danmu(ct)", o.target.color, text);
                    }
                });
            }
        });
    });
    setInterval(()=>{
        var nowVideoHolder = document.getElementsByClassName("bilibili-player-video")[0] || document.getElementsByClassName("bpx-player-video-wrap")[0] || document.getElementById("live-player");
        if(!nowVideoHolder)
            return;
        var nowVideo = nowVideoHolder.getElementsByTagName("video")[0];
        if(video != nowVideo)
        {
            videoHolder = nowVideoHolder;
            video = nowVideo;
            video.addEventListener("enterpictureinpicture",() => {
                if(!timeoutId)
                {
                    timeoutId = setTimeout(draw, Math.floor(1000 / 60));
                    setTimeout(()=>{
                        nVideo.requestPictureInPicture();
                        nVideo.play();
                    }, 250);
                }
                else
                {
                    nVideo.requestPictureInPicture();
                    nVideo.play();
                }
            });
            var style = document.createElement("style");
            style.innerText = `
            .bpx-player-ctrl-btn.bpx-player-ctrl-pip, .bilibili-player-video-btn.bilibili-player-video-btn-pip.closed
            {
                filter: drop-shadow(1px 1px 3px #49e3dc);
            }
            `;
            document.body.appendChild(style);
        }
        var nowDanmuHolder = document.getElementsByClassName("bilibili-player-video-danmaku")[0] || document.getElementsByClassName("bpx-player-row-dm-wrap")[0] || document.getElementsByClassName("web-player-danmaku")[0];
        if(nowDanmuHolder != danmuHolder || width != video.videoWidth || height != video.videoHeight)
        {
            danmuHolder = nowDanmuHolder;
            danmuObserver.disconnect();
            width = video.videoWidth;
            height = video.videoHeight;
            canvasWidth = canvas.width = (Math.min(height, width) < 700 ? width : Math.floor(width / 2));
            canvasHeight = canvas.height = (Math.min(height, width) < 700 ? height : Math.floor(height / 2));
            textCanvas.height = danmuFontsize = Math.floor(Math.min(canvasWidth, canvasHeight) / 14.5);
            textCanvas.width = danmuFontsize * 35;

            textCanvasContext.textBaseline = "top";
            textCanvasContext.shadowBlur = 3;
            textCanvasContext.shadowColor = "rgb(0, 0, 0)";
            textCanvasContext.font = danmuFontsize + 'px SimHei,"Microsoft JhengHei",Arial,Helvetica,sans-serif';

            nVideo.srcObject = canvas.captureStream(60);
            setTimeout(() => nVideo.play(), 1500);
            danmuObserver.observe(danmuHolder, { childList: true, subtree: true });
            console.log("[哔哩哔哩画中画弹幕]", "视频切换")
            console.log("[哔哩哔哩画中画弹幕]", "视频分辨率", width, height);
            console.log("[哔哩哔哩画中画弹幕]", "渲染分辨率", canvasWidth, canvasHeight);
        }
    }, 900);

    var lastTime = Date.now();
    function draw()
    {
        var nowTime = Date.now();
        var timeInterval = nowTime - lastTime;
        lastTime = nowTime;
        if(video)
        {
            context.globalAlpha = 1;
            context.drawImage(video, 0, 0, width, height, 0, 0, canvasWidth, canvasHeight);
            if(video.readyState >= 1)
            {
                context.globalAlpha = 0.7;
                danmuList.forEach(o => {
                    context.drawImage(o.i, Math.floor(o.x), Math.floor(o.y));
                    if(!video.paused)
                    {
                        o.x -= timeInterval * danmuFontsize * 0.0035;
                    }
                });
                danmuList = danmuList.filter(o => (o.x >= -o.w));
            }
            else
            {
                danmuList = [];
            }
        }
        timeoutId = setTimeout(draw, Math.floor(1000 / 60));
    }
    console.log("[哔哩哔哩画中画弹幕]", "已加载")
}, 500);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址