// ==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);