// ==UserScript==
// @name bilibili优化
// @namespace binger.cc
// @version 1.5
// @description 增加些快捷按键,优化个别视图,阻止按空格翻页。
// @author Ervoconite
// @match https://*.bilibili.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @grant GM_getResourceText
// ==/UserScript==
//==========================================: 设定
const autoWide = true; // 自动宽屏
const DEBUG = false; // debug 标志
// const DEBUG = true; // debug 标志
//==========================================: 不被Greakfork允许的外部插件
// #@require https://cdn.jsdelivr.net/npm/toastify-js
/**
* Minified by jsDelivr using Terser v5.14.1.
* Original file: /npm/[email protected]/src/toastify.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
/*!
* Toastify js 1.12.0
* https://github.com/apvarun/toastify-js
* @license MIT licensed
*
* Copyright (C) 2018 Varun A P
*/
!function (t, o) { "object" == typeof module && module.exports ? module.exports = o() : t.Toastify = o() }(this, (function (t) { var o = function (t) { return new o.lib.init(t) }; function i(t, o) { return o.offset[t] ? isNaN(o.offset[t]) ? o.offset[t] : o.offset[t] + "px" : "0px" } function s(t, o) { return !(!t || "string" != typeof o) && !!(t.className && t.className.trim().split(/\s+/gi).indexOf(o) > -1) } return o.defaults = { oldestFirst: !0, text: "Toastify is awesome!", node: void 0, duration: 3e3, selector: void 0, callback: function () { }, destination: void 0, newWindow: !1, close: !1, gravity: "toastify-top", positionLeft: !1, position: "", backgroundColor: "", avatar: "", className: "", stopOnFocus: !0, onClick: function () { }, offset: { x: 0, y: 0 }, escapeMarkup: !0, ariaLive: "polite", style: { background: "" } }, o.lib = o.prototype = { toastify: "1.12.0", constructor: o, init: function (t) { return t || (t = {}), this.options = {}, this.toastElement = null, this.options.text = t.text || o.defaults.text, this.options.node = t.node || o.defaults.node, this.options.duration = 0 === t.duration ? 0 : t.duration || o.defaults.duration, this.options.selector = t.selector || o.defaults.selector, this.options.callback = t.callback || o.defaults.callback, this.options.destination = t.destination || o.defaults.destination, this.options.newWindow = t.newWindow || o.defaults.newWindow, this.options.close = t.close || o.defaults.close, this.options.gravity = "bottom" === t.gravity ? "toastify-bottom" : o.defaults.gravity, this.options.positionLeft = t.positionLeft || o.defaults.positionLeft, this.options.position = t.position || o.defaults.position, this.options.backgroundColor = t.backgroundColor || o.defaults.backgroundColor, this.options.avatar = t.avatar || o.defaults.avatar, this.options.className = t.className || o.defaults.className, this.options.stopOnFocus = void 0 === t.stopOnFocus ? o.defaults.stopOnFocus : t.stopOnFocus, this.options.onClick = t.onClick || o.defaults.onClick, this.options.offset = t.offset || o.defaults.offset, this.options.escapeMarkup = void 0 !== t.escapeMarkup ? t.escapeMarkup : o.defaults.escapeMarkup, this.options.ariaLive = t.ariaLive || o.defaults.ariaLive, this.options.style = t.style || o.defaults.style, t.backgroundColor && (this.options.style.background = t.backgroundColor), this }, buildToast: function () { if (!this.options) throw "Toastify is not initialized"; var t = document.createElement("div"); for (var o in t.className = "toastify on " + this.options.className, this.options.position ? t.className += " toastify-" + this.options.position : !0 === this.options.positionLeft ? (t.className += " toastify-left", console.warn("Property `positionLeft` will be depreciated in further versions. Please use `position` instead.")) : t.className += " toastify-right", t.className += " " + this.options.gravity, this.options.backgroundColor && console.warn('DEPRECATION NOTICE: "backgroundColor" is being deprecated. Please use the "style.background" property.'), this.options.style) t.style[o] = this.options.style[o]; if (this.options.ariaLive && t.setAttribute("aria-live", this.options.ariaLive), this.options.node && this.options.node.nodeType === Node.ELEMENT_NODE) t.appendChild(this.options.node); else if (this.options.escapeMarkup ? t.innerText = this.options.text : t.innerHTML = this.options.text, "" !== this.options.avatar) { var s = document.createElement("img"); s.src = this.options.avatar, s.className = "toastify-avatar", "left" == this.options.position || !0 === this.options.positionLeft ? t.appendChild(s) : t.insertAdjacentElement("afterbegin", s) } if (!0 === this.options.close) { var e = document.createElement("button"); e.type = "button", e.setAttribute("aria-label", "Close"), e.className = "toast-close", e.innerHTML = "✖", e.addEventListener("click", function (t) { t.stopPropagation(), this.removeElement(this.toastElement), window.clearTimeout(this.toastElement.timeOutValue) }.bind(this)); var n = window.innerWidth > 0 ? window.innerWidth : screen.width; ("left" == this.options.position || !0 === this.options.positionLeft) && n > 360 ? t.insertAdjacentElement("afterbegin", e) : t.appendChild(e) } if (this.options.stopOnFocus && this.options.duration > 0) { var a = this; t.addEventListener("mouseover", (function (o) { window.clearTimeout(t.timeOutValue) })), t.addEventListener("mouseleave", (function () { t.timeOutValue = window.setTimeout((function () { a.removeElement(t) }), a.options.duration) })) } if (void 0 !== this.options.destination && t.addEventListener("click", function (t) { t.stopPropagation(), !0 === this.options.newWindow ? window.open(this.options.destination, "_blank") : window.location = this.options.destination }.bind(this)), "function" == typeof this.options.onClick && void 0 === this.options.destination && t.addEventListener("click", function (t) { t.stopPropagation(), this.options.onClick() }.bind(this)), "object" == typeof this.options.offset) { var l = i("x", this.options), r = i("y", this.options), p = "left" == this.options.position ? l : "-" + l, d = "toastify-top" == this.options.gravity ? r : "-" + r; t.style.transform = "translate(" + p + "," + d + ")" } return t }, showToast: function () { var t; if (this.toastElement = this.buildToast(), !(t = "string" == typeof this.options.selector ? document.getElementById(this.options.selector) : this.options.selector instanceof HTMLElement || "undefined" != typeof ShadowRoot && this.options.selector instanceof ShadowRoot ? this.options.selector : document.body)) throw "Root element is not defined"; var i = o.defaults.oldestFirst ? t.firstChild : t.lastChild; return t.insertBefore(this.toastElement, i), o.reposition(), this.options.duration > 0 && (this.toastElement.timeOutValue = window.setTimeout(function () { this.removeElement(this.toastElement) }.bind(this), this.options.duration)), this }, hideToast: function () { this.toastElement.timeOutValue && clearTimeout(this.toastElement.timeOutValue), this.removeElement(this.toastElement) }, removeElement: function (t) { t.className = t.className.replace(" on", ""), window.setTimeout(function () { this.options.node && this.options.node.parentNode && this.options.node.parentNode.removeChild(this.options.node), t.parentNode && t.parentNode.removeChild(t), this.options.callback.call(t), o.reposition() }.bind(this), 400) } }, o.reposition = function () { for (var t, o = { top: 15, bottom: 15 }, i = { top: 15, bottom: 15 }, e = { top: 15, bottom: 15 }, n = document.getElementsByClassName("toastify"), a = 0; a < n.length; a++) { t = !0 === s(n[a], "toastify-top") ? "toastify-top" : "toastify-bottom"; var l = n[a].offsetHeight; t = t.substr(9, t.length - 1); (window.innerWidth > 0 ? window.innerWidth : screen.width) <= 360 ? (n[a].style[t] = e[t] + "px", e[t] += l + 15) : !0 === s(n[a], "toastify-left") ? (n[a].style[t] = o[t] + "px", o[t] += l + 15) : (n[a].style[t] = i[t] + "px", i[t] += l + 15) } return this }, o.lib.init.prototype = o.lib, o }));
//# sourceMappingURL=/sm/e1ebbfe1bf0b0061f0726ebc83434e1c2f8308e6354c415fd05ecccdaad47617.map
//==========================================: 预设数据
const d = document;
const _Log = console.log;
const querySelector = (sel) => { return d.querySelector(sel) }
const getActElm = () => { return document.activeElement.nodeName }
// 设定选择器:
const player = '#bilibili-player',
sel_ctrl = `${player} .bpx-player-control-wrap`,
sel_Rate = `${sel_ctrl} .bpx-player-ctrl-playbackrate > ul`,
sel_Webv = `${sel_ctrl} .bpx-player-ctrl-web`,
sel_Wide = `${sel_ctrl} .bpx-player-ctrl-wide`,
sel_tooltip = `${player} .bpx-player-tooltip-area`
;
const cls_playBtn = ".bpx-player-ctrl-play", // 播放暂停按钮
cls_videoEl = ".bpx-player-container", // 这是播放器容器
cls_pauseSign = "bpx-state-paused" // 这是暂停标志
;
/*! 只做了一点删减。
* Toastify css 1.12.0
* https://github.com/apvarun/toastify-js
* @license MIT licensed
*
* Copyright (C) 2018 Varun A P
*/
GM_addStyle(`.toastify{color:#fff;display:inline-block;
position:fixed;opacity:0;cursor:pointer;text-decoration:none;
max-width:calc(50% - 20px);z-index:2147483647}
.toastify.on{opacity:1}
.toast-close{background:0 0;border:0;color:#fff;cursor:pointer;
font-family:inherit;font-size:1em;opacity:.4;padding:0 5px}
.toastify-right{right:15px}
.toastify-left{left:15px}
.toastify-top{top:-150px}
.toastify-bottom{bottom:-150px}
.toastify-rounded{border-radius:25px}
.toastify-avatar{width:1.5em;height:1.5em;margin:-7px 5px;
border-radius:2px}
.toastify-center{margin-left:auto;margin-right:auto;left:0;
right:0;max-width:fit-content;max-width:-moz-fit-content}
@media only screen and (max-width:360px){
.toastify-left,.toastify-right{margin-left:auto;margin-right:auto;
left:0;right:0;max-width:fit-content}}
`);
// 个性化样式
GM_addStyle(`
.toast-info {
font-size: small;
font-family: SongTi;
padding: 8px 10px !important;
border-radius: 5em !important;
box-shadow: -2px 4px 6px 1px #00000073 !important;
transition: all .2s cubic-bezier(.215, .61, .355, 1) !important;
background: linear-gradient(90deg, #6190E8 0%, #A7BFE8 100%) !important;
}
.toast-guide {
color: black !important;
font-size: medium;
font-weight: bold;
padding: 12px 20px !important;
background: linear-gradient(270deg, #f4c4f3 0%, #fc67fa 100%) !important;
}
.toast-close {
color: black !important;
}
`); // Toast class
function _infoToast(msg, duration) {
Toastify({
text: msg,
duration: duration,
className: "toast-info",
close: true, gravity: "top", position: "right",
stopOnFocus: true, oldestFirst: true,
offset: { x: 10, y: 60 }
}).showToast();
}
function _guideToast(text, duration, close, callback) {
let tst = Toastify({
text,
duration,
className: "toast-info toast-guide",
close, gravity: "top", position: "right",
stopOnFocus: true, oldestFirst: true,
offset: { x: 10, y: 60 },
onClick
});
function onClick() {
if (callback) callback();
tst.hideToast();
}
tst.showToast();
}
function runToast(msg) {
_infoToast(msg, 1500)
}
function tipToast(msg) {
_infoToast(msg, 3000)
}
function pinToast(msg) {
_infoToast(msg, -1)
}
function firstSettingToast() {
_guideToast('First 点击此处', -1, false,
function () { console.log('First setting') }
)
}
//==========================================
//==========================================
//==========================================
//==========================================: Script执行
(function () {
'use strict';
// let styleSheets = ['toastifyCSS']
// styleSheets.forEach(url => {
// GM_addStyle(GM_getResourceText(url))
// })
if (location.href.match(/space.bilibili.com/) &&
location.pathname.endsWith("favlist")) {
_Log('Favlist modifing');
do_view_adjusting();
}
else if (location.href.match(/bilibili.com\/video|list\/watchlater/)) {
_Log('Player modifing');
do_keybindings();
}
if (DEBUG) window.addEventListener('keydown', (event) => {
if (event.key === '/') eval(prompt("Run script:"));
if (event.key === 'F2') {
firstSettingToast()
pinToast('好好好这么玩是吧')
}
})
})();
//==========================================
//==========================================
//==========================================
//==========================================: Script主体
/**
*
* @description 个人空间——⭐收藏:视图调整
*
*/
function do_view_adjusting() {
GM_addStyle(`
#page-fav .fav-main{width:800px!important}
#page-fav .fav-main .small-item{width:170px!important}
#page-fav .fav-main .small-item:nth-child(5n){margin-right:inherit!important}
#page-fav .fav-main .fav-action-bottom .fav-action-fixtop{width:800px!important;}
#page-fav .fav-sidenav{width:300px!important}
#page-fav .fav-sidenav .text{line-height:33px!important;width:180px!important}
#page-fav .fav-sidenav .fav-list-container{max-height:none!important}
@media (min-width:1420px){
#page-fav .fav-main{width:980px}
}
/* #page-fav .fav-list>li:nth-child(odd){background:whitesmoke!important;} */
#page-fav .fav-sidenav > div:nth-child(2){background:aliceblue!important;}
#page-fav .modal-wrapper .target-favlist{max-height:80vh!important;}
#page-fav .modal-wrapper .target-favitem{height:unset!important;margin:.5em!important;}
#page-fav .modal-wrapper .fav-meta{display:flex!important;}
#page-fav .modal-wrapper .fav-meta .fav-state{margin-left:20px!important;}
`)
setTimeout(() => {
querySelector("#page-fav div.fav-sidenav > " +
"div:nth-child(2) > div.favlist-title").click();
}, 1000);
}
/**
*
* @description 视频播放器:加快捷键
*
*/
function do_keybindings() {
if (DEBUG) console.clear(); // 开发行为
// 阻止空格滚屏:
d.body.addEventListener('keydown', (e) => {
if (e.key === ' ') e.preventDefault();
});
//
//######################################### 监视加载
//#########################################
//
// 加载好了,开始正事。
function doModify(obs) {
if (doModify.done) return;
if (querySelector(sel_Wide)) {
if (DEBUG && !querySelector(cls_videoEl).classList
.contains(cls_pauseSign)) {
// 开发行为 // 没有暂停就让他暂停
querySelector(cls_playBtn).click(); _Log("Debug: pause")
}
obs.disconnect();
clearInterval(Tick);
doModify.done = true;
// 修改播放器
modifyPlayer();
_Log('%c终于加载好了~', "color:lime"); tipToast("按键设定完毕~");
// 启动提示监视器
RenamerObs.observe(
querySelector(sel_tooltip),
{ childList: true }
);
}
}
doModify.done = false;
// 定义加载行为监视器
let LoadObs = new MutationObserver((list, obs) => {
// _Log("is same", mobs == muobs, mobs, muobs); // True
doModify(LoadObs);
});
// 开始监视加载行为
LoadObs.observe(d.body, { childList: true });
// 应对‘稍后再看’这种无行为的页面
let Tick = setInterval(() => { doModify(LoadObs) }, 1000);
//
//######################################### 干正事
//#########################################
//
let lastWideT = '宽屏模式', lastWebvT = '网页全屏';
// obs: 给 宽屏 和 页面全屏 按钮 加提示
const RenamerObs = new MutationObserver((mlist) => {
if (mlist.length && mlist[0].type == 'childList') {
// _Log(mlist);
// mlist.forEach((e) => {
// if (e.addedNodes.length > 0) { e.addedNodes.forEach((i) => {
// _Log('Add ', i.lastChild.textContent) })}
// if (e.removedNodes.length > 0) { e.removedNodes.forEach((i) => {
// _Log('\tRemove ', i.lastChild.textContent) })}
// });
let wideTip = null;
let webvTip = null;
let wide_texts = ['宽屏模式', '退出宽屏'];
let webv_texts = ['网页全屏', '退出网页全屏'];
if (mlist[0].addedNodes.length > 0) {
wideTip = mlist.find(node => {
return wide_texts.includes(node.addedNodes[0].lastChild.textContent)
}).addedNodes[0].lastChild;
webvTip = mlist.find(node => {
return webv_texts.includes(node.addedNodes[0].lastChild.textContent)
}).addedNodes[0].lastChild;
// _Log(wideTip, webvTip);
if (lastWideT != wideTip.textContent) {
lastWideT = wideTip.textContent;
}
if (lastWebvT != webvTip.textContent) {
lastWebvT = webvTip.textContent;
}
wideTip.textContent += ' (h)';
webvTip.textContent += ' (g)';
}
}
})
// 修改播放器的函数
function modifyPlayer() {
const btnWebv = querySelector(sel_Webv),
btnWide = querySelector(sel_Wide),
btnRate = querySelector(sel_Rate),
btnRates = Array.from(btnRate.children);
const rateK2R = new Map([
['1', '1'],
['2', '1.25'],
['3', '1.5'],
['4', '2'],
['5', '0.5'],
['6', '0.75']
]), rateR2K = new Map();
rateK2R.forEach((r, k) => { rateR2K.set(r, k) });
const rate = (k) => {
let btn = btnRates.find(e => {
return e.dataset.value == rateK2R.get(k)
})
if (btn) { btn.click(); runToast(btn.dataset.value + ' 倍速~') }
}
window.addEventListener('keyup', (e) => {
if (['TEXTAREA', 'INPUT'].indexOf(getActElm()) > -1) return; // 输入时不反应。
var k = e.key;
if (isNaN(k) || !k.trim().length) switch (k) {
case 'h': btnWide.click(); runToast(lastWideT); break;
case 'g': btnWebv.click(); runToast(lastWebvT); break;
default:
} else {
rate(k)
}
});
// 给倍速备注按键
btnRate.style.cssText = 'text-align:end;width:100px;padding-right:8px;';
btnRates.map(e => { e.innerHTML += `(按${rateR2K.get(e.dataset.value)})` })
if (autoWide) btnWide.click();
}
}