// ==UserScript==
// @name B站快捷键
// @description B站播放视频或直播时可用的快捷键,直接使用键盘操作,比鼠标更便捷
// @namespace https://github.com/RiverYale/Userscripts/
// @homepage https://riveryale.github.io/Userscripts/
// @version 5.6
// @author RiverYale
// @match *://www.bilibili.com/video/*
// @match *://www.bilibili.com/bangumi/*
// @match *://www.bilibili.com/blackboard/*
// @match *://www.bilibili.com/festival/*
// @match *://live.bilibili.com/*
// @icon https://www.bilibili.com/favicon.ico?v=1
// @run-at document-start
// @compatible chrome
// @compatible edge
// @license MIT License
// ==/UserScript==
/*================== 更新脚本前注意保存自己修改的内容! ==================*/
var onKeyDown = function (e) {
if (isOnTyping(e)) { // 判断是否正在输入
return;
}
if (17 == e.keyCode) { // Ctrl 弹幕开关
danmuToggle(e);
} else if (76 == e.keyCode) { // L 画面占比调整
videoScale();
} else if (191 == e.keyCode) { // /? 全屏
fullScreenToggle(e);
} else if (222 == e.keyCode) { // '" 宽屏
wideScreenToggel();
} else if (188 == e.keyCode) { // ,< 减速
speedAdjust("down");
} else if (190 == e.keyCode) { // .> 加速
speedAdjust("up");
} else if (186 == e.keyCode) { // ;: 视频结束后重播,直播时刷新
restart(e);
} else if (32 == e.keyCode) { // Space 直播时暂停
livePause(e);
} else if (38 == e.keyCode) { // ↑键 直播时音量+
liveVolumeAdjust(e, "up");
} else if (40 == e.keyCode) { // ↓键 直播时音量-
liveVolumeAdjust(e, "down");
} else if (77 == e.keyCode) { // M键 静音开关
liveMutedToggle(e);
} else if (81 == e.keyCode) { // Q 直播选择最高画质
bestQualitySelect()
} else if (83 == e.keyCode) { // S 直播宽屏模式下切换弹幕侧边栏
sliderToggle()
}
}
document.addEventListener("keydown", onKeyDown);
/*================== 更新脚本前注意保存自己修改的内容! ==================*/
var pageType = 0;
if (document.URL.indexOf("https://www.bilibili.com/video") >= 0) {
pageType = 0;
} else if(document.URL.indexOf("https://www.bilibili.com/blackboard") >= 0) {
pageType = 1;
} else if(document.URL.indexOf("https://www.bilibili.com/festival") >= 0) {
pageType = 1;
} else if(document.URL.indexOf("https://www.bilibili.com/bangumi") >= 0) {
pageType = 2;
} else if(document.URL.indexOf("https://live.bilibili.com") >= 0) {
pageType = 3;
}
const LIVE_TOOLS_LEFT = ".left-area .icon";
const LIVE_TOOLS_RIGHT = ".right-area .icon";
function isOnTyping(e) {
const target = e.target;
if (target.readOnly) {
return false;
}
return (target.tagName.toLowerCase() == "input" && ["text", "password"].indexOf(target.type) > -1)
|| target.tagName.toLowerCase() == "textarea"
}
function danmuToggle(e) {
switch (pageType) {
case 0:
case 1:
case 2:
if (e.keyCode != 68 || e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) {
fireKeyEvent(document.querySelector('body'), 'keydown', 68);
fireKeyEvent(document.querySelector('body'), 'keyup', 68);
}
break;
case 3:
var video = document.querySelector("video");
imitataMouseMove(video, 0, 0);
document.querySelectorAll(LIVE_TOOLS_RIGHT)[3].click()
break;
}
}
function videoScale() {
var video = document.querySelector("video");
var videoWrapper = video.parentElement;
videoWrapper.style.display = 'flex';
videoWrapper.style.justifyContent = 'center';
videoWrapper.style.alignItems = 'center';
if (document.querySelector('#videoScaleStyle') != null) {
document.querySelector('#videoScaleStyle').remove();
}
let cssStyle = document.createElement('style');
let liveToolBar = document.querySelector('#web-player__bottom-bar__container');
let toolBarHeight = liveToolBar == null ? 0 : liveToolBar.clientHeight;
cssStyle.id = 'videoScaleStyle';
cssStyle.innerHTML = `video,canvas{top:0;bottom:0;left:0;right:0;margin:auto;transform:translateY(${-toolBarHeight/2}px)}`;
videoWrapper.appendChild(cssStyle);
if (video.style.width == '') {
video.style.width = '100%';
}
var width = video.style.width.slice(0, -1) - 25;
width = (width + 75) % 100 + 25;
video.style.width = width + '%';
video.style.height = `calc(${width}% - ${toolBarHeight})`;
if (width == 100) {
document.querySelector('#videoScaleStyle').remove();
}
}
function fullScreenToggle(e) {
switch (pageType) {
case 0:
case 1:
case 2:
if (e.keyCode != 70 || e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) {
fireKeyEvent(document.querySelector('body'), 'keydown', 70);
fireKeyEvent(document.querySelector('body'), 'keyup', 70);
}
break;
case 3:
var video = document.querySelector("video");
imitataMouseMove(video, 0, 0);
document.querySelectorAll(LIVE_TOOLS_RIGHT)[0].click()
break;
}
}
function wideScreenToggel() {
switch (pageType) {
case 0:
case 1:
document.querySelector(".bpx-player-ctrl-wide").click();
break;
case 2:
document.querySelector(".squirtle-widescreen-wrap").children[0].click();
break;
case 3:
var video = document.querySelector("video");
imitataMouseMove(video, 0, 0);
document.querySelectorAll(LIVE_TOOLS_RIGHT)[1].click()
break;
}
}
function speedAdjust(upOrDown) {
if (pageType == 3) {
return;
}
var video = document.querySelector("video");
var step = document.querySelectorAll('.bpx-player-ctrl-playbackrate-menu')[0];
if (video == null) {
video = document.querySelector("bwp-video");
}
if (step == undefined) {
step = document.querySelectorAll('.squirtle-speed-select-list')[0];
}
step = step.children;
for (let i = 0; i < step.length; i++) {
if (-1 != step[i].getAttribute("class").search("active")) {
var infoText = step[i].innerHTML;
if ("down" == upOrDown && i < step.length - 1) {
step[i + 1].click();
infoText = step[i + 1].innerHTML;
} else if ("up" == upOrDown && i > 0) {
step[i - 1].click();
infoText = step[i - 1].innerHTML;
}
break;
}
}
showInfo(video.parentNode, infoText);
}
function restart(e) {
switch (pageType) {
case 0:
case 1:
var re = function() {
var endingPanel = document.querySelector(".bpx-player-ending-panel");
var restartIcon = document.querySelector("[data-action='restart']");
if (endingPanel != null && window.getComputedStyle(endingPanel).visibility != 'hidden') {
restartIcon.click();
}
}
var electricPanel = document.querySelector(".bpx-player-electric-panel");
var jumpElectric = document.querySelector(".bpx-player-electric-jump");
if (electricPanel != null && window.getComputedStyle(electricPanel).visibility != 'hidden') {
jumpElectric.click();
setTimeout(re, 500)
} else {
re();
}
break;
case 2:
var endingPanel = document.querySelector(".bpx-player-ending-panel");
var restartIcon = document.querySelector(".restart");
if (endingPanel != null && window.getComputedStyle(endingPanel).visibility != 'hidden') {
if(restartIcon == null) {
if (e.keyCode != 32 || e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) {
fireKeyEvent(document.querySelector('body'), 'keydown', 32);
fireKeyEvent(document.querySelector('body'), 'keyup', 32);
}
} else {
restartIcon.click();
}
}
break;
case 3:
var video = document.querySelector("video");
imitataMouseMove(video, 0, 0);
document.querySelectorAll(LIVE_TOOLS_LEFT)[1].click()
break;
}
}
function livePause(e) {
switch (pageType) {
case 0:
case 1:
case 2:
if (e.keyCode != 32 || e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) {
fireKeyEvent(document.querySelector('body'), 'keydown', 32);
fireKeyEvent(document.querySelector('body'), 'keyup', 32);
}
break;
case 3:
e.preventDefault();
var video = document.querySelector("video");
imitataMouseMove(video, 0, 0);
document.querySelectorAll(LIVE_TOOLS_LEFT)[0].click()
break;
}
}
function liveVolumeAdjust(e, upOrDown) {
switch (pageType) {
case 0:
case 1:
case 2:
if ("up" == upOrDown) {
if (e.keyCode != 38 || e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) {
fireKeyEvent(document.querySelector('body'), 'keydown', 38);
fireKeyEvent(document.querySelector('body'), 'keyup', 38);
}
} else if ("down" == upOrDown) {
if (e.keyCode != 40 || e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) {
fireKeyEvent(document.querySelector('body'), 'keydown', 40);
fireKeyEvent(document.querySelector('body'), 'keyup', 40);
}
}
break;
case 3:
e.preventDefault();
var maxVol = 100;
var step = 10;
var video = document.querySelector("video");
var vol = Math.floor(video.volume * maxVol);
if ("up" == upOrDown) {
vol = Math.min(maxVol, vol + step);
} else if ("down" == upOrDown) {
vol = Math.max(0, vol - step);
}
video.volume = vol / maxVol;
showInfo(video.parentNode, "音量 " + vol, 150);
if (video.muted) {
liveMutedToggle(e)
}
break;
}
}
function liveMutedToggle(e) {
switch (pageType) {
case 0:
case 1:
case 2:
if (e.keyCode != 77 || e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) {
fireKeyEvent(document.querySelector('body'), 'keydown', 77);
fireKeyEvent(document.querySelector('body'), 'keyup', 77);
}
break;
case 3:
e.preventDefault();
var video = document.querySelector("video");
imitataMouseMove(video, 0, 0);
document.querySelectorAll(LIVE_TOOLS_LEFT)[2].click()
break;
}
}
function sliderToggle() {
if (pageType != 3) {
return
}
document.querySelector("#aside-area-toggle-btn").click();
}
function bestQualitySelect() {
if (pageType != 3) {
return
}
var video = document.querySelector("video");
imitataMouseMove(video, 0, 0);
var wrapElement = document.querySelector(".quality-wrap")
imitateMouseClick('mouseenter', wrapElement, 0, 0)
setTimeout(() => document.querySelectorAll(".quality-wrap > .panel > .list-it")[0].click(), 100)
}
/* 鼠标按键事件模拟 */
function imitateMouseClick(type, oElement, iClientX, iClientY) {
var oEvent;
oEvent = document.createEvent("MouseEvents");
var rect = oElement.getBoundingClientRect();
oEvent.initMouseEvent(type, true, true, document.defaultView, 0, 0, 0, rect.x + iClientX, rect.y + iClientY, false, false, false, false, 0, null);
oElement.dispatchEvent(oEvent);
}
/* 鼠标移动事件模拟 */
function imitataMouseMove(oElement, clientX, clientY) {
var doc = oElement.ownerDocument;
var win = doc.defaultView || doc.parentWindow;
var mousemove = document.createEvent("MouseEvent");
mousemove.initMouseEvent("mousemove", true, true, win, 0, clientX, clientY, clientX, clientY, 0, 0, 0, 0, 0, null);
oElement.dispatchEvent(mousemove);
}
/* 键盘事件模拟 */
function fireKeyEvent(el, evtType, keyCode){
var doc = el.ownerDocument;
var win = doc.defaultView || doc.parentWindow;
var evtObj;
if (doc.createEvent){
if (win.KeyEvent) {
evtObj = doc.createEvent('KeyEvents');
evtObj.initKeyEvent( evtType, true, true, win, false, false, false, false, keyCode, 0 );
} else if (doc.createEventObject) {
evtObj = doc.createEventObject();
evtObj.keyCode = keyCode;
el.fireEvent('on' + evtType, evtObj);
} else {
evtObj = doc.createEvent('UIEvents');
Object.defineProperty(evtObj, 'keyCode', {
get : function() { return this.keyCodeVal; }
});
Object.defineProperty(evtObj, 'which', {
get : function() { return this.keyCodeVal; }
});
evtObj.initUIEvent( evtType, true, true, win, 1 );
evtObj.keyCodeVal = keyCode;
if (evtObj.keyCode !== keyCode) {
console.log("keyCode " + evtObj.keyCode + " 和 (" + evtObj.which + ") 不匹配");
}
}
el.dispatchEvent(evtObj);
}
}
/* 元素中间显示提示 */
function showInfo(parent, text, width = 100) {
var infoBoard = document.createElement("div");
var style = infoBoard.style;
infoBoard.setAttribute("id", "_infoBoard");
infoBoard.innerText = text;
style.width = `${width}px`;
style.height = "45px";
style.left = `calc(50% - ${width / 2}px)`;
style.top = "calc(50% - 50px)";
style.lineHeight = "45px";
style.background = "rgba(0, 0, 0, 0.6)";
style.color = "rgba(255, 255, 255, 0.8)";
style.position = "absolute";
style.zIndex = "12";
style.fontSize = "25px";
style.textAlign = "center";
var oldOne = parent.querySelector("#_infoBoard");
if (oldOne != null) {
parent.removeChild(oldOne);
}
var pos = parent.style.position;
parent.style.position = "relative";
parent.appendChild(infoBoard);
setTimeout(function () {
try {
parent.removeChild(infoBoard);
} catch (error) {}
parent.style.position = pos;
}, 1500);
}
// function imitataMouseMove(oElement, clientX, clientY) {
// var doc = oElement.ownerDocument;
// var win = doc.defaultView || doc.parentWindow;
// var mousemove = document.createEvent("MouseEvent");
// mousemove.initMouseEvent("mousemove", true, true, win, 0, clientX, clientY, clientX, clientY, 0, 0, 0, 0, 0, null);
// oElement.dispatchEvent(mousemove);
// }
// setInterval(() => {
// var video = document.querySelector("video");
// imitataMouseMove(video, 0, 0);
// }, 1000);