// ==UserScript==
// @name 【移动端】bilibili优化
// @namespace https://github.com/WhiteSevs/TamperMonkeyScript
// @version 2025.7.12.20
// @author WhiteSevs
// @description 阻止跳转App、App端推荐视频流、解锁视频画质(番剧解锁需配合其它插件)、美化显示、去广告等
// @license GPL-3.0-only
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAACjBJREFUeF7tnX+MHGUZx7/PbFsr3O3eNWBTaQu1RhNQEv7CBKNtQsBQTRDSJiYabNBaerP2bmcL8pcHiZH2du6u7Fx/SCmESGLQSP+QQk0aMAElMTGmsURQCQSLqcG7222ttb2dR8a700u7e/u+8850d2ae/Xee55nn+T6ffWd29p33Jcgn0wpQpquX4iEAZBwCAUAAyLgCGS9fRgABIOMKZLx8GQEEgIwrkPHyZQQQADKuQMbLlxFAAMi4Ahkvv+MjwJQ9MpyDtQ6gtT7xn5n9U/3eruE09qUba+0oALXi6DEw39Gk2Uct4l291fIbaQDhTLFyo880AuCuS+sh4GTecz7TqTo7BkDddv/AwE2tCg+EIeItSYcgaD4zPbdYrSD6ZaFaurMTEHQEgGAotGB9v13BSYdAqflzIvjwH+nEpa8jAEwX3SeI8a12AATHkwqBTvPn6nwm7zn3qWgSpU1HAKjboy8zeINqIUmDQLf5swDQK3mvtFFVk6jsOgKAzggwX2hSIAjT/MyNAKr3AM3umLv5xjBs84M6M3UPEBRcs90Xmv0saje0detIYNJ8AEcLnrOpXe1xHO/IJSAoxESwboMgybV0DIC0QJDk5s/de8QxsKjHTLKASc59wc21erPiskyikEnMuVn/OnoJWJhQkgRNUq7tvrRdA0BS7gnS1PyuuAe4lNBuFribc2v3TW91vKtGgPkku1HobswpbNMX+nUlAN12OUhr87vyEtBtN4Zpbv4VAWDadjdbwGYG1oOxAoQVAPJRDF8pjFEHYxKESYDfAvBMwSu/GGedsVwCzu4cv7nhN7YRcA8zVsVZQNpjM/AeAUdyudyhnr2DJ6KuN1IApgYq1xORbQE2A8ujTjbL8Qg47wMeM3v9E+V3o9IiMgBqtvt1ABUAK6NKTuI0VeA0QI8WvNK+KPSJBIBaccQGW9UoEpIYagoQ4XC+6tyvZt3ayhiAsP/rmyYu/v+dR3a8UHVuN9HCCICa7R4D0Gxev0lO4qungFfwnKKey/+tQwNQH3APMOE7YU8sftEpYDKdLBQAZ+zR7T54f3QlSCRTBSzQA71e6YBuHG0A/r5juGe51fv6om+66GYh9sYKBNPkzvtnPvexfcNndYJpAzBtV75HoB/qnGShbfBgwwL9Jax/mv2YeS0InwhbI4Mf7vPKj+n4awEwue2xgrVs6UkCrtM5CYDXCNiTW2L99urxob9p+mbKfGpwrC/n+zfCpx/ovDwTiMTAKTQufLZv/8NTqqJpAVArjmwCW79QDT6bFD/U55X36PiI7awCZ4rumM8Y1NGDgS19nvNTVR8tAOq2u5uBB1WD+8Tl/mrZVbUXu8sVCP5MI+A5VW0I9KO8V1L+daYFQG2g8kcQfVolGSYc6qs631axFZvFFajZbvCU1VbU6e2C56xXtFVfLJqHn1pe/2DyX6qBeam1vm9s6G1Ve7FrrcC5He6aCxbeIKBHRaf8NSs+SsNbz6vYKo8A/xwcWzUz47+vErSTrzop5pc4s7rt/oqBL6gkvmSJ9XHVm21lAOaWOTmpkgATtvdVnYMqtmKjpoDODaFFfJPqyirKANTtsdsY/qsq6frwN/Z7u15RsRUbNQWm7MrdFuh5FWuC9fm8N/Samq2KFYApe2SDBetlFXMBQEUlPZu49FceAeJKQE+G7FrHpb8AkBCmBICENCquNAWAuJRNSFwBICGNiitNASAuZRMSN9MAnCs+vnrG//caEE31XtU4RXseOtONfftH8fH8MlxY7fvce7GR++s1B0qnosozkwAEj58bDT7IzF9ZKKTJHLioGnJpnLrtlhkIFoRe8KHfUM7amt87+KbpeTMHwFTRvcVi/K6VcAw82ec5SsvNmorfzn/adg8R0HKOvj9jres/MPROuziLHc8UAO9vG77q6mU9JwBq87cmf6nglYOp6R371OzKnQC9tGgCjF/nr12zkYa3XAibaKYAmNrhftOy8FQ7sYjxdH7C2drOLs7jdXv0IIO3tTsHgx/o88ras3bn42YKAOW1hBnvFCacde3Ej/N4zXb/BOCTCuf4ccFzvqFg19QkUwDorCZe8Bzlx9lhxV/Mr2a7rBLXdDVwAaCFygLA5cLo/Bur/O2Ji8BmfZUR4HJV4tJfAFAZvxexkUtAE3F0hiAZAdQIlBFA7gFimZEllwC1L2BLK7kEyCVAfgZeyoDcA1z+rZDnABpDrfwMlJ+ByvsKyoOgFD4Imrbd3aT0FjL9vuCVbtEYXCI3rdmjrwN8a/vAvK/glQfa2zW3yNTPwGnbvZeAn7UTyyKM91adoXZ2cR6vD4xWmNhpew72v1aY2PWTtnYtDDIFQKBB3XafZ+DuxQS7OEOro5x2FaY5Su9MMp4tTDjBSqqhP5kDYO5t5GBOQKtt1Yc/vP4/ElrRCB3b7IR6LJfL3dezd/C0ySkzB8C8WHV79H6Av8zABoDeJMarZPmHVd9+NRFdx/eD7aPXfWQpl32mDQCvBXCMQMfzXulJnTitbDMLQBTipSGGAJCGLhrUIAAYiJcGVwEgDV00qEEAMBAvDa4CQBq6aFCDAGAgXhpcBYA0dNGgBgHAQLw0uAoAaeiiQQ0CgIF4aXAVANLQRYMaBAAD8dLg2nEAdJaKBWNTYcI5mgbhu6UGHQBiWSpWaeLD/9TiUsErj3WLeGnIo1Z0vwvGXpVaYlksWme5+A/3FHoi7zltF01QKUZsZhXQmSkdy3LxOhtGENGJRo6+2D8+NC0NNFegPuB+ignKC03FsmFEUIbOljEE+nneK91rXr5EmLYr+wm0XVGJeLaMmR2GNDeNAn+13ysfUUxczJooUCuO2mBW3pk93k2jQmwb5/vY2r/PeVq6q6/AtF15kEC7dTxj3TYu7MaRwWpePuHoxXMzL117uDtX+dQROU7bszvHV/qNxq1z2/PdpnOu2DeODJIx3To2eElSp6gs2TLzDSDcELbm2LeODRKTzaPDtidevyu2eXRQhmwfH28zw0S/YtvHzydXH3APMEF5i9IwRYmPmgImi2crLxHTLJWa7Qbr9N6hlqZYxaSAV/CcYtjYRgAEJ63Z7gsA7gqbgPgZKEA4Xqg6txtEUN87eLGT1IojNthSflhhkrD4zipAhMP5qtNyiXpVnYxHgPkT1Ww3eP25AmCl6snFLpQCpwF6tOCV9oXyvsQpMgCCuFMDleuJyLYAm4HlUSQoMea+8cB5H/CY2eufKL8blS6RAjCf1Nmd4zc3/MY2Au5hxqqoks1iHAbeI+BILpc71LN38ETUGsQCwMIkp213swVsZmA9GCtAWAEgH3UhKYlXB2MShEmA3wLwTMErvxhnbbEDEGfyEttcAQHAXMNERxAAEt0+8+QFAHMNEx1BAEh0+8yTFwDMNUx0BAEg0e0zT14AMNcw0REEgES3zzx5AcBcw0RHEAAS3T7z5AUAcw0THUEASHT7zJMXAMw1THQEASDR7TNPXgAw1zDREf4DSPKG2yZqlokAAAAASUVORK5CYII=
// @supportURL https://github.com/WhiteSevs/TamperMonkeyScript/issues
// @match *://m.bilibili.com/*
// @match *://live.bilibili.com/*
// @match *://www.bilibili.com/read/*
// @match *://www.bilibili.com/h5/comment/*
// @require https://fastly.jsdelivr.net/gh/WhiteSevs/TamperMonkeyScript@86be74b83fca4fa47521cded28377b35e1d7d2ac/lib/CoverUMD/index.js
// @require https://fastly.jsdelivr.net/gh/WhiteSevs/TamperMonkeyScript@86be74b83fca4fa47521cded28377b35e1d7d2ac/lib/QRCode/index.umd.js
// @require https://fastly.jsdelivr.net/npm/@whitesev/utils@2.7.0/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/@whitesev/domutils@1.5.11/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/@whitesev/pops@2.1.13/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/qmsg@1.3.8/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/viewerjs@1.11.7/dist/viewer.min.js
// @require https://fastly.jsdelivr.net/npm/md5@2.3.0/dist/md5.min.js
// @require https://fastly.jsdelivr.net/npm/flv.js@1.6.2/dist/flv.js
// @require https://fastly.jsdelivr.net/npm/artplayer@5.2.3/dist/artplayer.js
// @require https://fastly.jsdelivr.net/gh/WhiteSevs/ArtPlayer@aca6fb3795ea03b9614cd32613e2588e60470524/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.js
// @resource ViewerCSS https://fastly.jsdelivr.net/npm/viewerjs@1.11.7/dist/viewer.min.css
// @connect *
// @connect m.bilibili.com
// @connect www.bilibili.com
// @connect api.bilibili.com
// @connect app.bilibili.com
// @connect passport.bilibili.com
// @connect hdslb.com
// @connect aisubtitle.hdslb.com
// @grant GM_addStyle
// @grant GM_deleteValue
// @grant GM_getResourceText
// @grant GM_getValue
// @grant GM_info
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_unregisterMenuCommand
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @run-at document-start
// @downloadURL none
// ==/UserScript==
(a=>{function e(n){if(typeof n!="string")throw new TypeError("cssText must be a string");let p=document.createElement("style");return p.setAttribute("type","text/css"),p.innerHTML=n,document.head?document.head.appendChild(p):document.body?document.body.appendChild(p):document.documentElement.childNodes.length===0?document.documentElement.appendChild(p):document.documentElement.insertBefore(p,document.documentElement.childNodes[0]),p}if(typeof GM_addStyle=="function"){GM_addStyle(a);return}e(a)})(' @charset "UTF-8";.m-video2-awaken-btn,.openapp-dialog{display:none!important}.m-head .launch-app-btn.m-nav-openapp,.m-head .launch-app-btn.home-float-openapp,.m-head m-open-app{display:none!important}.m-home .launch-app-btn.home-float-openapp{display:none!important}.m-space .launch-app-btn.m-space-float-openapp,.m-space .launch-app-btn.m-nav-openapp,.m-space m-open-app:has(>.m-fixed-openapp){display:none!important}#app .video .launch-app-btn.m-video-main-launchapp:has([class^=m-video2-awaken]),#app .video .launch-app-btn.m-nav-openapp,#app .video .mplayer-widescreen-callapp,#app .video .launch-app-btn.m-float-openapp,#app .video .m-video-season-panel .launch-app-btn .open-app{display:none!important}#app.LIVE .open-app-btn.bili-btn-warp{display:none!important}#app .m-dynamic .launch-app-btn.m-nav-openapp,#app .m-dynamic .dynamic-float-openapp.dynamic-float-btn,#app .m-dynamic m-open-app:has(>.m-fixed-openapp){display:none!important}#app .m-opus .float-openapp.opus-float-btn,#app .m-opus .v-switcher .launch-app-btn.list-more,#app .m-opus .opus-nav .launch-app-btn.m-nav-openapp,#app .m-opus .m-navbar .m-nav-openapp,#app .m-opus m-open-app.m-open-app.fixed-openapp{display:none!important}#app .topic-detail .launch-app-btn.m-nav-openapp,#app .topic-detail .launch-app-btn.m-topic-float-openapp{display:none!important}#app.main-container bili-open-app.btn-download{display:none!important}#__next m-open-app[class^=TopBar_download],#__next m-open-app:has([class^=GoApp]){display:none!important}#__next m-open-app[class^=MainButton_btnWrap]{visibility:hidden!important}#app .read-app-main bili-open-app{display:none!important}#app .playlist>.open-app-wp{display:none!important}#app .playlist>.open-app-wp+div{padding-top:56.25%}html{--bili-color: #fb7299;--bili-color-rgb: 251, 114, 153} ');
(function (Qmsg, DOMUtils, Utils, pops, md5, Artplayer, artplayerPluginDanmuku, Viewer, flvjs) {
'use strict';
const BilibiliBeautifyCSS = '@charset "UTF-8";\r\n/* 主页 */\r\n#app .m-head {\r\n --bg-color: #f0f1f3;\r\n --bg-rever-color: #ffffff;\r\n --pd-width: 1.3333vmin;\r\n --bd-circle: 1.3333vmin;\r\n --card-height: 30vmin;\r\n --icon-font-size: 3.2vmin;\r\n --icon-text-font-size: 2.6vmin;\r\n --icon-font-margin-right: 3vmin;\r\n --title-font-size: 2.8vmin;\r\n background-color: var(--bg-color);\r\n}\r\n#app .m-head .m-home {\r\n background-color: var(--bg-color);\r\n}\r\n/* 美化视频卡片 */\r\n#app .m-head .video-list .card-box .v-card {\r\n background-color: var(--bg-rever-color);\r\n padding: 0px;\r\n margin: 0px;\r\n width: calc(50% - var(--pd-width) / 2);\r\n border-radius: var(--bd-circle);\r\n margin-top: var(--pd-width);\r\n display: grid;\r\n /* 视频封面区域 */\r\n}\r\n#app .m-head .video-list .card-box .v-card .card {\r\n background: var(--bg-rever-color);\r\n border-radius: unset;\r\n border-top-left-radius: var(--bd-circle);\r\n border-top-right-radius: var(--bd-circle);\r\n height: var(--card-height);\r\n}\r\n#app .m-head .video-list .card-box .v-card .card .count {\r\n display: flex;\r\n justify-content: safe flex-start;\r\n padding-right: 0;\r\n}\r\n#app .m-head .video-list .card-box .v-card .card .count .iconfont {\r\n font-size: var(--icon-text-font-size);\r\n}\r\n#app .m-head .video-list .card-box .v-card .card .count > span {\r\n font-size: var(--icon-text-font-size);\r\n margin-right: var(--icon-font-margin-right);\r\n}\r\n/* 视频标题区域 */\r\n#app .m-head .video-list .card-box .v-card .title {\r\n padding: 0;\r\n margin: var(--pd-width);\r\n font-size: var(--title-font-size);\r\n}\r\n/* 两列 => 左边的 */\r\n#app .m-head .video-list .card-box .v-card:nth-child(2n-1) {\r\n /*background-color: red;*/\r\n margin-right: calc(var(--pd-width) / 2);\r\n}\r\n/* 两列 => 右边的 */\r\n#app .m-head .video-list .card-box .v-card:nth-child(2n) {\r\n /*background-color: rebeccapurple;*/\r\n margin-left: calc(var(--pd-width) / 2);\r\n}\r\n';
const BilibiliRouter = {
/**
* 视频页面
* + /video/
*/
isVideo() {
return window.location.pathname.startsWith("/video/");
},
/**
* 番剧
* + /bangumi/
*/
isBangumi() {
return window.location.pathname.startsWith("/bangumi/");
},
/**
* 搜索
* + /search
*/
isSearch() {
return window.location.pathname.startsWith("/search");
},
/**
* 搜索结果页面
*
* + /search?keyword=xxx
*/
isSearchResult() {
let urlSearchParams = new URLSearchParams(window.location.search);
return this.isSearch() && urlSearchParams.has("keyword");
},
/**
* 直播
* + live.bilibili.com
*/
isLive() {
return window.location.hostname === "live.bilibili.com";
},
/**
* 专栏稿件
* + /opus
*/
isOpus() {
return window.location.pathname.startsWith("/opus");
},
/**
* 话题
* + /topic-detail
*/
isTopicDetail() {
return window.location.pathname.startsWith("/topic-detail");
},
/**
* 动态
* + /dynamic
*/
isDynamic() {
return window.location.pathname.startsWith("/dynamic");
},
/**
* 首页
* + /
* + /channel
*/
isHead() {
return window.location.pathname === "/" || window.location.pathname.startsWith("/channel");
},
/**
* 个人空间
* + /space
*/
isSpace() {
return window.location.pathname.startsWith("/space");
},
/**
* 播放列表
* + /playlist
*/
isPlayList() {
return window.location.pathname.startsWith("/playlist");
}
};
const BilibiliPCRouter = {
/**
* 桌面端
*/
isPC() {
return window.location.hostname === "www.bilibili.com";
},
/**
* 应该是动态?
* + /read/mobile-readlist/
* + /read/mobile?id=
*/
isReadMobile() {
return this.isPC() && window.location.pathname.startsWith("/read/mobile");
}
};
var _GM_deleteValue = /* @__PURE__ */ (() => typeof GM_deleteValue != "undefined" ? GM_deleteValue : void 0)();
var _GM_getResourceText = /* @__PURE__ */ (() => typeof GM_getResourceText != "undefined" ? GM_getResourceText : void 0)();
var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
var _GM_info = /* @__PURE__ */ (() => typeof GM_info != "undefined" ? GM_info : void 0)();
var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
var _GM_unregisterMenuCommand = /* @__PURE__ */ (() => typeof GM_unregisterMenuCommand != "undefined" ? GM_unregisterMenuCommand : void 0)();
var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
var _monkeyWindow = /* @__PURE__ */ (() => window)();
const KEY = "GM_Panel";
const ATTRIBUTE_INIT = "data-init";
const ATTRIBUTE_KEY = "data-key";
const ATTRIBUTE_DEFAULT_VALUE = "data-default-value";
const ATTRIBUTE_INIT_MORE_VALUE = "data-init-more-value";
const PROPS_STORAGE_API = "data-storage-api";
const PanelUISize = {
/**
* 一般设置界面的尺寸
*/
setting: {
get width() {
if (window.innerWidth < 550) {
return "88vw";
} else if (window.innerWidth < 700) {
return "550px";
} else {
return "700px";
}
},
get height() {
if (window.innerHeight < 450) {
return "70vh";
} else if (window.innerHeight < 550) {
return "450px";
} else {
return "550px";
}
}
},
/**
* 信息界面,一般用于提示信息之类
*/
info: {
get width() {
return window.innerWidth < 350 ? "350px" : "350px";
},
get height() {
return window.innerHeight < 250 ? "250px" : "250px";
}
}
};
class StorageUtils {
/** 存储的键名 */
storageKey;
listenerData;
/**
* 存储的键名,可以是多层的,如:a.b.c
*
* 那就是
* {
* "a": {
* "b": {
* "c": {
* ...你的数据
* }
* }
* }
* }
* @param key
*/
constructor(key) {
if (typeof key === "string") {
let trimKey = key.trim();
if (trimKey == "") {
throw new Error("key参数不能为空字符串");
}
this.storageKey = trimKey;
} else {
throw new Error("key参数类型错误,必须是字符串");
}
this.listenerData = new Utils.Dictionary();
}
/**
* 获取本地值
*/
getLocalValue() {
let localValue = _GM_getValue(this.storageKey);
if (localValue == null) {
localValue = {};
this.setLocalValue(localValue);
}
return localValue;
}
/**
* 设置本地值
* @param value
*/
setLocalValue(value) {
_GM_setValue(this.storageKey, value);
}
/**
* 设置值
* @param key 键
* @param value 值
*/
set(key, value) {
let oldValue = this.get(key);
let localValue = this.getLocalValue();
Reflect.set(localValue, key, value);
this.setLocalValue(localValue);
this.triggerValueChangeListener(key, oldValue, value);
}
/**
* 获取值
* @param key 键
* @param defaultValue 默认值
*/
get(key, defaultValue) {
let localValue = this.getLocalValue();
return Reflect.get(localValue, key) ?? defaultValue;
}
/**
* 获取所有值
*/
getAll() {
let localValue = this.getLocalValue();
return localValue;
}
/**
* 删除值
* @param key 键
*/
delete(key) {
let oldValue = this.get(key);
let localValue = this.getLocalValue();
Reflect.deleteProperty(localValue, key);
this.setLocalValue(localValue);
this.triggerValueChangeListener(key, oldValue, void 0);
}
/**
* 判断是否存在该值
*/
has(key) {
let localValue = this.getLocalValue();
return Reflect.has(localValue, key);
}
/**
* 获取所有键
*/
keys() {
let localValue = this.getLocalValue();
return Reflect.ownKeys(localValue);
}
/**
* 获取所有值
*/
values() {
let localValue = this.getLocalValue();
return Reflect.ownKeys(localValue).map(
(key) => Reflect.get(localValue, key)
);
}
/**
* 清空所有值
*/
clear() {
_GM_deleteValue(this.storageKey);
}
/**
* 监听值改变
* + .set
* + .delete
* @param key 监听的键
* @param callback 值改变的回调函数
*/
addValueChangeListener(key, callback) {
let listenerId = Math.random();
let listenerData = this.listenerData.get(key) || [];
listenerData.push({
id: listenerId,
key,
callback
});
this.listenerData.set(key, listenerData);
return listenerId;
}
/**
* 移除监听
* @param listenerId 监听的id或键名
*/
removeValueChangeListener(listenerId) {
let flag = false;
for (const [key, listenerData] of this.listenerData.entries()) {
for (let index = 0; index < listenerData.length; index++) {
const value = listenerData[index];
if (typeof listenerId === "string" && value.key === listenerId || typeof listenerId === "number" && value.id === listenerId) {
listenerData.splice(index, 1);
index--;
flag = true;
}
}
this.listenerData.set(key, listenerData);
}
return flag;
}
/**
* 主动触发监听器
* @param key 键
* @param oldValue (可选)旧值
* @param newValue (可选)新值
*/
triggerValueChangeListener(key, oldValue, newValue) {
if (!this.listenerData.has(key)) {
return;
}
let listenerData = this.listenerData.get(key);
for (let index = 0; index < listenerData.length; index++) {
const data2 = listenerData[index];
if (typeof data2.callback === "function") {
let value = this.get(key);
let __newValue;
let __oldValue;
if (typeof oldValue !== "undefined" && arguments.length >= 2) {
__oldValue = oldValue;
} else {
__oldValue = value;
}
if (typeof newValue !== "undefined" && arguments.length > 2) {
__newValue = newValue;
} else {
__newValue = value;
}
data2.callback(key, __oldValue, __newValue);
}
}
}
}
const PopsPanelStorageApi = new StorageUtils(KEY);
const PanelContent = {
$data: {
/**
* @private
*/
__contentConfig: null,
get contentConfig() {
if (this.__contentConfig == null) {
this.__contentConfig = new utils.Dictionary();
}
return this.__contentConfig;
}
},
/**
* 设置所有配置项,用于初始化默认的值
*
* 如果是第一组添加的话,那么它默认就是设置菜单打开的配置
* @param configList 配置项
*/
addContentConfig(configList) {
if (!Array.isArray(configList)) {
configList = [configList];
}
let index = this.$data.contentConfig.keys().length;
this.$data.contentConfig.set(index, configList);
},
/**
* 获取所有的配置内容,用于初始化默认的值
*/
getAllContentConfig() {
return this.$data.contentConfig.values().flat();
},
/**
* 获取配置内容
* @param index 配置索引
*/
getConfig(index = 0) {
return this.$data.contentConfig.get(index) ?? [];
},
/**
* 获取默认左侧底部的配置项
*/
getDefaultBottomContentConfig() {
return [
{
id: "script-version",
title: `版本:${_GM_info?.script?.version || "未知"}`,
isBottom: true,
forms: [],
clickFirstCallback(event, rightHeaderElement, rightContainerElement) {
let supportURL = _GM_info?.script?.supportURL || _GM_info?.script?.namespace;
if (typeof supportURL === "string" && utils.isNotNull(supportURL)) {
window.open(supportURL, "_blank");
}
return false;
}
}
];
}
};
const PanelMenu = {
$data: {
__menuOption: [
{
key: "show_pops_panel_setting",
text: "⚙ 设置",
autoReload: false,
isStoreValue: false,
showText(text) {
return text;
},
callback: () => {
Panel.showPanel(PanelContent.getConfig(0));
}
}
],
get menuOption() {
return this.__menuOption;
}
},
init() {
this.initExtensionsMenu();
},
/**
* 初始化菜单项
*/
initExtensionsMenu() {
if (!Panel.isTopWindow()) {
return;
}
GM_Menu.add(this.$data.menuOption);
},
/**
* 添加菜单项
* @param option 菜单配置
*/
addMenuOption(option) {
if (!Array.isArray(option)) {
option = [option];
}
this.$data.menuOption.push(...option);
},
/**
* 更新菜单项
* @param option 菜单配置
*/
updateMenuOption(option) {
if (!Array.isArray(option)) {
option = [option];
}
option.forEach((optionItem) => {
let findIndex = this.$data.menuOption.findIndex((it) => {
return it.key === optionItem.key;
});
if (findIndex !== -1) {
this.$data.menuOption[findIndex] = optionItem;
}
});
},
/**
* 获取菜单项
* @param [index=0] 索引
*/
getMenuOption(index = 0) {
return this.$data.menuOption[index];
},
/**
* 删除菜单项
* @param [index=0] 索引
*/
deleteMenuOption(index = 0) {
this.$data.menuOption.splice(index, 1);
}
};
const Panel = {
/** 数据 */
$data: {
/**
* @private
*/
__configDefaultValueData: null,
/**
* @private
*/
__onceExecMenuData: null,
/**
* @private
*/
__onceExecData: null,
/**
* @private
*/
__panelConfig: {},
$panel: null,
/**
* 菜单项的默认值
*/
get configDefaultValueData() {
if (this.__configDefaultValueData == null) {
this.__configDefaultValueData = new utils.Dictionary();
}
return this.__configDefaultValueData;
},
/**
* 成功只执行了一次的项
*/
get onceExecMenuData() {
if (this.__onceExecMenuData == null) {
this.__onceExecMenuData = new utils.Dictionary();
}
return this.__onceExecMenuData;
},
/**
* 成功只执行了一次的项
*/
get onceExecData() {
if (this.__onceExecData == null) {
this.__onceExecData = new utils.Dictionary();
}
return this.__onceExecData;
},
/** 脚本名,一般用在设置的标题上 */
get scriptName() {
return SCRIPT_NAME;
},
/**
* pops.panel的默认配置
*/
get panelConfig() {
return this.__panelConfig;
},
set panelConfig(value) {
this.__panelConfig = value;
},
/** 菜单项的总值在本地数据配置的键名 */
key: KEY,
/** 菜单项在attributes上配置的菜单键 */
attributeKeyName: ATTRIBUTE_KEY,
/** 菜单项在attributes上配置的菜单默认值 */
attributeDefaultValueName: ATTRIBUTE_DEFAULT_VALUE
},
init() {
this.initContentDefaultValue();
PanelMenu.init();
},
/** 判断是否是顶层窗口 */
isTopWindow() {
return _unsafeWindow.top === _unsafeWindow.self;
},
/** 初始化菜单项的默认值保存到本地数据中 */
initContentDefaultValue() {
const initDefaultValue = (config) => {
if (!config.attributes) {
return;
}
if (config.type === "button" || config.type === "forms" || config.type === "deepMenu") {
return;
}
let needInitConfig = {};
let key = config.attributes[ATTRIBUTE_KEY];
if (key != null) {
needInitConfig[key] = config.attributes[ATTRIBUTE_DEFAULT_VALUE];
}
let __attr_init__ = config.attributes[ATTRIBUTE_INIT];
if (typeof __attr_init__ === "function") {
let __attr_result__ = __attr_init__();
if (typeof __attr_result__ === "boolean" && !__attr_result__) {
return;
}
}
let initMoreValue = config.attributes[ATTRIBUTE_INIT_MORE_VALUE];
if (initMoreValue && typeof initMoreValue === "object") {
Object.assign(needInitConfig, initMoreValue);
}
let needInitConfigList = Object.keys(needInitConfig);
if (!needInitConfigList.length) {
log$1.warn(["请先配置键", config]);
return;
}
needInitConfigList.forEach((__key) => {
let __defaultValue = needInitConfig[__key];
this.setDefaultValue(__key, __defaultValue);
});
};
const loopInitDefaultValue = (configList) => {
for (let index = 0; index < configList.length; index++) {
let configItem = configList[index];
initDefaultValue(configItem);
let childForms = configItem.forms;
if (childForms && Array.isArray(childForms)) {
loopInitDefaultValue(childForms);
}
}
};
const contentConfigList = [...PanelContent.getAllContentConfig()];
for (let index = 0; index < contentConfigList.length; index++) {
let leftContentConfigItem = contentConfigList[index];
if (!leftContentConfigItem.forms) {
continue;
}
const rightContentConfigList = leftContentConfigItem.forms;
if (rightContentConfigList && Array.isArray(rightContentConfigList)) {
loopInitDefaultValue(rightContentConfigList);
}
}
},
/**
* 设置初始化使用的默认值
*/
setDefaultValue(key, defaultValue) {
if (this.$data.configDefaultValueData.has(key)) {
log$1.warn("请检查该key(已存在): " + key);
}
this.$data.configDefaultValueData.set(key, defaultValue);
},
/**
* 设置值
* @param key 键
* @param value 值
*/
setValue(key, value) {
PopsPanelStorageApi.set(key, value);
},
/**
* 获取值
* @param key 键
* @param defaultValue 默认值
*/
getValue(key, defaultValue) {
let localValue = PopsPanelStorageApi.get(key);
if (localValue == null) {
if (this.$data.configDefaultValueData.has(key)) {
return this.$data.configDefaultValueData.get(key);
}
return defaultValue;
}
return localValue;
},
/**
* 删除值
* @param key 键
*/
deleteValue(key) {
PopsPanelStorageApi.delete(key);
},
/**
* 判断该键是否存在
* @param key 键
*/
hasKey(key) {
return PopsPanelStorageApi.has(key);
},
/**
* 监听调用setValue、deleteValue
* @param key 需要监听的键
* @param callback
*/
addValueChangeListener(key, callback) {
let listenerId = PopsPanelStorageApi.addValueChangeListener(
key,
(__key, __newValue, __oldValue) => {
callback(key, __oldValue, __newValue);
}
);
return listenerId;
},
/**
* 移除监听
* @param listenerId 监听的id
*/
removeValueChangeListener(listenerId) {
PopsPanelStorageApi.removeValueChangeListener(listenerId);
},
/**
* 主动触发菜单值改变的回调
* @param key 菜单键
* @param newValue 想要触发的新值,默认使用当前值
* @param oldValue 想要触发的旧值,默认使用当前值
*/
triggerMenuValueChange(key, newValue, oldValue) {
PopsPanelStorageApi.triggerValueChangeListener(key, oldValue, newValue);
},
/**
* 移除已执行的仅执行一次的菜单
* @param key 键
*/
deleteExecMenuOnce(key) {
this.$data.onceExecMenuData.delete(key);
let flag = PopsPanelStorageApi.removeValueChangeListener(key);
return flag;
},
/**
* 移除已执行的仅执行一次的菜单
* @param key 键
*/
deleteOnceExec(key) {
this.$data.onceExecData.delete(key);
},
/**
* 执行菜单
*
* @param queryKey 键|键数组
* @param callback 执行的回调函数
* @param checkExec 判断是否执行回调
*
* (默认)如果想要每个菜单是`与`关系,即每个菜单都判断为开启,那么就判断它们的值&就行
*
* 如果想要任意菜单存在true再执行,那么判断它们的值|就行
*
* + 返回值都为`true`,执行回调,如果回调返回了
${data2.filter((it) => it != null).map((item) => {
return (
/*html*/
`
${item.key}
${item.value}
`
);
}).join("\n")}
`
),
mounted: async ($topWrap) => {
let $close = $topWrap.querySelector(
".art-player-video-statistics-close svg"
);
this.art.proxy($close, "click", (event) => {
event.stopPropagation();
event.stopImmediatePropagation();
event.preventDefault();
this.closeLayer();
});
}
};
}
/**
* 判断是否已经注册过layer
*/
isRegisterLayer() {
return this.$key.setting_name in this.art.layers;
}
/**
* 显示layer
* @param intervalUpdateInfo 是否定时刷新
*/
showLayer(intervalUpdateInfo) {
clearInterval(this.$data.intervalId);
let option = this.getLayerOption();
this.art.layers.add(option);
if (intervalUpdateInfo) {
this.unbindUpdateLayerEvent();
this.bindUpdateLayerEvent();
}
}
/**
* 更新layer
*/
updateLayer() {
let option = this.getLayerOption();
this.art.layers.update(option);
}
/**
* 绑定layer更新事件
*/
bindUpdateLayerEvent() {
this.art.on("play", this.updateLayerEvent_interval, this);
this.art.on("restart", this.updateLayerEvent_once, this);
this.art.on(
// @ts-ignore
"m4sAudio:loadedmetadata",
this.updateLayerEvent_once,
this
);
this.art.on("pause", this.updateLayerEvent_clear_interval, this);
this.art.on("video:ended", this.updateLayerEvent_clear_interval, this);
if (this.art.playing) {
this.updateLayerEvent_interval();
}
}
/**
* 取消绑定layer更新事件
*/
unbindUpdateLayerEvent() {
this.art.off("play", this.updateLayerEvent_interval);
this.art.off("restart", this.updateLayerEvent_once);
this.art.off(
// @ts-ignore
"m4sAudio:loadedmetadata",
this.updateLayerEvent_once
);
this.art.off("pause", this.updateLayerEvent_clear_interval);
this.art.off("video:ended", this.updateLayerEvent_clear_interval);
}
/**
* layer更新事件
*/
updateLayerEvent_interval() {
clearInterval(this.$data.intervalId);
this.$data.intervalId = setInterval(() => {
this.updateLayer();
}, 1500);
}
/**
* layer更新事件
*/
updateLayerEvent_once() {
this.updateLayer();
}
/**
* layer停止更新事件
*/
updateLayerEvent_clear_interval() {
clearInterval(this.$data.intervalId);
}
/**
* 关闭layer
*/
closeLayer() {
clearInterval(this.$data.intervalId);
this.art.layers.remove(this.$key.setting_name);
this.unbindUpdateLayerEvent();
}
/**
* 更新配置
*/
update(option) {
this.option = option;
}
}
const artplayerPluginVideoStatistics = (option) => {
return (art) => {
let videoStatistics = new VideoStatistics(art, option);
return {
name: videoStatistics.$key.plugin_KEY,
update(option2) {
videoStatistics.update(option2);
}
};
};
};
const ArtPlayerDanmakuCommonOption = () => {
return {
heatmap: false,
// 默认弹幕颜色,可以被单独弹幕项覆盖
color: "#FFFFFF",
// 默认弹幕模式: 0: 滚动,1: 顶部,2: 底部
mode: 0,
// 弹幕发射器挂载点, 默认为播放器控制栏中部
mount: void 0,
// 当播放器宽度小于此值时,弹幕发射器置于播放器底部
width: 800,
// 热力图数据
points: [],
// 弹幕载入前的过滤器
filter: (danmu) => danmu.text.length <= 100,
// 弹幕显示前的过滤器,返回 true 则可以发送
beforeVisible: () => true,
// 是否开启弹幕发射器
emitter: false,
// 弹幕输入框最大长度, 范围在[1 ~ 1000]
maxLength: 50,
// 输入框锁定时间,范围在[1 ~ 60]
lockTime: 3,
// 弹幕主题,支持 dark 和 light,只在自定义挂载时生效
theme: utils.isThemeDark() ? "dark" : "light"
// OPACITY: {}, // 不透明度配置项
// FONT_SIZE: {}, // 弹幕字号配置项
// MARGIN: {}, // 显示区域配置项
// SPEED: {}, // 弹幕速度配置项
// COLOR: [], // 颜色列表配置项
};
};
const generateVideoSelectSetting = (option) => {
let epList = option.epList || [];
if (epList.length === 1) {
let parentEp = epList[0];
return parentEp.pages.map((pageInfo) => {
return {
isDefault: pageInfo.cid === option.cid,
title: pageInfo.part,
aid: option.aid,
bvid: option.bvid,
cid: pageInfo.cid,
onSelect(selectOption, index) {
parentEp.cid = pageInfo.cid;
BilibiliVideoPlayer.updateArtPlayerVideoInfo(
{
aid: option.aid,
bvid: option.bvid,
cid: pageInfo.cid,
pic: pageInfo.first_frame || "",
title: pageInfo.part,
epList: option.epList || []
},
true
);
}
};
});
} else {
return epList.map((epInfo) => {
return {
isDefault: epInfo.aid === option.aid && epInfo.cid === option.cid,
title: GenerateArtPlayerEpTitle(epInfo.title),
aid: epInfo.aid,
bvid: epInfo.bvid,
cid: epInfo.cid,
onSelect(selectItem, index) {
BilibiliVideoPlayer.updateArtPlayerVideoInfo(
{
aid: epInfo.aid,
bvid: epInfo.bvid,
cid: epInfo.cid,
pic: epInfo.arc.pic,
title: epInfo.title,
epList: option.epList || []
},
true
);
}
};
});
}
};
const BilibiliVideoArtPlayer = {
$data: {
art: null,
/** 当前的配置项 */
currentOption: null
},
/**
* 重置环境变量
*/
resetEnv(isInit) {
if (isInit) {
Reflect.set(this.$data, "art", null);
}
Reflect.set(this.$data, "currentOption", null);
},
/**
* 初始化播放器
* @param option
*/
async init(option) {
this.resetEnv(true);
this.$data.currentOption = option;
const localArtDanmakuOption_KEY = "artplayer-video-danmaku-option";
const artPlayerDanmakuOptionHelper = new ArtPlayerDanmakuOptionHelper(
localArtDanmakuOption_KEY
);
const localArtDanmakuOption = artPlayerDanmakuOptionHelper.getLocalArtDanmakuOption();
const artOption = {
...ArtPlayerCommonOption(),
container: option.container,
/** 视频封面 */
poster: option.poster,
/** 自定义设置列表 */
settings: [],
plugins: [
artplayerPluginToast(),
artplayPluginQuality({
from: "video",
qualityList: option.quality
})
]
};
artOption.type = "mp4";
if (Panel.getValue("artplayer-plugin-video-danmaku-enable")) {
artOption.plugins.push(
artplayerPluginDanmuku({
...ArtPlayerDanmakuCommonOption(),
danmuku: option.danmukuUrl,
// 以下为非必填
// 弹幕持续时间,范围在[1 ~ 10]
speed: localArtDanmakuOption.speed,
// 弹幕上下边距,支持像素数字和百分比
margin: localArtDanmakuOption["margin"],
// 弹幕透明度,范围在[0 ~ 1]
opacity: localArtDanmakuOption["opacity"],
// 弹幕可见的模式
modes: localArtDanmakuOption["modes"],
// 弹幕字体大小,支持像素数字和百分比
fontSize: localArtDanmakuOption["fontSize"],
// 弹幕是否防重叠
antiOverlap: localArtDanmakuOption["antiOverlap"],
// 是否同步播放速度
synchronousPlayback: localArtDanmakuOption["synchronousPlayback"],
// 弹幕层是否可见
visible: localArtDanmakuOption["visible"],
// 手动发送弹幕前的过滤器,返回 true 则可以发送,可以做存库处理
beforeEmit(danmu) {
return new Promise((resolve) => {
console.log(danmu);
setTimeout(() => {
resolve(true);
}, 1e3);
});
}
})
);
}
if (Panel.getValue("artplayer-plugin-video-m4sAudioSupport-enable")) {
artOption.plugins.push(
artplayerPluginM4SAudioSupport({
from: "video",
audioList: option.audioList || []
})
);
}
if (Panel.getValue("artplayer-plugin-video-epChoose-enable")) {
artOption.plugins.push(
artplayerPluginEpChoose({
EP_LIST: generateVideoSelectSetting(option),
automaticBroadcast: true
})
);
}
if (Panel.getValue("artplayer-plugin-video-cc-subtitle-enable")) {
artOption.plugins.push(
artplayerPluginBilibiliCCSubTitle({
from: "video",
cid: option.cid,
aid: option.aid,
bvid: option.bvid
})
);
}
if (Panel.getValue("artplayer-plugin-video-toptoolbar-enable")) {
artOption.plugins.push(
artplayerPluginTopToolBar({
onlineInfoParams: {
aid: option.aid,
cid: option.cid,
bvid: option.bvid
},
title: option.videoTitle,
showWrap: true,
showTitle: true,
showOnlineTotal: true
})
);
}
if (Panel.getValue("artplayer-plugin-video-statistics-enable")) {
artOption.plugins.push(
artplayerPluginVideoStatistics({
data: []
})
);
}
if (Panel.getValue("bili-video-playerAutoPlayVideo")) {
artOption.muted = true;
artOption.autoplay = true;
}
this.$data.art = new Artplayer(artOption);
artPlayerDanmakuOptionHelper.onConfigChange(this.$data.art);
return this.$data.art;
},
/**
* 更新新的播放信息
* @param option
*/
async update(art, option) {
this.resetEnv(false);
this.$data.currentOption = option;
log$1.info(`更新新的播放信息`, option);
art.pause();
log$1.info(`暂停视频`);
art.currentTime = 0;
log$1.info(`重置播放进度`);
this.updatePluginInfo(art, option);
art.play();
log$1.info("播放");
},
/**
* 更新插件数据
* @param art
* @param option
*/
updatePluginInfo(art, option) {
let plugin_quality = art.plugins[ArtPlayer_PLUGIN_QUALITY_KEY];
plugin_quality.update({
from: "video",
qualityList: option.quality
});
log$1.info(`更新画质`, option.quality);
if (Panel.getValue("artplayer-plugin-video-danmaku-enable")) {
art.plugins.artplayerPluginDanmuku.config({
danmuku: option.danmukuUrl
});
art.plugins.artplayerPluginDanmuku.load();
log$1.info(`更新弹幕姬`, option.danmukuUrl);
}
if (Panel.getValue("artplayer-plugin-video-m4sAudioSupport-enable")) {
let plugin_m4sAudioSupport = art.plugins[ArtPlayer_PLUGIN_M4S_AUDIO_SUPPORT_KEY];
plugin_m4sAudioSupport.update({
from: "video",
audioList: option.audioList || []
});
log$1.info(`更新音频`, option.audioList);
}
if (Panel.getValue("artplayer-plugin-video-epChoose-enable")) {
let plugin_epChoose = art.plugins[ArtPlayer_PLUGIN_EP_CHOOSE_KEY];
plugin_epChoose.update({
EP_LIST: generateVideoSelectSetting(option),
automaticBroadcast: true
});
log$1.info(`更新选集信息`, option.epList);
}
if (Panel.getValue("artplayer-plugin-video-cc-subtitle-enable")) {
let plugin_bilibiliCCSubTitle = art.plugins[ArtPlayer_PLUGIN_BILIBILI_CC_SUBTITLE_KEY];
const subTitleOption = {
from: "video",
aid: option.aid,
bvid: option.bvid,
cid: option.cid
};
plugin_bilibiliCCSubTitle.update(subTitleOption);
log$1.info(`更新字幕`, subTitleOption);
}
if (Panel.getValue("artplayer-plugin-video-toptoolbar-enable")) {
let plugin_topToolBar = art.plugins[ArtPlayer_PLUGIN_TOP_TOOLBAR_KEY];
const topToolBarOption = {
showRight: true,
showRightFollow: true,
showWrap: true,
showTitle: true,
showOnlineTotal: true,
title: option.videoTitle,
onlineInfoParams: {
aid: option.aid,
cid: option.cid,
bvid: option.bvid
}
};
plugin_topToolBar.update(topToolBarOption);
log$1.info(`更新顶部标题`, topToolBarOption);
}
}
};
function handleDashVideoQualityInfo$1(dashInfo) {
let result = [];
dashInfo.video.forEach((dashVideoInfo) => {
if (!dashInfo.accept_quality.includes(dashVideoInfo.id)) {
return;
}
let findSupportFormat = dashInfo.support_formats.find(
(formatsItem) => formatsItem.quality === dashVideoInfo.id
);
let videoUrl = BilibiliCDNProxy.findBetterCDN(
dashVideoInfo.base_url,
dashVideoInfo.baseUrl,
dashVideoInfo.backup_url,
dashVideoInfo.backupUrl
);
videoUrl = BilibiliCDNProxy.replaceVideoCDN(videoUrl);
let qualityName = findSupportFormat?.new_description;
result.push({
name: qualityName,
url: videoUrl,
type: dashVideoInfo.mimeType || dashVideoInfo.mime_type,
id: dashVideoInfo.id,
quality: dashVideoInfo.id,
vip: false,
codecid: dashVideoInfo.codecid,
codecs: dashVideoInfo.codecs,
frameRate: dashVideoInfo.frameRate || dashVideoInfo.frame_rate,
bandwidth: dashVideoInfo.bandwidth
});
});
return result;
}
const GenerateArtPlayerOption$1 = async (option) => {
const audioInfo = [];
let qualityInfo = [];
if (Panel.getValue("bili-video-playType", "mp4") === "mp4") {
const videoPlayInfo = await BilibiliVideoApi.playUrl({
bvid: option.bvid,
cid: option.cid,
fnval: 1,
fnver: 0,
fourk: 1,
high_quality: 1,
qn: 127,
setPlatformHTML5: true
});
log$1.info(["视频播放地址信息:", videoPlayInfo]);
if (!videoPlayInfo) {
return;
}
let currentDurl = videoPlayInfo["durl"][0];
let findSupportFormat = videoPlayInfo.support_formats.find(
(formatsItem) => formatsItem.quality === videoPlayInfo.quality
);
let videoUrl = BilibiliCDNProxy.findBetterCDN(
currentDurl.url,
currentDurl.url || currentDurl.backup_url?.[0]
);
let qualityName = findSupportFormat?.new_description;
qualityInfo.push({
name: qualityName,
url: videoUrl,
type: "audio/mp4",
id: videoPlayInfo.quality,
codecid: videoPlayInfo.video_codecid,
quality: videoPlayInfo.quality,
vip: false,
codecs: "",
frameRate: "",
bandwidth: 0
});
} else {
const videoPlayInfo = await BilibiliVideoApi.playUrl({
bvid: option.bvid,
cid: option.cid,
fnval: 16 | 1024 | 2048,
fnver: 0,
fourk: 1,
high_quality: 1,
qn: 127,
setPlatformHTML5: false
});
log$1.info(["视频播放地址信息:", videoPlayInfo]);
if (!videoPlayInfo) {
return;
}
videoPlayInfo.dash.audio.forEach((item) => {
let audioUrl = BilibiliCDNProxy.findBetterCDN(
item.baseUrl,
item.base_url,
item.baseUrl,
item.backup_url
);
if (Panel.getValue("bili-video-uposServerSelect-applyAudio")) {
audioUrl = BilibiliCDNProxy.replaceVideoCDN(audioUrl);
}
audioInfo.push({
url: audioUrl,
id: item.id,
text: VideoSoundQualityCode[item.id] || "",
codecs: item.codecs,
mimeType: item.mimeType,
bandwidth: item.bandwidth,
size: 0
});
});
audioInfo.sort((leftItem, rightItem) => {
return rightItem.id - leftItem.id;
});
log$1.info(`ArtPlayer: 获取的音频信息`, audioInfo);
qualityInfo = [
...handleDashVideoQualityInfo$1({
accept_quality: videoPlayInfo.accept_quality,
support_formats: videoPlayInfo.support_formats,
video: videoPlayInfo.dash.video
})
];
log$1.info(`ArtPlayer: 获取的视频画质信息`, qualityInfo);
}
const currentVideoQuality = qualityInfo.map((item, index) => {
return {
quality: item.quality,
html: item.name,
url: item.url,
codecid: item.codecid,
codecs: item.codecs,
frameRate: item.frameRate,
mimeType: item.type,
bandwidth: item.bandwidth
};
});
const artPlayerOption = {
// @ts-ignore
container: null,
epList: option.epList,
audioUrl: null,
url: "",
poster: option.pic,
aid: option.aid,
bvid: option.bvid,
cid: option.cid,
videoTitle: option.title,
danmukuUrl: `https://api.bilibili.com/x/v1/dm/list.so?oid=${option.cid}`,
quality: currentVideoQuality
};
artPlayerOption.url = qualityInfo?.[0]?.url;
if (audioInfo.length) {
artPlayerOption.audioList = audioInfo.map((item, index) => {
return {
isDefault: index === 0,
url: item.url,
soundQualityCode: item.id,
soundQualityCodeText: item.text,
codecs: item.codecs,
mimeType: item.mimeType,
bandwidth: item.bandwidth,
size: item.size
};
});
}
return artPlayerOption;
};
const BilibiliVideoPlayer = {
$data: {
art: null
},
init() {
Panel.execMenu("bili-video-enableArtPlayer", () => {
this.coverVideoPlayer();
});
},
/**
* 覆盖播放器
*/
coverVideoPlayer() {
if ($("#artplayer")) {
log$1.warn("已使用ArtPlayer覆盖原播放器,更新播放信息");
} else {
addStyle(
/*css*/
`
/* 隐藏原本的播放器 */
${BilibiliData.className.video} .m-video-player .player-container,
${BilibiliData.className.mVideo} .m-video-player .player-container{
display: none !important;
}
${artPlayerCommonCSS}
${artPlayerCSS$1}
`
);
let controlsPadding = Panel.getValue(
"bili-video-artplayer-controlsPadding-left-right",
0
);
if (controlsPadding != 0) {
addStyle(
/*css*/
`
@media (orientation: landscape) {
.art-video-player .art-layers .art-layer-top-wrap,
/* 底部 */
.art-video-player .art-bottom{
padding-left: ${controlsPadding}px !important;
padding-right: ${controlsPadding}px !important;
}
/* 锁定图标 */
.art-video-player .art-layer-lock{
--art-lock-left-size: ${controlsPadding}px;
}
}
`
);
}
}
this.updateArtPlayerVideoInfo();
},
/**
* 更新播放信息
* @param videoInfo
* @param isEpChoose 是否是从选集内调用的
*/
updateArtPlayerVideoInfo(videoInfo, isEpChoose) {
let that = this;
let queryMVideoPlayer = () => {
return $(BilibiliData.className.video + " .m-video-player") || $(BilibiliData.className.mVideo + " .m-video-player");
};
VueUtils.waitVuePropToSet(queryMVideoPlayer, {
msg: "等待m-video-player加载完成",
check(vueInstance) {
if (!isEpChoose && BilibiliVideoArtPlayer.$data.currentOption != null) {
BilibiliVideoArtPlayer.$data.art.pause();
return typeof vueInstance?.info?.aid === "number" && BilibiliVideoArtPlayer.$data.currentOption.aid !== vueInstance.info.aid && typeof vueInstance?.info?.bvid === "string" && typeof vueInstance?.info?.cid === "number";
} else {
return typeof vueInstance?.info?.aid === "number" && typeof vueInstance?.info?.bvid === "string" && typeof vueInstance?.info?.cid === "number";
}
},
async set(vueInstance) {
const $mVideoPlayer = queryMVideoPlayer();
let { aid, bvid, cid, pic, title } = vueInstance;
aid = aid || vueInstance.info.aid;
bvid = bvid || vueInstance.info.bvid;
cid = cid || vueInstance.info.cid;
pic = pic || vueInstance.info.pic;
title = title || vueInstance.info.title;
let epInfoList = [];
const $seasonNew = $(".m-video-season-new");
const $partNew = $(".m-video-part-new");
if ($seasonNew && VueUtils.getVue($seasonNew)) {
let seasonVueIns = VueUtils.getVue($seasonNew);
let videoList = seasonVueIns?.videoList;
if (Array.isArray(videoList)) {
epInfoList = videoList;
}
} else if ($partNew && VueUtils.getVue($partNew)) {
let partVueIns = VueUtils.getVue($partNew);
let info = partVueIns?.info;
let currentPage = partVueIns?.p;
let pages = partVueIns?.pages || partVueIns?.info?.pages;
if (Array.isArray(pages)) {
epInfoList.push({
season_id: 0,
section_id: 0,
id: 0,
aid: aid || info.aid,
bvid: bvid || info.bvid,
cid: cid || info.cid,
title: title || info.title,
attribute: 0,
arc: {
aid: aid || info.aid,
videos: info?.videos,
type_id: 0,
type_name: "",
copyright: info?.copyright,
pic: info?.pic,
title: info?.title,
pubdate: info?.pubdate,
ctime: info?.ctime,
desc: info?.desc,
state: info?.state,
duration: info?.duration,
rights: info?.rights,
author: info?.owner,
stat: info?.stat,
dynamic: info?.dynamic,
dimension: info?.dimension,
desc_v2: info?.desc_v2,
is_chargeable_season: info?.is_chargeable_season,
is_blooper: info?.is_blooper,
enable_vt: info?.enable_vt,
vt_display: info?.vt_display
},
page: info?.pages?.[currentPage],
pages: info?.pages
});
}
}
if (videoInfo == null) {
videoInfo = {
aid,
bvid,
cid,
pic,
title,
epList: epInfoList
};
}
log$1.info(`视频播放信息 => aid:${aid} bvid:${bvid} cid:${cid}`);
const artPlayerOption = await GenerateArtPlayerOption$1(videoInfo);
if (artPlayerOption == null) {
return;
}
let $artPlayer = $("#artplayer");
if (!$artPlayer) {
const $artContainer = domUtils.createElement("div", {
className: "artplayer-container",
innerHTML: (
/*html*/
`
`
)
});
$artPlayer = $artContainer.querySelector("#artplayer");
domUtils.append($mVideoPlayer, $artContainer);
}
artPlayerOption.container = $artPlayer;
if (that.$data.art == null) {
let art = await BilibiliVideoArtPlayer.init(artPlayerOption);
if (art) {
that.$data.art = art;
} else {
return;
}
that.$data.art.volume = 1;
that.$data.art.once("ready", () => {
Panel.execMenu(
"bili-video-playerAutoPlayVideoFullScreen",
async () => {
log$1.info(`自动进入全屏`);
that.$data.art.fullscreen = true;
that.$data.art.once("fullscreenError", () => {
log$1.warn(
"未成功进入全屏,需要用户交互操作,使用网页全屏代替"
);
that.$data.art.fullscreenWeb = true;
});
}
);
});
} else {
const $artContainer = $(".artplayer-container");
if ($artContainer && !$artContainer.contains(that.$data.art.template.$container)) {
log$1.warn("artplayer-container的artplayer被移除了,重新添加元素");
domUtils.empty($artContainer);
domUtils.append($artContainer, that.$data.art.template.$container);
}
await BilibiliVideoArtPlayer.update(that.$data.art, artPlayerOption);
}
$mVideoPlayer.style.paddingTop = "";
}
});
}
};
const wbi = async (params) => {
async function getWbiQueryString(params2) {
const response = await BilibiliUserApi.nav(false);
if (!response) {
return;
}
const { img_url, sub_url } = response.wbi_img;
const imgKey = img_url.slice(
img_url.lastIndexOf("/") + 1,
img_url.lastIndexOf(".")
);
const subKey = sub_url.slice(
sub_url.lastIndexOf("/") + 1,
sub_url.lastIndexOf(".")
);
const originKey = imgKey + subKey;
const mixinKeyEncryptTable = [
46,
47,
18,
2,
53,
8,
23,
32,
15,
50,
10,
31,
58,
3,
45,
35,
27,
43,
5,
49,
33,
9,
42,
19,
29,
28,
14,
39,
12,
38,
41,
13,
37,
48,
7,
16,
24,
55,
40,
61,
26,
17,
0,
1,
60,
51,
30,
4,
22,
25,
54,
21,
56,
59,
6,
63,
57,
62,
11,
36,
20,
34,
44,
52
];
const mixinKey = mixinKeyEncryptTable.map((n) => originKey[n]).join("").slice(0, 32);
const query = Object.keys(params2).sort().map((key) => {
const value = params2[key].toString().replace(/[!'()*]/g, "");
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}).join("&");
const wbiSign = md5(query + mixinKey);
return query + "&w_rid=" + wbiSign;
}
return await getWbiQueryString(params);
};
function b2a(bvid) {
const XOR_CODE2 = 23442827791579n;
const MASK_CODE = 2251799813685247n;
const BASE2 = 58n;
const BYTES = ["B", "V", 1, "", "", "", "", "", "", "", "", ""];
const BV_LEN = BYTES.length;
const ALPHABET = "FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf".split("");
const DIGIT_MAP = [0, 1, 2, 9, 7, 5, 6, 4, 8, 3, 10, 11];
let r = 0n;
for (let i = 3; i < BV_LEN; i++) {
r = r * BASE2 + BigInt(ALPHABET.indexOf(bvid[DIGIT_MAP[i]]));
}
return `${r & MASK_CODE ^ XOR_CODE2}`;
}
const MobileCommentModule = function() {
const global = typeof _unsafeWindow === "undefined" ? window : _unsafeWindow;
const videoRE = /https:\/\/m\.bilibili\.com\/video\/.*/;
const dynamicRE = /https:\/\/m.bilibili.com\/dynamic\/\d+/;
const opusRE = /https:\/\/m.bilibili.com\/opus\/\d+/;
let oid, createrID, commentType, replyList;
const sortTypeConstant = { LATEST: 0, HOT: 2 };
let currentSortType;
let nextOffset = "";
let replyPool;
if (dynamicRE.test(global.location.href)) setupXHRInterceptor();
addStyle2();
return { init };
async function init(commentModuleWrapper) {
oid = createrID = commentType = void 0;
replyPool = {};
currentSortType = sortTypeConstant.HOT;
setupStandardCommentContainer(commentModuleWrapper);
replyList = commentModuleWrapper.querySelector(".reply-list");
await new Promise((resolve) => {
const timer = setInterval(async () => {
if (videoRE.test(global.location.href)) {
const videoID = global.location.pathname.replace("/video/", "").replace("/", "");
if (videoID.startsWith("av")) oid = videoID.slice(2);
if (videoID.startsWith("BV")) oid = b2a(videoID);
commentType = 1;
} else if (dynamicRE.test(global.location.href)) {
oid = global.dynamicDetail?.oid;
commentType = global.dynamicDetail?.commentType;
} else if (opusRE.test(global.location.href)) {
oid = global?.__INITIAL_STATE__?.opus?.detail?.basic?.comment_id_str;
commentType = global?.__INITIAL_STATE__?.opus?.detail?.basic?.comment_type;
}
if (oid && commentType) {
clearInterval(timer);
resolve();
}
}, 200);
});
await enableSwitchingSortType(commentModuleWrapper);
await loadFirstPagination(commentModuleWrapper);
}
function setupStandardCommentContainer(commentModuleWrapper) {
commentModuleWrapper.innerHTML = `
`;
}
async function enableSwitchingSortType(commentModuleWrapper) {
const navSortElement = commentModuleWrapper.querySelector(
".comment-container .reply-header .nav-sort"
);
const hotSortElement = navSortElement.querySelector(".hot-sort");
const timeSortElement = navSortElement.querySelector(".time-sort");
navSortElement.classList.add("hot");
navSortElement.classList.remove("time");
hotSortElement.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
if (currentSortType === sortTypeConstant.HOT) return;
currentSortType = sortTypeConstant.HOT;
navSortElement.classList.add("hot");
navSortElement.classList.remove("time");
commentModuleWrapper.scrollTo(0, 0);
loadFirstPagination(commentModuleWrapper);
});
timeSortElement.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
if (currentSortType === sortTypeConstant.LATEST) return;
currentSortType = sortTypeConstant.LATEST;
navSortElement.classList.add("time");
navSortElement.classList.remove("hot");
commentModuleWrapper.scrollTo(0, 0);
loadFirstPagination(commentModuleWrapper);
});
}
async function loadFirstPagination(commentModuleWrapper) {
const { data: firstPaginationData, code: resultCode } = await getPaginationData();
createrID = firstPaginationData.upper.mid;
replyList.innerHTML = "";
replyPool = {};
document.querySelector(".comment-container .reply-warp .no-more-replies-info")?.remove();
document.querySelector(".comment-container .reply-warp .anchor-for-loading")?.remove();
if (resultCode !== 0) {
const info = resultCode === 12061 ? "UP主已关闭评论区" : "无法从API获取评论数据";
replyList.innerHTML = `${info}
`;
return;
}
const totalReplyElement = commentModuleWrapper.querySelector(
".comment-container .reply-header .total-reply"
);
const totalReplyCount = parseInt(firstPaginationData?.cursor?.all_count) || 0;
totalReplyElement.textContent = totalReplyCount;
if (firstPaginationData?.cursor?.name?.includes("精选")) {
const navSortElement = commentModuleWrapper.querySelector(
".comment-container .reply-header .nav-sort"
);
navSortElement.innerHTML = `精选评论
`;
}
if (firstPaginationData.top_replies && firstPaginationData.top_replies.length !== 0) {
const topReplyData = firstPaginationData.top_replies[0];
appendReplyItem(topReplyData, true);
}
for (const replyData of firstPaginationData.replies) {
appendReplyItem(replyData);
}
if (firstPaginationData.replies.length === 0 || firstPaginationData.cursor.is_end) {
const infoElement = document.createElement("p");
infoElement.classList.add("no-more-replies-info");
infoElement.style = "padding-bottom: 100px; text-align: center; color: #999;";
infoElement.textContent = "没有更多评论";
document.querySelector(".comment-container .reply-warp").appendChild(infoElement);
return;
}
addAnchor();
}
async function getPaginationData() {
const params = {
pagination_str: JSON.stringify({
offset: nextOffset || ""
}),
oid,
type: commentType,
wts: parseInt(Date.now() / 1e3),
plat: 1,
web_location: 1315875
};
if (currentSortType === sortTypeConstant.HOT) {
params.mode = 3;
if (!nextOffset) {
params.seek_rpid = "";
}
} else if (currentSortType === sortTypeConstant.LATEST) {
params.mode = 2;
}
const fetchResult = await httpx.get(
`https://api.bilibili.com/x/v2/reply/wbi/main?${await wbi(params)}`,
{
fetch: true
}
);
const fetchResultJSON = utils.toJSON(fetchResult.data.responseText);
nextOffset = fetchResultJSON.data.cursor?.pagination_reply?.next_offset || "";
return fetchResultJSON;
}
function appendReplyItem(replyData, isTopReply) {
if (replyPool[replyData.rpid_str]) {
return;
}
const replyItemElement = document.createElement("div");
replyItemElement.classList.add("reply-item");
replyItemElement.innerHTML = `
${isTopReply ? '置顶 ' : ""}${replyData.content.pictures ? `` : ""}${getConvertedMessage(replyData.content)}
${replyData.content.pictures ? `
${getImageItems(replyData.content.pictures)}
` : ""}
${getFormattedTime(
replyData.ctime
)}
${replyData.like}
${replyData.card_label ? replyData.card_label.reduce(
(acc, cur) => acc + `${cur.text_content} `,
""
) : ""}
${getSubReplyItems(replyData.replies)}
${replyData.rcount > replyData.replies.length ? `
共${replyData.rcount}条回复,
点击查看
` : ""}
`;
replyList.appendChild(replyItemElement);
replyPool[replyData.rpid_str] = true;
const previewImageContainer = replyItemElement.querySelector(
".preview-image-container"
);
if (previewImageContainer)
new Viewer(previewImageContainer, {
title: false,
toolbar: false,
tooltip: false,
keyboard: false
});
const subReplyList = replyItemElement.querySelector(".sub-reply-list");
const viewMoreBtn = replyItemElement.querySelector(".view-more-btn");
viewMoreBtn && viewMoreBtn.addEventListener(
"click",
() => loadPaginatedSubReplies(
replyData.rpid,
subReplyList,
replyData.rcount,
1
)
);
}
function getFormattedTime(ms) {
const time = new Date(ms * 1e3);
const year = time.getFullYear();
const month = (time.getMonth() + 1).toString().padStart(2, "0");
const day = time.getDate().toString().padStart(2, "0");
const hour = time.getHours().toString().padStart(2, "0");
const minute = time.getMinutes().toString().padStart(2, "0");
return `${year}-${month}-${day} ${hour}:${minute}`;
}
function getMemberLevelColor(level) {
return {
0: "#C0C0C0",
1: "#BBBBBB",
2: "#8BD29B",
3: "#7BCDEF",
4: "#FEBB8B",
5: "#EE672A",
6: "#F04C49"
}[level];
}
function getConvertedMessage(content) {
let result = content.message;
const keywordBlacklist = [
"https://www.bilibili.com/video/av",
"https://b23.tv/mall-"
];
if (content.vote && content.vote.deleted === false) {
const linkElementHTML = `${content.vote.title} `;
keywordBlacklist.push(linkElementHTML);
result = result.replace(`{vote:${content.vote.id}}`, linkElementHTML);
}
if (content.emote) {
for (const [key, value] of Object.entries(content.emote)) {
const imageElementHTML = ` `;
keywordBlacklist.push(imageElementHTML);
result = result.replaceAll(key, imageElementHTML);
}
}
result = result.replaceAll(/(\d{1,2}[::]){1,2}\d{1,2}/g, (timestamp) => {
timestamp = timestamp.replaceAll(":", ":");
if (!videoRE.test(global.location.href)) return timestamp;
const parts = timestamp.split(":");
if (parts.some((part) => parseInt(part) >= 60)) return timestamp;
let totalSecond;
if (parts.length === 2)
totalSecond = parseInt(parts[0]) * 60 + parseInt(parts[1]);
else if (parts.length === 3)
totalSecond = parseInt(parts[0]) * 3600 + parseInt(parts[1]) * 60 + parseInt(parts[2]);
if (Number.isNaN(totalSecond)) return timestamp;
const linkElementHTML = `${timestamp} `;
keywordBlacklist.push(linkElementHTML);
return linkElementHTML;
});
if (content.at_name_to_mid) {
for (const [key, value] of Object.entries(content.at_name_to_mid)) {
const linkElementHTML = `@${key} `;
keywordBlacklist.push(linkElementHTML);
result = result.replaceAll(`@${key}`, linkElementHTML);
}
}
if (Object.keys(content.jump_url).length) {
const entries = [].concat(
Object.entries(content.jump_url).filter(
(entry) => entry[0].startsWith("https://")
),
Object.entries(content.jump_url).filter(
(entry) => !entry[0].startsWith("https://")
)
);
for (const [key, value] of entries) {
const href = key.startsWith("BV") || /^av\d+$/.test(key) ? `https://www.bilibili.com/video/${key}` : value.pc_url || key;
if (href.includes("search.bilibili.com") && keywordBlacklist.join("").includes(key))
continue;
const linkElementHTML = `${value.title} `;
keywordBlacklist.push(linkElementHTML);
result = result.replaceAll(key, linkElementHTML);
}
}
return result;
}
function getImageItems(images) {
let imageSizeConfig = "width: 84px; height: 84px;";
if (images.length === 1)
imageSizeConfig = "max-width: 260px; max-height: 180px;";
if (images.length === 2) imageSizeConfig = "width: 128px; height: 128px;";
let result = "";
for (const image of images) {
result += ``;
}
return result;
}
function getSubReplyItems(subReplies) {
if (!(subReplies instanceof Array)) return "";
let result = "";
for (const replyData of subReplies) {
result += `
${getConvertedMessage(
replyData.content
)}
${getFormattedTime(
replyData.ctime
)}
${replyData.like}
`;
}
return result;
}
async function loadPaginatedSubReplies(rootReplyID, subReplyList, subReplyAmount, paginationNumber) {
const params = {
oid,
type: commentType,
root: rootReplyID,
ps: 10,
pn: paginationNumber,
web_location: 333.788
};
const subReplyResponse = await httpx.get(
`https://api.bilibili.com/x/v2/reply/reply?${Utils.toSearchParamsStr(
params
)}`,
{
allowInterceptConfig: false,
fetch: true
}
);
if (!subReplyResponse.status) {
log.error(subReplyResponse);
Qmsg.error("请求异常,获取评论的回复失败");
return;
}
const subReplyJSON = utils.toJSON(subReplyResponse.data.responseText);
if (subReplyJSON === -352) {
Qmsg.error("请登录后再进行操作");
console.error("you should login first", subReplyResponse);
return;
}
const subReplyData = subReplyJSON.data;
subReplyList.innerHTML = getSubReplyItems(subReplyData.replies);
addSubReplyPageSwitcher(
rootReplyID,
subReplyList,
subReplyAmount,
paginationNumber
);
const replyItem = subReplyList.parentElement.parentElement;
replyItem.scrollIntoView({ behavior: "instant" });
global.scrollTo(0, document.documentElement.scrollTop - 60);
}
function addSubReplyPageSwitcher(rootReplyID, subReplyList, subReplyAmount, currentPageNumber) {
if (subReplyAmount <= 10) return;
const pageAmount = Math.ceil(subReplyAmount / 10);
const pageSwitcher = document.createElement("div");
pageSwitcher.classList.add("view-more");
pageSwitcher.innerHTML = `
`;
pageSwitcher.querySelector(".pagination-to-prev-btn")?.addEventListener(
"click",
() => loadPaginatedSubReplies(
rootReplyID,
subReplyList,
subReplyAmount,
currentPageNumber - 1
)
);
pageSwitcher.querySelector(".pagination-to-next-btn")?.addEventListener(
"click",
() => loadPaginatedSubReplies(
rootReplyID,
subReplyList,
subReplyAmount,
currentPageNumber + 1
)
);
pageSwitcher.querySelectorAll(".pagination-page-number:not(.current-page)")?.forEach((pageNumberElement) => {
const number = parseInt(pageNumberElement.textContent);
pageNumberElement.addEventListener(
"click",
() => loadPaginatedSubReplies(
rootReplyID,
subReplyList,
subReplyAmount,
number
)
);
});
subReplyList.appendChild(pageSwitcher);
}
function addAnchor() {
const anchorElement = document.createElement("div");
anchorElement.classList.add("anchor-for-loading");
anchorElement.textContent = "正在加载...";
anchorElement.style = `text-align: center; color: #61666d; transform: translateY(-50px);`;
document.querySelector(".comment-container .reply-warp").appendChild(anchorElement);
const ob = new IntersectionObserver(async (entries) => {
if (!entries[0].isIntersecting) return;
const { data: newPaginationData } = await getPaginationData();
if (!newPaginationData.replies || newPaginationData.replies.length === 0) {
anchorElement.textContent = "所有评论已加载完毕";
ob.disconnect();
return;
}
for (const replyData of newPaginationData.replies) {
appendReplyItem(replyData);
}
});
ob.observe(anchorElement);
}
function setupXHRInterceptor() {
const originXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
const url = arguments[1];
if (typeof url === "string" && url.includes("reply/wbi/main")) {
const { searchParams } = new URL(
`${url.startsWith("//") ? "https:" : ""}${url}`
);
global.dynamicDetail = {
oid: searchParams.get("oid"),
commentType: searchParams.get("type")
};
}
return originXHROpen.apply(this, arguments);
};
}
async function addStyle2() {
await new Promise((resolve) => {
const timer = setInterval(() => {
if (document && document.createElement && document.head && document.head.appendChild) {
clearInterval(timer);
resolve();
}
}, 100);
});
const replyHeaderCSS = document.createElement("style");
replyHeaderCSS.textContent = `
.reply-header {
padding: 12px;
border-bottom: 1px solid #f1f2f3;
}
.reply-navigation {
margin-bottom: 0 !important;
}
.reply-navigation .nav-bar .nav-title {
font-size: 1rem !important;
}
`;
document.head.appendChild(replyHeaderCSS);
const replyListCSS = document.createElement("style");
replyListCSS.textContent = `
.reply-list {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
.reply-item {
padding: 12px !important;
font-size: 1rem !important;
border-bottom: 1px solid #f4f5f7;
}
.reply-item .root-reply-container {
padding: 0 !important;
display: flex;
}
.reply-item .root-reply-container .root-reply-avatar {
position: relative !important;
width: initial !important;
}
.reply-item .root-reply-container .content-warp {
margin-left: 12px;
}
.reply-item .root-reply-container .content-warp .user-info,
.reply-item .root-reply-container .content-warp .root-reply .reply-content {
font-size: 14px !important;
}
.reply-item .root-reply-container .content-warp .root-reply .reply-content-container {
width: calc(100vw - 88px) !important;
}
.reply-item .root-reply-container .content-warp .root-reply .reply-content .note-prefix {
margin-right: 4px !important;
}
.reply-item .sub-reply-container {
padding-left: 44px !important;
}
.reply-item .sub-reply-container .sub-reply-list .sub-reply-item {
width: calc(100% - 24px);
}
.reply-item .sub-reply-container .sub-reply-list .sub-reply-item .sub-user-info {
margin-right: 0 !important;
}
.reply-item .sub-reply-container .sub-reply-list .sub-reply-item .sub-user-info .sub-user-name,
.reply-item .sub-reply-container .sub-reply-list .sub-reply-item .reply-content {
font-size: 14px !important;
}
.reply-info .reply-time,
.reply-info .reply-like,
.sub-reply-info .sub-reply-time,
.sub-reply-info .sub-reply-like {
margin-right: 12px !important;
}
`;
document.head.appendChild(replyListCSS);
const avatarCSS = document.createElement("style");
avatarCSS.textContent = `
.reply-item .root-reply-avatar .avatar .bili-avatar {
width: 40px;
height: 40px;
}
.sub-reply-item .sub-reply-avatar .avatar .bili-avatar {
width: 24px;
height: 24px;
}
`;
document.head.appendChild(avatarCSS);
const viewMoreCSS = document.createElement("style");
viewMoreCSS.textContent = `
.sub-reply-container .view-more-btn:hover {
color: #00AEEC;
}
.view-more {
padding-left: 8px;
color: #222;
font-size: 13px;
user-select: none;
}
.pagination-page-count {
margin-right: 4px !important;
}
.pagination-page-dot,
.pagination-page-number {
margin: 0 4px;
}
.pagination-btn,
.pagination-page-number {
cursor: pointer;
}
.current-page,
.pagination-btn:hover,
.pagination-page-number:hover {
color: #00AEEC;
}
`;
(document.head || document.documentElement).appendChild(viewMoreCSS);
const otherCSS = document.createElement("style");
otherCSS.textContent = `
:root {
--text1: #18191C;
--text3: #9499A0;
--brand_blue: #00AEEC;
--brand_pink: #FF6699;
--bg2: #F6F7F8;
}
.jump-link {
color: #008DDA;
}
`;
(document.head || document.documentElement).appendChild(otherCSS);
}
}();
const MobileCommentModuleStyle = ':root {\r\n --v_xs: 5px;\r\n --v_xsx: 4px;\r\n --v_xxs: 6px;\r\n --v_sm: 10px;\r\n --v_smx: 8px;\r\n --v_xsm: 12px;\r\n --v_md: 15px;\r\n --v_mdx: 14px;\r\n --v_xmd: 16px;\r\n --v_lg: 20px;\r\n --v_lgx: 18px;\r\n --v_xlg: 22px;\r\n --v_xl: 25px;\r\n --v_xlx: 24px;\r\n --v_xxl: 26px;\r\n --v_fs_1: 24px;\r\n --v_fs_2: 18px;\r\n --v_fs_3: 16px;\r\n --v_fs_4: 14px;\r\n --v_fs_5: 13px;\r\n --v_fs_6: 12px;\r\n --v_lh_xs: 1;\r\n --v_lh_sm: 1.25;\r\n --v_lh_md: 1.5;\r\n --v_lh_lg: 1.75;\r\n --v_lh_xl: 2;\r\n --v_height_xs: 16px;\r\n --v_height_sm: 24px;\r\n --v_height_md: 32px;\r\n --v_height_lg: 40px;\r\n --v_height_xl: 48px;\r\n --v_radius: 6px;\r\n --v_radius_sm: 4px;\r\n --v_radius_md: 8px;\r\n --v_radius_lg: 10px;\r\n --v_brand_pink: var(--brand_pink, #ff6699);\r\n --v_brand_pink_thin: var(--brand_pink_thin, #ffecf1);\r\n --v_brand_blue: var(--brand_blue, #00aeec);\r\n --v_brand_blue_thin: var(--brand_blue_thin, #dff6fd);\r\n --v_stress_red: var(--stress_red, #f85a54);\r\n --v_stress_red_thin: var(--stress_red_thin, #feecea);\r\n --v_success_green: var(--success_green, #2ac864);\r\n --v_success_green_thin: var(--success_green_thin, #e4f8ea);\r\n --v_operate_orange: var(--operate_orange, #ff7f24);\r\n --v_operate_orange_thin: var(--operate_orange_thin, #fff0e3);\r\n --v_pay_yellow: var(--pay_yellow, #ffb027);\r\n --v_pay_yellow_thin: var(--pay_yellow_thin, #fff6e4);\r\n --v_bg1: var(--bg1, #ffffff);\r\n --v_bg2: var(--bg2, #f6f7f8);\r\n --v_bg3: var(--bg3, #f1f2f3);\r\n --v_bg1_float: var(--bg1_float, #ffffff);\r\n --v_bg2_float: var(--bg2_float, #f1f2f3);\r\n --v_text_white: var(--text_white, #ffffff);\r\n --v_text1: var(--text1, #18191c);\r\n --v_text2: var(--text2, #61666d);\r\n --v_text3: var(--text3, #9499a0);\r\n --v_text4: var(--text4, #c9ccd0);\r\n --v_text_link: var(--text_link, #008ac5);\r\n --v_text_notice: var(--text_notice, #e58900);\r\n --v_line_light: var(--line_light, #f1f2f3);\r\n --v_line_regular: var(--line_regular, #e3e5e7);\r\n --v_line_bold: var(--line_bold, #c9ccd0);\r\n --v_graph_white: var(--graph_white, #ffffff);\r\n --v_graph_bg_thin: var(--graph_bg_thin, #f6f7f8);\r\n --v_graph_bg_regular: var(--graph_bg_regular, #f1f2f3);\r\n --v_graph_bg_thick: var(--graph_bg_thick, #e3e5e7);\r\n --v_graph_weak: var(--graph_weak, #c9ccd0);\r\n --v_graph_medium: var(--graph_medium, #9499a0);\r\n --v_graph_icon: var(--graph_icon, #61666d);\r\n --v_shadow: var(--shadow, #000000);\r\n --v_brand_pink_hover: var(--brand_pink_hover, #ff8cb0);\r\n --v_brand_pink_active: var(--brand_pink_active, #e84b85);\r\n --v_brand_pink_disabled: var(--brand_pink_disabled, #ffb3ca);\r\n --v_brand_blue_hover: var(--brand_blue_hover, #40c5f1);\r\n --v_brand_blue_active: var(--brand_blue_active, #008ac5);\r\n --v_brand_blue_disabled: var(--brand_blue_disabled, #80daf6);\r\n --v_stress_red_hover: var(--stress_red_hover, #fa857f);\r\n --v_stress_red_active: var(--stress_red_active, #e23d3d);\r\n --v_stress_red_disabled: var(--stress_red_disabled, #fcafaa);\r\n --v_text_hover: var(--text_hover, #797f87);\r\n --v_text_active: var(--text_active, #61666d);\r\n --v_text_disabled: var(--text_disabled, #c9ccd0);\r\n --v_line_border: var(--line_border, #c9ccd0);\r\n --v_line_bolder_hover: var(--line_bolder_hover, #e3e5e7);\r\n --v_line_bolder_active: var(--line_bolder_active, #aeb3b9);\r\n --v_line_bolder_disabled: var(--line_bolder_disabled, #f1f2f3);\r\n}\r\n\r\n@font-face {\r\n font-family: fanscard;\r\n src: url(//s1.hdslb.com/bfs/static/jinkela/mall-h5/asserts/fansCard.ttf);\r\n}\r\n\r\n.svg-icon {\r\n display: inline-flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n.svg-icon svg {\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.svg-icon.use-color svg path {\r\n fill: currentColor;\r\n color: inherit;\r\n}\r\n\r\n.top-vote-card {\r\n background-color: var(--graph_bg_thin);\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n height: 80px;\r\n width: 100%;\r\n margin-bottom: 24px;\r\n padding: 12px 16px 12px 10px;\r\n border-radius: 6px;\r\n}\r\n\r\n.top-vote-card__multi {\r\n cursor: pointer;\r\n}\r\n\r\n.top-vote-card__multi:hover .vote-result-text {\r\n color: var(--brand_blue);\r\n transition: 0.2s;\r\n}\r\n\r\n.top-vote-card-left {\r\n width: 40%;\r\n max-width: calc(40% - 30px);\r\n margin-right: 20px;\r\n word-wrap: break-word;\r\n font-size: 13px;\r\n line-height: 18px;\r\n color: var(--text1);\r\n}\r\n\r\n.top-vote-card-left__title {\r\n display: flex;\r\n align-items: center;\r\n}\r\n\r\n.top-vote-card-left__title svg {\r\n margin-right: 2px;\r\n flex: none;\r\n}\r\n\r\n.top-vote-card-left__title span {\r\n display: -webkit-box;\r\n float: none;\r\n height: 18px;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n word-break: break-word;\r\n -webkit-box-orient: vertical;\r\n -webkit-line-clamp: 1;\r\n}\r\n\r\n.top-vote-card-left__join {\r\n height: 17px;\r\n display: flex;\r\n align-items: center;\r\n margin-top: 4px;\r\n font-size: 12px;\r\n color: var(--text3);\r\n}\r\n\r\n.top-vote-card-left__join .vote-icon {\r\n height: 12px;\r\n}\r\n\r\n.top-vote-card-left__join span {\r\n display: flex;\r\n align-items: center;\r\n}\r\n\r\n.top-vote-card-right {\r\n width: 60%;\r\n font-size: var(--2fde2a28);\r\n line-height: 17px;\r\n display: flex;\r\n --option-height: 40px;\r\n --option-radius: 6px;\r\n}\r\n\r\n.top-vote-card-right .vote-text__not-vote {\r\n opacity: 0.9;\r\n}\r\n\r\n.top-vote-card-right .vote-text__not-vote .vui_ellipsis {\r\n font-weight: 400 !important;\r\n}\r\n\r\n.top-vote-card-right .vote-text :first-child {\r\n font-weight: 500;\r\n}\r\n\r\n.top-vote-card-right .vote-icon {\r\n flex: none;\r\n}\r\n\r\n.top-vote-card-right .left-vote-option {\r\n position: relative;\r\n display: flex;\r\n min-width: 120px;\r\n align-items: center;\r\n justify-content: space-between;\r\n background-color: rgba(255, 102, 153, var(--212267a6));\r\n height: var(--option-height);\r\n width: var(--38c5ebb3);\r\n padding-left: 10px;\r\n border-radius: var(--option-radius) 0 0 var(--option-radius);\r\n cursor: pointer;\r\n margin-right: 30px;\r\n color: var(--332a347e);\r\n transition: width ease-out 0.2s;\r\n}\r\n\r\n.top-vote-card-right .left-vote-option .skew-vote-option {\r\n position: absolute;\r\n right: -20px;\r\n top: 0;\r\n}\r\n\r\n.top-vote-card-right .left-vote-option .skew-vote-option__fill {\r\n left: -8px;\r\n background-color: #f69;\r\n transform: skew(21deg);\r\n border-top-right-radius: calc(var(--option-radius) - 2px);\r\n border-bottom-right-radius: var(--option-radius);\r\n}\r\n\r\n.top-vote-card-right .skew-vote-option {\r\n height: 40px;\r\n width: 20px;\r\n overflow: hidden;\r\n opacity: var(--212267a6);\r\n pointer-events: none;\r\n}\r\n\r\n.top-vote-card-right .skew-vote-option__fill {\r\n pointer-events: all;\r\n position: absolute;\r\n top: 0;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.top-vote-card-right .right-vote-option {\r\n position: relative;\r\n display: flex;\r\n min-width: 120px;\r\n align-items: center;\r\n flex-direction: row-reverse;\r\n justify-content: space-between;\r\n background-color: rgba(0, 174, 236, var(--212267a6));\r\n height: var(--option-height);\r\n width: var(--4b2970aa);\r\n padding-right: 10px;\r\n border-radius: 0 var(--option-radius) var(--option-radius) 0;\r\n cursor: pointer;\r\n color: var(--1e587827);\r\n transition: width ease-out 0.2s;\r\n}\r\n\r\n.top-vote-card-right .right-vote-option .skew-vote-option {\r\n position: absolute;\r\n left: -20px;\r\n top: 0;\r\n}\r\n\r\n.top-vote-card-right .right-vote-option .skew-vote-option__fill {\r\n left: 8px;\r\n background-color: #00aeec;\r\n transform: skew(21deg);\r\n border-top-left-radius: var(--option-radius);\r\n border-bottom-left-radius: calc(var(--option-radius) - 2px);\r\n}\r\n\r\n.top-vote-card-right .right-vote-option .vote-text {\r\n text-align: right;\r\n}\r\n\r\n.top-vote-card-right .had_voted {\r\n cursor: unset;\r\n}\r\n\r\n.reply-header .reply-notice {\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n min-height: 40px;\r\n padding: 4px 10px;\r\n margin-bottom: 16px;\r\n font-size: 13px;\r\n border-radius: 2px;\r\n color: var(--Ye5_u);\r\n cursor: pointer;\r\n}\r\n\r\n.reply-header .reply-notice:after {\r\n content: "";\r\n position: absolute;\r\n width: 100%;\r\n height: 100%;\r\n top: 0;\r\n left: 0;\r\n background-color: var(--Ye5_u);\r\n opacity: 0.2;\r\n}\r\n\r\n.reply-header .reply-notice .notice-icon {\r\n width: 16px;\r\n height: 16px;\r\n margin-right: 5px;\r\n}\r\n\r\n.reply-header .reply-notice .notice-content {\r\n flex: 1;\r\n padding: 0 5px;\r\n vertical-align: top;\r\n word-wrap: break-word;\r\n word-break: break-all;\r\n}\r\n\r\n.reply-header .reply-notice .notice-close-icon {\r\n position: relative;\r\n z-index: 1;\r\n width: 10px;\r\n height: 10px;\r\n margin-left: 5px;\r\n}\r\n\r\n.reply-header .reply-navigation {\r\n margin-bottom: 22px;\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar {\r\n display: flex;\r\n align-items: center;\r\n list-style: none;\r\n margin: 0;\r\n padding: 0;\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar .nav-title {\r\n display: flex;\r\n align-items: center;\r\n}\r\n\r\n@media screen and (max-width: 1681px) {\r\n .reply-header .reply-navigation .nav-bar .nav-title {\r\n font-size: 20px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1681px) {\r\n .reply-header .reply-navigation .nav-bar .nav-title {\r\n font-size: 24px;\r\n }\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar .nav-title .nav-title-text {\r\n color: var(--text1);\r\n font-family: PingFang SC, HarmonyOS_Medium, Helvetica Neue, Microsoft YaHei,\r\n sans-serif;\r\n font-weight: 500;\r\n}\r\n\r\n@media (-webkit-max-device-pixel-ratio: 1) {\r\n .reply-header .reply-navigation .nav-bar .nav-title .nav-title-text {\r\n font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica,\r\n Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, sans-serif;\r\n }\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar .nav-title .total-reply {\r\n margin: 0 36px 0 6px;\r\n font-weight: 400;\r\n color: var(--text3);\r\n}\r\n\r\n@media screen and (max-width: 1681px) {\r\n .reply-header .reply-navigation .nav-bar .nav-title .total-reply {\r\n font-size: 13px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1681px) {\r\n .reply-header .reply-navigation .nav-bar .nav-title .total-reply {\r\n font-size: 14px;\r\n }\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar .nav-select-reply {\r\n font-family: PingFang SC, HarmonyOS_Medium, Helvetica Neue, Microsoft YaHei,\r\n sans-serif;\r\n font-weight: 500;\r\n color: var(--text1);\r\n}\r\n\r\n@media screen and (max-width: 1681px) {\r\n .reply-header .reply-navigation .nav-bar .nav-select-reply {\r\n font-size: 13px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1681px) {\r\n .reply-header .reply-navigation .nav-bar .nav-select-reply {\r\n font-size: 16px;\r\n }\r\n}\r\n\r\n@media (-webkit-max-device-pixel-ratio: 1) {\r\n .reply-header .reply-navigation .nav-bar .nav-select-reply {\r\n font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica,\r\n Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, sans-serif;\r\n }\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar .nav-sort {\r\n display: flex;\r\n align-items: center;\r\n color: var(--text3);\r\n}\r\n\r\n@media screen and (max-width: 1681px) {\r\n .reply-header .reply-navigation .nav-bar .nav-sort {\r\n font-size: 13px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1681px) {\r\n .reply-header .reply-navigation .nav-bar .nav-sort {\r\n font-size: 16px;\r\n }\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar .nav-sort .part-symbol {\r\n height: 11px;\r\n margin: 0 12px;\r\n border-left: solid 1px;\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar .nav-sort .hot-sort {\r\n cursor: pointer;\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar .nav-sort .hot-sort:hover {\r\n color: var(--brand_blue);\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar .nav-sort .time-sort {\r\n cursor: pointer;\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar .nav-sort .time-sort:hover {\r\n color: var(--brand_blue);\r\n}\r\n\r\n.reply-header .reply-navigation .nav-bar .nav-sort.hot .hot-sort,\r\n.reply-header .reply-navigation .nav-bar .nav-sort.time .time-sort {\r\n color: var(--text1);\r\n}\r\n\r\n.reply-header .reply-navigation .nav-operation-warp {\r\n position: absolute;\r\n right: 0;\r\n}\r\n\r\n/*\r\n * @bilibili/userAvatar\r\n * version: 1.2.0-beta.2. Powered by main-frontend\r\n * 用户头像公共组件.\r\n * author: wuxiuran\r\n */\r\n.bili-avatar {\r\n display: block;\r\n position: relative;\r\n background-image: url(data:image/gif;base64,R0lGODlhtAC0AOYAALzEy+To7rG6wb/Hzd/k6rK7wsPK0bvDybO8w9/j6dDW3NHX3eHl6+Hm7LnByLa+xeDl6+Lm7M/V27vDyt7j6dHX3r/Gzb/HzsLJ0LS9xLW+xbe/xtLY3s/V3OPn7dne5NXb4eDk67jAx7S8w+Dk6rrCybW9xMXM08TL0sLK0Nrf5cXM0tjd48zS2bO7wsrR17W+xLfAx8fO1La/xsbN07K7wbzEytzh573FzNLX3uLn7cDHzsbN1NPZ377Gzb7FzNbc4sjP1dfd49bb4tvg5svR2LfAxsnQ1s7U293h6Nbb4dTa4MrQ19fc4t3i6L7GzMnP1s7U2tXa4M3T2sDIz97i6N7i6dje5MjO1dfc473Ey8HJz9vg57jBx8jP1tPY38PL0cfO1dne5dXa4ePn7sHIz8vS2Nrf5tDW3djd5M3T2cDIztTZ4L3Fy7rCyMTL0czT2bC5wOXp7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4zLWMwMTEgNjYuMTQ1NjYxLCAyMDEyLzAyLzA2LTE0OjU2OjI3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1OTQ4QTFCMzg4NDAxMUU1OTA2NUJGQjgwNzVFMDQ2NSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1OTQ4QTFCNDg4NDAxMUU1OTA2NUJGQjgwNzVFMDQ2NSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjU5NDhBMUIxODg0MDExRTU5MDY1QkZCODA3NUUwNDY1IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU5NDhBMUIyODg0MDExRTU5MDY1QkZCODA3NUUwNDY1Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAAAAAAAsAAAAALQAtAAAB/+AcoKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19sA6SCtTCakBCyuKOLmXKAGOOAhLiDkFoQzCOA9YEDyE5SHCBx9KhdhhMc6EBhMJeXDQMY6GjKIgXCgZR0jIQR4msDRxJRQBHyzjoHwpR0LODRI9keDI0kAAnoI8rMgJoyYnlTkBUEA6KMDSmTsxhTjIEsBAqlWvlowR9BIBCzmf9ANLyCrTrJP/SAzI+WMtW5EncmpIUwkCTpZaqtw9FIBGzgxlIRHgWvLH1MGIDLN8ACRSArQsfRCAnCgAj5wmsjwigbnkk80hA6hezbr1ajkeMoCu7Lq1HIM5C9yQU7v363EQFhxBMeGA8ePIkx+fMEFAzjgFmCtHPuHBcwEAik/fbnwCCiZfQHKzcoLk8/Po06tfr95BC7vWAkgQwb6+/fv4ETqocC2EgfwABihgRzToQM1ZJT0AwIIMNujggxBGKOGEFFYIgHkWYQCBNA0A0BEASOzmDAMS2NBRCh5AE4AMFiGAhIHSeIAEAhYdAQ0HFmkwxDVDmPBQAU2MiCECSiDiAQkhMBAC/wFMNunkk1ASkMCUUzJJAgQMMNDAllxyGUEEXTaQ5ZhjQmDmmRCEcOVRhyhBI0I2RNCMGRZ5cUgO5RWAQAYuCCBADYDW4OeghBZqqJ8FuLAnDBo84OijkDqqwaQwwGDCpRlkOsKmCHTaqQsjAIDFAocEYVEHzDCA4QMkFNIAGAgdcMEAtM5K6621XqDrrrz2uiuuFgQr7LDEFmsBrsjiWgJCYIg3CAnW6ZeiMgtYBEUhEfwQhwEqsFkMGSxw9IOchHjxIwjKBICBRS4R8pkZzHgWhwyFCGHRCcoQMIJFZxAyRBz4NhMADgIUOYgKFjnAQDJLOIeQboTQUAB8y3wgAP8PhHBRwEMCwEUMiw+Z8BhvJVChogMHeEuBbA+NkQysDxmxsCARbPBCNDs8QK4cDBhhUQvJrJHwtHJAAAMS0byQwYZJYRgHxsjM9VAJ3kJgAqrQoAFDCFUdYBEKyUiN0ASENCCCBNF0IIKzcpj4kAFhWwQAIRE4gDY0EjiwsxwePpRC3A+1Qbfd0eS9N2PbAo7QAIPf/YzhhBCFENxRW/T3IHU77gzkg6RgEeXHiB0HBmWfnXYMbK/7tuKjl72B5s10sMHMgqg+OeukD9LA62nPTojtiVf+0A+EMPAA7Mx08ADTgjxhOetzDwLBA1g/04EGzPP9vPBjEwKBBtU7o8D/1oS4jdDloVtE9iAhZBC+JVkg0YS3kQzhgAMoRBEkJgpk0OogMvEb61I2CH29LxJWWMIKROAcAUzACpIIgLYsIoITAGFvkVAAAlAjiADejnseIQQBEHDARlBAAT5gWUemIIkXPKcLGEhD9hyhABdwUA4eDF76HrI+QRCgAAqARADYYACHHUZEjvDAstAzAx54TBEKmBghcgg6Y4iuh3L4YRAbEQEFuGE96HoEA2awHgHIgAg0lCIAP8c6G4gQiIw4wwvIyJ5+QUIB9SkACpCYiCjCx3w6tKJFtCBCEnZmDGUwono20AP6OSIIG2NPAbAwskNo8IbOWx0I10AIEoyg/4RyIMJf2DMDNcwQEiowQCTXU4AjYHAQl/wdG0GIPjmQwH2HCIHT0jMCJtDOElWAwi7RgwNEKGAENwReFYshutz50JCGAJl6HuCFG2YiAl/oW3oQYMwNylKTO0SIM7MIzUL8Jz0bkIE1O8GCLfjoPA/oZjJnGc7WFdAFWyxEtZ4zAhpwwJGhSIAEnrDKjpDKkgWYJzgF+ZBxavEQHlhJRzSAAja80hQkmIIBNGCRGfySEH785gfrWcuHHuIDGajBBnBwAhb8DxYk+MAKLBCFdcJSjbWjJ0PPR4gEwBERViDCR4GhgBrAR5msq6JP8yk+AcDHcwtlpk6XGg0FOJUQUP8d6U4DmYAaMLUZVq3kObUq1YeAbRAJEMBXNUGCV3pgnR94YibCSoixBrKsCDmrINK6VkwoQQNlKAQRJpCBdgmCAQdAgFM6QddBoECneI2DXm+jVk98Jg5hFMRVCDkIF8YBeXMVQCUfG1ViiC5ggqBAZTvhhBhARAWCqMIq0QAbKDgHAVz4RGMFQVqymtYiNCCEavuKiRu41gUGKMIXNyCTAuxgiSOojG5FS4i8lHYYoqMXWn/qiSrkUABSaMASEaKF3ILCqvC5rG+xaxEsuA60mtABHKhQgi2EkQFH2IIBFABQTsiObWGA7G8fYiPMmQ4aamMbFATM3ofcDHOEw5v/3gjBBAYLQ3RFaFzhJjyIIlg4GBgmhA4i/DgOC8LD172wRZggYhJvzsRyqHCKQWyRFdDtwNZbGyHEctcBI8Rk0oMBKJOhABNwbRBUsAgYkiHR7klPA/AlMgyyl0PUGgN4VMOcEYAGDRTorCrjjUMQkmFdhMgMzFB7hhayfFifPYS2yEAxQhCQhB13gWipykBwB3GDNyFkf8cgQkFhO4h/9eAZLYiDwQSBsIfQORkNcJphBUGDDHxlGSoowJ4HYa+H7GAZnkWInegGAA0k5hhKGIEDYDQIUz2Ey8kQgwse8gBrRmBdFzDDAna9gBzkoALADrawh01sYP8a2LxOtrKX/83sZVfA19CuQAucN4E6i5CjCMlAJZGxBYuM2RALoEF1NDADGAigAHrylLo95YJ2o/vd8NbTCDLQqA1sIAYiEEEM9o3vfOvbCPYO+Axm8KhJaQABg0K3AEzwBgngWRAVESAzmrBKBGS2EAFIEwNIQAEKJOBJVAq5yBPQ8ZJ73EpYytKWyKSllbM8S2gKgcxJbnIKHNkQIPBzAQjNjN7GwQQXnwYI3omQazmjCl1oURRYXVU/xyFO0ACCCscmgUszowEc2IIiMSKNBSgSIRuwkNjHTvayN2iYIwj6MxZA9AG5/e3TVDs0WBBmuNv97k+3ozUIwARs4/3vAZpBC4ZaDf8CtMACdDzPuQvwdcBfx0/rEQEAWnBKbYRgCUsAgRSkMIYxLKAHIGjCFVRABC6ogAUg4IADII+QMHDg9bCHfQf29ZARKCD2uLdrHBDQgyawIK4fEAIQNL+EHoB+CJrvwReykAC2xaMHX/80Ij5QEmsbIgJ1j0MYJvFweARglLVfyCHk/JCDGuILLKmBXNkyhII+xOiGACRCrFwV8GeIMyKd6EsHsbKS4ACgQNB4D8NzSBEAZEAGqiEHNzBrOREFhrAELJEBFKMu57FMBcgmrpYTNsB0cpCBHQEXmXYeBYBGkNEAbvYcFxcAXsMSDlhd6WFjkNED6eEDGeN0FgFkguD/BO7HEo82GKKTE+o3CPvEEg7gLdKEHt/GFn2mHnpVZiXRgwQwdeehATYVEommHgIAQSNxHksgCKGmHiwEFgGQdOsRXCH4HPAyPfXRBRwYEiBQH9oWBeixAwEwBffBH1Thc+rxArqXIFZAH/bxA/1lDyFgg+mhARuAHgJgLvchAKdGED7xd9FyHxZ4D23gePmBAIIREkQggJioHmrwEl/4ifXBZvcQAMNEilj4iPOQBZ6oiuixfQRxhLBISs4nDx6QiLV4HxxwD1Kwi/gRWPbghMDIStYnD7tTjPcBa/KgBMp4HxPQfe7AY8+IhdIVDw3gWtVYH/TnDlmwjfaxAVWogg60CI7pkQPxQAbZZ47nUWDvcAWvyI7+N4jocIXyqB4FIH7tEADadI/p8WDtsIT+qB7R6A5IMJBltH7lkFUIiR7uqA7f05DqAQDSWA7/IpHpsXPsUI4YyRJhmA4S1JHpgYPo4AS0J5LPIQI3dw5v2BHnFo/+WAOTZg4yhpLnYX6xEAgAOw==);\r\n -webkit-background-size: cover;\r\n background-size: cover;\r\n border-radius: 50%;\r\n margin: 0;\r\n padding: 0;\r\n}\r\n\r\n.bili-avatar * {\r\n margin: 0;\r\n padding: 0;\r\n}\r\n\r\n.bili-avatar-face {\r\n position: absolute;\r\n top: 50%;\r\n left: 50%;\r\n -webkit-transform: translate(-50%, -50%);\r\n -moz-transform: translate(-50%, -50%);\r\n -ms-transform: translate(-50%, -50%);\r\n -o-transform: translate(-50%, -50%);\r\n transform: translate(-50%, -50%);\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.bili-avatar-pendent-dom {\r\n height: 176.48%;\r\n width: 176.48%;\r\n position: absolute;\r\n top: -38.33%;\r\n left: -38.33%;\r\n overflow: hidden;\r\n}\r\n\r\n.bili-avatar-pendent-dom img {\r\n height: 100%;\r\n min-width: 100%;\r\n -webkit-user-select: none;\r\n -moz-user-select: none;\r\n -ms-user-select: none;\r\n user-select: none;\r\n}\r\n\r\n.bili-avatar-img {\r\n border: none;\r\n display: block;\r\n -o-object-fit: cover;\r\n object-fit: cover;\r\n image-rendering: -webkit-optimize-contrast;\r\n}\r\n\r\n.bili-avatar-img-radius {\r\n border-radius: 50%;\r\n}\r\n\r\n.bili-avatar-img[src=""],\r\n.bili-avatar-img:not([src]) {\r\n opacity: 0;\r\n}\r\n\r\n.bili-avatar-img.bili-avatar-img-error {\r\n display: none;\r\n}\r\n\r\n.bili-avatar-right-icon {\r\n width: 27.5%;\r\n height: 27.5%;\r\n position: absolute;\r\n right: 0;\r\n bottom: -1px;\r\n -webkit-background-size: cover;\r\n background-size: cover;\r\n image-rendering: -webkit-optimize-contrast;\r\n}\r\n\r\n.bili-avatar-nft-icon {\r\n position: absolute;\r\n width: 27.5%;\r\n height: 27.5%;\r\n right: -webkit-calc(27.5% - 1px);\r\n right: -moz-calc(27.5% - 1px);\r\n right: calc(27.5% - 1px);\r\n bottom: -1px;\r\n -webkit-background-size: cover;\r\n background-size: cover;\r\n image-rendering: -webkit-optimize-contrast;\r\n}\r\n\r\n@-webkit-keyframes bili-avatar {\r\n 0% {\r\n -webkit-transform: translate3d(0, 0, 0);\r\n transform: translateZ(0);\r\n }\r\n\r\n to {\r\n -webkit-transform: translate3d(-97.5%, 0, 0);\r\n transform: translate3d(-97.5%, 0, 0);\r\n }\r\n}\r\n\r\n@-moz-keyframes bili-avatar {\r\n 0% {\r\n -moz-transform: translate3d(0, 0, 0);\r\n transform: translateZ(0);\r\n }\r\n\r\n to {\r\n -moz-transform: translate3d(-97.5%, 0, 0);\r\n transform: translate3d(-97.5%, 0, 0);\r\n }\r\n}\r\n\r\n@keyframes bili-avatar {\r\n 0% {\r\n -webkit-transform: translate3d(0, 0, 0);\r\n -moz-transform: translate3d(0, 0, 0);\r\n transform: translateZ(0);\r\n }\r\n\r\n to {\r\n -webkit-transform: translate3d(-97.5%, 0, 0);\r\n -moz-transform: translate3d(-97.5%, 0, 0);\r\n transform: translate3d(-97.5%, 0, 0);\r\n }\r\n}\r\n\r\n.bili-avatar .bili-avatar-size-80 {\r\n width: 22px;\r\n height: 22px;\r\n bottom: -1px;\r\n}\r\n\r\n.bili-avatar .bili-avatar-size-60,\r\n.bili-avatar .bili-avatar-size-50,\r\n.bili-avatar .bili-avatar-size-48 {\r\n width: 18px;\r\n height: 18px;\r\n bottom: -1px;\r\n}\r\n\r\n.bili-avatar .bili-avatar-size-40,\r\n.bili-avatar .bili-avatar-size-36 {\r\n width: 14px;\r\n height: 14px;\r\n bottom: -1px;\r\n}\r\n\r\n.bili-avatar .bili-avatar-size-30,\r\n.bili-avatar .bili-avatar-size-24 {\r\n width: 12px;\r\n height: 12px;\r\n bottom: -1px;\r\n}\r\n\r\n.bili-avatar .bili-avatar-size-nft-80 {\r\n width: 22px;\r\n height: 22px;\r\n bottom: -1px;\r\n right: -webkit-calc(22px - 1px);\r\n right: -moz-calc(22px - 1px);\r\n right: 21px;\r\n}\r\n\r\n.bili-avatar .bili-avatar-size-nft-60,\r\n.bili-avatar .bili-avatar-size-nft-50,\r\n.bili-avatar .bili-avatar-size-nft-48 {\r\n width: 18px;\r\n height: 18px;\r\n bottom: -1px;\r\n right: -webkit-calc(18px - 1px);\r\n right: -moz-calc(18px - 1px);\r\n right: 17px;\r\n}\r\n\r\n.bili-avatar .bili-avatar-size-nft-40,\r\n.bili-avatar .bili-avatar-size-nft-36 {\r\n width: 14px;\r\n height: 14px;\r\n bottom: -1px;\r\n right: -webkit-calc(14px - 1px);\r\n right: -moz-calc(14px - 1px);\r\n right: 13px;\r\n}\r\n\r\n.bili-avatar .bili-avatar-size-nft-30,\r\n.bili-avatar .bili-avatar-size-nft-24 {\r\n width: 12px;\r\n height: 12px;\r\n bottom: -1px;\r\n right: -webkit-calc(12px - 1px);\r\n right: -moz-calc(12px - 1px);\r\n right: 11px;\r\n}\r\n\r\n.reply-image {\r\n width: var(--3414c33c);\r\n height: var(--822197ea);\r\n}\r\n\r\n.reply-image.b-img {\r\n background-color: inherit;\r\n}\r\n\r\n.reply-image.b-img img {\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.opacity-enter-active,\r\n.opacity-leave-active {\r\n transition: opacity 0.15s ease;\r\n}\r\n\r\n.opacity-enter-from,\r\n.opacity-leave-to {\r\n opacity: 0;\r\n}\r\n\r\n.reply-box {\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.reply-box .box-normal {\r\n display: flex;\r\n z-index: 2;\r\n}\r\n\r\n.reply-box .box-normal .reply-box-avatar {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 80px;\r\n height: 48px;\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp {\r\n position: relative;\r\n flex: 1;\r\n transition: 0.2s;\r\n border: 1px solid var(--line_regular);\r\n border-radius: 6px;\r\n background-color: var(--bg3);\r\n overflow-x: hidden;\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp.focus-within,\r\n.reply-box .box-normal .reply-box-warp:hover {\r\n border-color: var(--line_regular);\r\n background-color: var(--bg1);\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .textarea-wrap {\r\n padding: 8px 0;\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n border-radius: 6px;\r\n cursor: text;\r\n overflow: hidden;\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .textarea-wrap .vote-info {\r\n margin-left: 10px;\r\n margin-bottom: 4px;\r\n height: 20px;\r\n font-size: 12px;\r\n line-height: 17px;\r\n display: flex;\r\n align-items: center;\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .textarea-wrap .vote-info__tag {\r\n flex: none;\r\n padding: 2px 6px;\r\n border-radius: 2px;\r\n margin-right: 4px;\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .textarea-wrap .vote-info__tag--pink {\r\n background-color: var(--Pi1);\r\n color: var(--Pi5);\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .textarea-wrap .vote-info__tag--blue {\r\n background-color: var(--brand_blue_thin);\r\n color: var(--brand_blue);\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .textarea-wrap .vote-info__tag--gary {\r\n background-color: var(--graph_bg_regular);\r\n color: var(--text3);\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .textarea-wrap .vote-info__text {\r\n max-width: calc(100% - 68px);\r\n color: var(--text2);\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .textarea-wrap .vote-info__close {\r\n flex: none;\r\n margin-left: 4px;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .reply-input {\r\n padding: 0 8px;\r\n width: 100%;\r\n height: 100%;\r\n border: 1px solid var(--Ga1);\r\n border-radius: 6px;\r\n background-color: var(--bg3);\r\n font-family: inherit;\r\n line-height: 20px;\r\n color: var(--text1);\r\n resize: none;\r\n outline: none;\r\n overflow-y: scroll;\r\n overflow-x: hidden;\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .reply-input.focus,\r\n.reply-box .box-normal .reply-box-warp .reply-input:hover {\r\n background-color: var(--bg1);\r\n border-color: var(--graph_weak);\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .reply-box-textarea {\r\n padding: 0 8px;\r\n width: 100%;\r\n height: 32px;\r\n border: none;\r\n border-radius: 6px;\r\n background-color: transparent;\r\n font-family: inherit;\r\n font-size: 14px;\r\n line-height: 32px;\r\n color: var(--text1);\r\n resize: none;\r\n outline: none;\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .reply-box-textarea::placeholder {\r\n color: var(--text3);\r\n}\r\n\r\n.reply-box .box-normal .reply-box-warp .image-content-wrap {\r\n background: transparent;\r\n}\r\n\r\n.reply-box .box-expand {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n margin-left: 80px;\r\n margin-top: 10px;\r\n z-index: 1;\r\n height: 32px;\r\n transition: all 0.2s ease-in-out;\r\n}\r\n\r\n.reply-box .box-expand.hide {\r\n margin-top: 0;\r\n height: 0;\r\n overflow: hidden;\r\n transition: all 0.2s ease-in-out;\r\n}\r\n\r\n.reply-box .box-expand .box-left {\r\n display: flex;\r\n align-items: center;\r\n}\r\n\r\n.reply-box .box-expand .reply-box-emoji {\r\n width: 32px;\r\n height: 26px;\r\n margin-right: 6px;\r\n position: relative;\r\n}\r\n\r\n.reply-box .box-expand .reply-box-emoji .emoji-btn {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 100%;\r\n height: 100%;\r\n border: 1px solid var(--line_regular);\r\n border-radius: 4px;\r\n color: var(--text3);\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box .box-expand .at-btn {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n position: relative;\r\n width: 32px;\r\n height: 26px;\r\n margin-right: 6px;\r\n border: 1px solid var(--line_regular);\r\n border-radius: 4px;\r\n color: var(--text3);\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box .box-expand .image-btn {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n position: relative;\r\n width: 32px;\r\n height: 26px;\r\n border: 1px solid var(--line_regular);\r\n border-radius: 4px;\r\n color: var(--text3);\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box .box-expand .image-btn.disabled {\r\n opacity: 0.4;\r\n}\r\n\r\n.reply-box .box-expand .image-btn .image-upload-input {\r\n appearance: none;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n opacity: 0;\r\n font-size: 0;\r\n user-select: auto;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box .box-expand .forward-to-dynamic {\r\n display: flex;\r\n align-items: center;\r\n margin-left: 16px;\r\n font-size: 12px;\r\n color: var(--text3);\r\n}\r\n\r\n.reply-box .box-expand .forward-to-dynamic .forward-input,\r\n.reply-box .box-expand .forward-to-dynamic .forward-label {\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box .box-expand .reply-box-send {\r\n float: right;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n position: relative;\r\n width: 70px;\r\n height: 32px;\r\n border-radius: 6px;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box .box-expand .reply-box-send .send-text {\r\n position: absolute;\r\n z-index: 1;\r\n font-size: 16px;\r\n color: var(--text_white);\r\n}\r\n\r\n.reply-box .box-expand .reply-box-send:after {\r\n content: "";\r\n position: absolute;\r\n opacity: 0.5;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 4px;\r\n background-color: var(--brand_blue);\r\n}\r\n\r\n.reply-box .box-expand .reply-box-send:hover:after {\r\n opacity: 1;\r\n}\r\n\r\n.reply-box.box-active\r\n .box-normal\r\n .reply-box-warp\r\n .reply-box-textarea.send-active {\r\n line-height: normal;\r\n}\r\n\r\n.reply-box.box-active .reply-box-send.send-active:after {\r\n opacity: 1;\r\n}\r\n\r\n.reply-box.disabled .box-normal .reply-box-warp .disable-mask {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n z-index: 1;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 6px;\r\n font-size: 12px;\r\n color: var(--text3);\r\n background-color: var(--bg3);\r\n}\r\n\r\n.reply-box.disabled .box-normal .reply-box-warp .disable-mask .no-login-mask {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 100%;\r\n height: 100%;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box.disabled\r\n .box-normal\r\n .reply-box-warp\r\n .disable-mask\r\n .no-login-mask\r\n .login-btn {\r\n padding: 4px 9px;\r\n margin: 0 3px;\r\n border-radius: 4px;\r\n color: var(--text_white);\r\n background-color: var(--brand_blue);\r\n}\r\n\r\n.reply-box.disabled\r\n .box-normal\r\n .reply-box-warp\r\n .disable-mask\r\n .no-login-mask\r\n .login-btn:hover {\r\n background-color: var(--Lb4);\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box.disabled .reply-box-send .send-text {\r\n color: var(--text3);\r\n}\r\n\r\n.reply-box.disabled .reply-box-send:after {\r\n opacity: 1;\r\n background-color: var(--bg3);\r\n}\r\n\r\n.reply-box.fixed-box {\r\n position: relative;\r\n z-index: 2;\r\n padding: 15px 0;\r\n border-top: 0.5px solid var(--graph_bg_thick);\r\n background-color: var(--bg1);\r\n}\r\n\r\n.reply-content-container.fold .reply-content {\r\n display: -webkit-box;\r\n -webkit-box-orient: vertical;\r\n -webkit-line-clamp: 4;\r\n}\r\n\r\n.reply-content-container .reply-content {\r\n color: var(--text1);\r\n overflow: hidden;\r\n word-wrap: break-word;\r\n word-break: break-word;\r\n white-space: pre-wrap;\r\n line-height: 24px;\r\n vertical-align: baseline;\r\n}\r\n\r\n.reply-content-container .reply-content .note-prefix {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 1px 4px;\r\n border-radius: 4px;\r\n margin-right: 8px;\r\n font-size: 12px;\r\n color: var(--text3);\r\n line-height: 20px;\r\n vertical-align: bottom;\r\n background-color: var(--bg2);\r\n}\r\n\r\n.reply-content-container .reply-content .note-prefix .note-icon {\r\n width: 16px;\r\n height: 16px;\r\n}\r\n\r\n.reply-content-container .reply-content .top-icon {\r\n top: -2px;\r\n display: inline-flex;\r\n justify-content: center;\r\n align-items: center;\r\n position: relative;\r\n width: 30px;\r\n height: 18px;\r\n border: 1px solid var(--brand_pink);\r\n border-radius: 3px;\r\n margin-right: 5px;\r\n font-size: 12px;\r\n color: var(--brand_pink);\r\n}\r\n\r\n.reply-content-container .reply-content .emoji-small {\r\n vertical-align: text-bottom;\r\n}\r\n\r\n@media screen and (max-width: 1681px) {\r\n .reply-content-container .reply-content .emoji-small {\r\n width: 20px;\r\n height: 20px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1681px) {\r\n .reply-content-container .reply-content .emoji-small {\r\n width: 22px;\r\n height: 22px;\r\n }\r\n}\r\n\r\n.reply-content-container .reply-content .emoji-large {\r\n width: 50px;\r\n height: 50px;\r\n vertical-align: text-bottom;\r\n}\r\n\r\n.reply-content-container .reply-content .icon {\r\n width: 20px;\r\n height: 20px;\r\n vertical-align: text-top;\r\n}\r\n\r\n@media screen and (max-width: 1681px) {\r\n .reply-content-container .reply-content .icon {\r\n line-height: 24px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1681px) {\r\n .reply-content-container .reply-content .icon {\r\n line-height: 26px;\r\n }\r\n}\r\n\r\n.reply-content-container .reply-content .icon.search-word {\r\n width: 12px;\r\n display: inline-block;\r\n background-size: contain;\r\n background-repeat: no-repeat;\r\n}\r\n\r\n.reply-content-container .reply-content .jump-link {\r\n vertical-align: baseline;\r\n}\r\n\r\n@media screen and (max-width: 1681px) {\r\n .reply-content-container .reply-content .jump-link {\r\n line-height: 24px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1681px) {\r\n .reply-content-container .reply-content .jump-link {\r\n line-height: 26px;\r\n }\r\n}\r\n\r\n.reply-content-container .expand-content {\r\n color: var(--text_link);\r\n cursor: pointer;\r\n margin-left: 4px;\r\n}\r\n\r\n.reply-content-container .expand-content:hover {\r\n color: var(--brand_blue);\r\n}\r\n\r\n.sub-reply-item {\r\n position: relative;\r\n padding: 8px 0 8px 42px;\r\n border-radius: 4px;\r\n}\r\n\r\n@media screen and (max-width: 1681px) {\r\n .sub-reply-item {\r\n font-size: 15px;\r\n line-height: 24px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1681px) {\r\n .sub-reply-item {\r\n font-size: 16px;\r\n line-height: 26px;\r\n }\r\n}\r\n\r\n.sub-reply-item.show-reply {\r\n background-color: #dff6fb;\r\n animation-name: enterAnimation-jumpReply-1f8a4018;\r\n animation-duration: 2s;\r\n animation-delay: 3s;\r\n animation-fill-mode: forwards;\r\n}\r\n\r\n.sub-reply-item .sub-user-info {\r\n display: inline-flex;\r\n align-items: center;\r\n margin-right: 9px;\r\n line-height: 24px;\r\n vertical-align: baseline;\r\n white-space: nowrap;\r\n}\r\n\r\n.sub-reply-item .sub-user-info .sub-reply-avatar {\r\n position: absolute;\r\n left: 8px;\r\n cursor: pointer;\r\n}\r\n\r\n.sub-reply-item .sub-user-info .sub-user-name {\r\n font-family: PingFang SC, HarmonyOS_Medium, Helvetica Neue, Microsoft YaHei,\r\n sans-serif;\r\n font-weight: 500;\r\n margin-right: 5px;\r\n color: var(--3bab3096);\r\n cursor: pointer;\r\n}\r\n\r\n@media screen and (max-width: 1681px) {\r\n .sub-reply-item .sub-user-info .sub-user-name {\r\n font-size: 13px;\r\n line-height: 24px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1681px) {\r\n .sub-reply-item .sub-user-info .sub-user-name {\r\n font-size: 14px;\r\n line-height: 26px;\r\n }\r\n}\r\n\r\n@media (-webkit-max-device-pixel-ratio: 1) {\r\n .sub-reply-item .sub-user-info .sub-user-name {\r\n font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica,\r\n Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, sans-serif;\r\n }\r\n}\r\n\r\n.sub-reply-item .sub-user-info .sub-user-level {\r\n cursor: pointer;\r\n}\r\n\r\n.sub-reply-item .sub-user-info .sub-up-icon {\r\n cursor: default;\r\n}\r\n\r\n.sub-reply-item .sub-reply-info {\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n margin-top: 2px;\r\n font-size: 13px;\r\n color: var(--text3);\r\n}\r\n\r\n.sub-reply-item .sub-reply-info .sub-reply-time {\r\n margin-right: var(--7530c1e4);\r\n}\r\n\r\n.sub-reply-item .sub-reply-info .sub-reply-location {\r\n margin-right: 20px;\r\n}\r\n\r\n.sub-reply-item .sub-reply-info .sub-reply-like {\r\n display: flex;\r\n align-items: center;\r\n margin-right: 19px;\r\n cursor: pointer;\r\n}\r\n\r\n.sub-reply-item .sub-reply-info .sub-reply-like .sub-like-icon {\r\n margin-right: 5px;\r\n color: #9499a0;\r\n}\r\n\r\n.sub-reply-item .sub-reply-info .sub-reply-like .sub-like-icon:hover,\r\n.sub-reply-item .sub-reply-info .sub-reply-like .sub-like-icon.liked {\r\n color: #00aeec;\r\n}\r\n\r\n.sub-reply-item .sub-reply-info .sub-reply-dislike {\r\n display: flex;\r\n align-items: center;\r\n margin-right: 19px;\r\n}\r\n\r\n.sub-reply-item .sub-reply-info .sub-reply-dislike .sub-dislike-icon {\r\n color: #9499a0;\r\n cursor: pointer;\r\n}\r\n\r\n.sub-reply-item .sub-reply-info .sub-reply-dislike .sub-dislike-icon:hover,\r\n.sub-reply-item .sub-reply-info .sub-reply-dislike .sub-dislike-icon.disliked {\r\n color: #00aeec;\r\n}\r\n\r\n.sub-reply-item .sub-reply-info .sub-reply-btn {\r\n cursor: pointer;\r\n}\r\n\r\n.sub-reply-item .sub-reply-info .sub-reply-btn:hover {\r\n color: var(--brand_blue);\r\n}\r\n\r\n.sub-reply-item .sub-reply-info .sub-reply-operation-warp {\r\n position: absolute;\r\n right: 40px;\r\n opacity: 0;\r\n}\r\n\r\n.sub-reply-item:hover .sub-reply-info .sub-reply-operation-warp {\r\n opacity: 1;\r\n}\r\n\r\n@keyframes enterAnimation-jumpReply-1f8a4018 {\r\n 0% {\r\n background-color: #dff6fb;\r\n }\r\n\r\n to {\r\n background-color: #dff6fb00;\r\n }\r\n}\r\n\r\n.sub-reply-list .view-more {\r\n padding-left: 8px;\r\n font-size: 13px;\r\n color: var(--text3);\r\n}\r\n\r\n.sub-reply-list .view-more .view-more-default .view-more-btn {\r\n cursor: pointer;\r\n}\r\n\r\n.sub-reply-list .view-more .view-more-default .view-more-btn:hover {\r\n color: var(--brand_blue);\r\n}\r\n\r\n.sub-reply-list .view-more .view-more-pagination {\r\n color: var(--text1);\r\n}\r\n\r\n.sub-reply-list .view-more .view-more-pagination .pagination-page-count {\r\n margin-right: 10px;\r\n}\r\n\r\n.sub-reply-list .view-more .view-more-pagination .pagination-btn {\r\n margin: 0 4 0 14px;\r\n cursor: pointer;\r\n}\r\n\r\n.sub-reply-list .view-more .view-more-pagination .pagination-btn:hover {\r\n color: var(--brand_blue);\r\n}\r\n\r\n.sub-reply-list .view-more .view-more-pagination .pagination-page-number {\r\n margin: 0 4px;\r\n cursor: pointer;\r\n}\r\n\r\n.sub-reply-list .view-more .view-more-pagination .pagination-page-number:hover,\r\n.sub-reply-list\r\n .view-more\r\n .view-more-pagination\r\n .pagination-page-number.current-page {\r\n color: var(--brand_blue);\r\n}\r\n\r\n.sub-reply-list .view-more .view-more-pagination .pagination-page-dot {\r\n margin: 0 4px;\r\n cursor: default;\r\n}\r\n\r\n.image-exhibition {\r\n margin-top: 8px;\r\n user-select: none;\r\n}\r\n\r\n.image-exhibition .preview-image-container {\r\n max-width: var(--dacbf126);\r\n display: flex;\r\n flex-wrap: wrap;\r\n row-gap: var(--77b1c8ee);\r\n column-gap: var(--0c349aa2);\r\n}\r\n\r\n.image-exhibition .preview-image-container .image-item-wrap {\r\n display: flex;\r\n justify-content: center;\r\n position: relative;\r\n border-radius: var(--7fefecd2);\r\n overflow: hidden;\r\n cursor: zoom-in;\r\n}\r\n\r\n.image-exhibition .preview-image-container .image-item-wrap.vertical {\r\n flex-direction: column;\r\n}\r\n\r\n.image-exhibition .preview-image-container .image-item-wrap.extra-long {\r\n justify-content: start;\r\n}\r\n\r\n.image-exhibition .preview-image-container .image-item-wrap .more-image {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n position: absolute;\r\n right: 4px;\r\n bottom: 4px;\r\n height: 20px;\r\n padding: 0 6px;\r\n border-radius: 4px;\r\n font-size: 13px;\r\n color: var(--text_white);\r\n font-weight: 500;\r\n line-height: 18px;\r\n background: rgba(0, 0, 0, 0.7);\r\n}\r\n\r\n.image-exhibition\r\n .preview-image-container\r\n .client-image-item-warp:nth-child(3n + 1) {\r\n border-bottom-right-radius: 0;\r\n border-top-right-radius: 0;\r\n}\r\n\r\n.image-exhibition\r\n .preview-image-container\r\n .client-image-item-warp:nth-child(3n + 2) {\r\n border-radius: 0;\r\n}\r\n\r\n.image-exhibition\r\n .preview-image-container\r\n .client-image-item-warp:nth-child(3n + 3) {\r\n border-bottom-left-radius: 0;\r\n border-top-left-radius: 0;\r\n}\r\n\r\n.image-exhibition\r\n .preview-image-container\r\n .client-image-item-warp:nth-last-child(1) {\r\n border-bottom-right-radius: var(--7fefecd2);\r\n border-top-right-radius: var(--7fefecd2);\r\n}\r\n\r\n.image-exhibition\r\n .preview-image-container\r\n .expand-image-item-warp:nth-child(1) {\r\n border-radius: var(--7fefecd2) 0 0 0;\r\n}\r\n\r\n.image-exhibition\r\n .preview-image-container\r\n .expand-image-item-warp:nth-child(3) {\r\n border-radius: 0 var(--7fefecd2) 0 0;\r\n}\r\n\r\n.image-exhibition\r\n .preview-image-container\r\n .expand-image-item-warp:nth-child(7) {\r\n border-radius: 0 0 0 var(--7fefecd2);\r\n}\r\n\r\n.image-exhibition\r\n .preview-image-container\r\n .expand-image-item-warp:nth-child(9) {\r\n border-radius: 0 0 var(--7fefecd2) 0;\r\n}\r\n\r\n.image-exhibition\r\n .preview-image-container\r\n .expand-image-item-warp:nth-child(3n + 2) {\r\n border-radius: 0;\r\n}\r\n\r\n.image-exhibition\r\n .preview-image-container\r\n .expand-image-item-warp.expand-image-two-rows:nth-child(4) {\r\n border-radius: 0 0 0 var(--7fefecd2);\r\n}\r\n\r\n.image-exhibition\r\n .preview-image-container\r\n .expand-image-item-warp.expand-image-two-rows:nth-child(6) {\r\n border-radius: 0 0 var(--7fefecd2) 0;\r\n}\r\n\r\n.reply-user-sailing {\r\n height: 48px;\r\n}\r\n\r\n.vote-warp {\r\n display: flex;\r\n width: 100%;\r\n height: 80px;\r\n border: 0.5px solid var(--graph_bg_thick);\r\n border-radius: 4px;\r\n margin: 10px 0;\r\n}\r\n\r\n.vote-warp .vote-icon-warp {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n flex-basis: 80px;\r\n flex-shrink: 0;\r\n border-top-left-radius: 4px;\r\n border-bottom-left-radius: 4px;\r\n background-color: var(--brand_blue_thin);\r\n}\r\n\r\n.vote-warp .vote-icon-warp .vote-icon {\r\n width: 40px;\r\n height: 40px;\r\n}\r\n\r\n.vote-warp .vote-container {\r\n display: flex;\r\n align-items: center;\r\n flex: 1;\r\n border-top-right-radius: 4px;\r\n border-bottom-right-radius: 4px;\r\n background-color: var(--bg1);\r\n}\r\n\r\n.vote-warp .vote-container .vote-text-warp {\r\n flex: 1;\r\n padding-left: 15px;\r\n}\r\n\r\n.vote-warp .vote-container .vote-text-warp .vote-title {\r\n font-size: 14px;\r\n color: var(--text1);\r\n}\r\n\r\n.vote-warp .vote-container .vote-text-warp .vote-desc {\r\n margin-top: 10px;\r\n font-size: 12px;\r\n color: var(--text3);\r\n}\r\n\r\n.vote-warp .vote-container .vote-btn-warp {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n flex-basis: 90px;\r\n flex-shrink: 0;\r\n}\r\n\r\n.vote-warp .vote-container .vote-btn-warp .vote-btn {\r\n width: 54px;\r\n height: 28px;\r\n border-radius: 4px;\r\n font-size: 13px;\r\n text-align: center;\r\n line-height: 28px;\r\n color: var(--text_white);\r\n background-color: var(--brand_blue);\r\n cursor: pointer;\r\n}\r\n\r\n.vote-warp .vote-container .vote-btn-warp .vote-btn:hover {\r\n background-color: var(--Lb4);\r\n}\r\n\r\n.vote-dialog {\r\n max-height: 100vh;\r\n overflow-y: auto;\r\n}\r\n\r\n.vote-dialog::-webkit-scrollbar {\r\n width: 4px;\r\n border-radius: 4px;\r\n background-color: transparent;\r\n}\r\n\r\n.vote-dialog::-webkit-scrollbar-thumb {\r\n border-radius: 4px;\r\n background-color: var(--graph_bg_thick);\r\n transition: 0.3s ease-in-out;\r\n}\r\n\r\n.vote-dialog::-webkit-scrollbar-track {\r\n border-radius: 4px;\r\n background-color: transparent;\r\n}\r\n\r\n.vote-dialog .vote-iframe-warp {\r\n height: 600px;\r\n padding-top: 10px;\r\n border-top: 0.5px solid var(--graph_weak);\r\n}\r\n\r\n.vote-dialog .vote-iframe-warp .vote-iframe {\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.reply-item {\r\n position: relative;\r\n}\r\n\r\n.reply-item .login-limit-mask {\r\n display: none;\r\n position: absolute;\r\n top: 0;\r\n right: 0;\r\n width: 100%;\r\n height: 100%;\r\n z-index: 10;\r\n pointer-events: none;\r\n}\r\n\r\n.reply-item .login-limit-mask .mask-top {\r\n height: 80%;\r\n background: linear-gradient(\r\n 180deg,\r\n rgba(255, 255, 255, 0) 0%,\r\n var(--bg1) 100%\r\n );\r\n}\r\n\r\n.reply-item .login-limit-mask .mask-bottom {\r\n height: 20%;\r\n background: var(--bg1);\r\n}\r\n\r\n.reply-item.login-limit-reply-end .login-limit-mask {\r\n display: block;\r\n}\r\n\r\n.reply-item .root-reply-container {\r\n padding: 22px 0 0 80px;\r\n}\r\n\r\n.reply-item .root-reply-container.show-reply {\r\n animation-name: enterAnimation-jumpReply-7041f671;\r\n animation-duration: 5s;\r\n animation-fill-mode: forwards;\r\n}\r\n\r\n.reply-item .root-reply-container .root-reply-avatar {\r\n display: flex;\r\n justify-content: center;\r\n position: absolute;\r\n left: 0;\r\n width: 80px;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp {\r\n flex: 1;\r\n position: relative;\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .reply-decorate {\r\n position: absolute;\r\n top: 0;\r\n right: 0;\r\n user-select: none;\r\n transform: translateY(-15px);\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .reply-decorate\r\n .easter-egg-label {\r\n width: 82px;\r\n height: 36px;\r\n transform: translateY(6px);\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .reply-decorate\r\n .easter-egg-label\r\n img {\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .reply-decorate\r\n .selected-reply\r\n .selected-reply-icon {\r\n width: var(--213e47ca);\r\n height: var(--268890ba);\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .reply-decorate .user-sailing {\r\n display: flex;\r\n align-items: center;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .reply-decorate\r\n .user-sailing\r\n .user-sailing-img {\r\n height: 48px;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .reply-decorate\r\n .user-sailing\r\n .user-sailing-text {\r\n position: absolute;\r\n right: 0;\r\n font-size: 13px;\r\n color: var(--2bd55d12);\r\n line-height: 16px;\r\n word-break: keep-all;\r\n transform: scale(0.7);\r\n transform-origin: center center;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .reply-decorate\r\n .user-sailing\r\n .user-sailing-text\r\n .sailing-text {\r\n font-family: fanscard;\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .user-info {\r\n display: flex;\r\n align-items: center;\r\n margin-bottom: 4px;\r\n}\r\n\r\n@media screen and (max-width: 1681px) {\r\n .reply-item .root-reply-container .content-warp .user-info {\r\n font-size: 13px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1681px) {\r\n .reply-item .root-reply-container .content-warp .user-info {\r\n font-size: 14px;\r\n }\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .user-info .user-name {\r\n font-family: PingFang SC, HarmonyOS_Medium, Helvetica Neue, Microsoft YaHei,\r\n sans-serif;\r\n font-weight: 500;\r\n margin-right: 5px;\r\n color: var(--dc735352);\r\n cursor: pointer;\r\n}\r\n\r\n@media (-webkit-max-device-pixel-ratio: 1) {\r\n .reply-item .root-reply-container .content-warp .user-info .user-name {\r\n font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica,\r\n Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, sans-serif;\r\n }\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .user-info .user-level {\r\n cursor: pointer;\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .user-info .up-icon {\r\n cursor: default;\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .user-info .contractor-box {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n position: relative;\r\n width: var(--697d5c46);\r\n height: 12px;\r\n padding: 2px;\r\n border-radius: 2px;\r\n background-color: var(--brand_pink_thin);\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .user-info\r\n .contractor-box.originalFan {\r\n border: 0.5px solid var(--brand_pink);\r\n background-color: transparent;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .user-info\r\n .contractor-box\r\n .contractor-text {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n font-size: 16px;\r\n transform-origin: center center;\r\n transform: scale(0.5);\r\n position: absolute;\r\n color: var(--brand_pink);\r\n white-space: nowrap;\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .user-info .fan-badge {\r\n display: flex;\r\n align-items: center;\r\n height: 14px;\r\n padding-left: 5px;\r\n border: 0.5px solid var(--3d3b5a1e);\r\n border-radius: 10px;\r\n margin-left: 5px;\r\n background-image: var(--35269ce2);\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .user-info\r\n .fan-badge\r\n .badge-icon-wrap {\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n width: var(--1f5204fd);\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .user-info\r\n .fan-badge\r\n .badge-icon-wrap\r\n .badge-frist-icon {\r\n position: absolute;\r\n left: -8px;\r\n width: 20px;\r\n height: 20px;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .user-info\r\n .fan-badge\r\n .badge-icon-wrap\r\n .badge-second-icon {\r\n position: absolute;\r\n right: 0;\r\n width: 8px;\r\n height: 11px;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .user-info\r\n .fan-badge\r\n .badge-name-wrap {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n position: relative;\r\n width: var(--4f9eed68);\r\n height: 100%;\r\n margin-right: 4px;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .user-info\r\n .fan-badge\r\n .badge-name-wrap\r\n .badge-name {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n font-size: 18px;\r\n transform-origin: center center;\r\n transform: scale(0.5);\r\n position: absolute;\r\n top: 50%;\r\n left: 50%;\r\n color: var(--57e6be72);\r\n font-weight: 500;\r\n white-space: nowrap;\r\n transform: scale(0.5) translate(-50%, -50%);\r\n transform-origin: 0 0;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .user-info\r\n .fan-badge\r\n .badge-level-wrap {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n position: relative;\r\n width: 11.5px;\r\n height: 11.5px;\r\n border-radius: 50%;\r\n margin-right: 0.5px;\r\n background-color: var(--59f85baa);\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .user-info\r\n .fan-badge\r\n .badge-level-wrap\r\n .badge-level {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n font-size: 14px;\r\n transform-origin: center center;\r\n transform: scale(0.5);\r\n position: absolute;\r\n top: 52%;\r\n left: 50%;\r\n font-family: Reeji-CloudHuPo-GBK;\r\n color: var(--103312b6);\r\n font-weight: 500;\r\n white-space: nowrap;\r\n line-height: 1;\r\n transform: scale(0.5) translate(-50%, -43%);\r\n transform-origin: 0 0;\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .vote-info {\r\n margin-bottom: 4px;\r\n height: 20px;\r\n font-size: 12px;\r\n line-height: 17px;\r\n display: flex;\r\n align-items: center;\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .vote-info__tag {\r\n padding: 2px 6px;\r\n border-radius: 2px;\r\n margin-right: 4px;\r\n flex: none;\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .vote-info__tag--pink {\r\n background-color: var(--Pi1);\r\n color: var(--Pi5);\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .vote-info__tag--blue {\r\n background-color: var(--brand_blue_thin);\r\n color: var(--brand_blue);\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .vote-info__tag--gray {\r\n background-color: var(--graph_bg_regular);\r\n color: var(--text3);\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .vote-info__text {\r\n color: var(--Ga7_u);\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .root-reply {\r\n position: relative;\r\n padding: 2px 0;\r\n}\r\n\r\n@media screen and (max-width: 1681px) {\r\n .reply-item .root-reply-container .content-warp .root-reply {\r\n font-size: 15px;\r\n line-height: 24px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1681px) {\r\n .reply-item .root-reply-container .content-warp .root-reply {\r\n font-size: 16px;\r\n line-height: 26px;\r\n }\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-content-container {\r\n display: block;\r\n overflow: hidden;\r\n width: 100%;\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .root-reply .reply-info {\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n margin-top: 2px;\r\n font-size: 13px;\r\n color: var(--text3);\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-time {\r\n margin-right: var(--472bae2d);\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-location {\r\n margin-right: 20px;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-like {\r\n display: flex;\r\n align-items: center;\r\n margin-right: 19px;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-like\r\n .like-icon {\r\n margin-right: 5px;\r\n color: #9499a0;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-like\r\n .like-icon:hover,\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-like\r\n .like-icon.liked {\r\n color: #00aeec;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-dislike {\r\n display: flex;\r\n align-items: center;\r\n margin-right: 19px;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-dislike\r\n .dislike-icon {\r\n color: #9499a0;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-dislike\r\n .dislike-icon:hover,\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-dislike\r\n .dislike-icon.disliked {\r\n color: #00aeec;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-btn {\r\n cursor: pointer;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-btn:hover {\r\n color: var(--brand_blue);\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-operation-warp {\r\n position: absolute;\r\n right: 20px;\r\n display: none;\r\n}\r\n\r\n.reply-item .root-reply-container .content-warp .root-reply .reply-tag-list {\r\n display: flex;\r\n align-items: center;\r\n margin-top: 6px;\r\n font-size: 12px;\r\n line-height: 17px;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container\r\n .content-warp\r\n .root-reply\r\n .reply-tag-list\r\n .reply-tag-item {\r\n padding: 2px 6px;\r\n border-radius: 2px;\r\n margin-right: 10px;\r\n}\r\n\r\n.reply-item\r\n .root-reply-container:hover\r\n .content-warp\r\n .root-reply\r\n .reply-info\r\n .reply-operation-warp {\r\n display: block;\r\n}\r\n\r\n.reply-item .sub-reply-container {\r\n padding-left: 72px;\r\n}\r\n\r\n.reply-item .reply-box-container {\r\n padding: 25px 0 10px 80px;\r\n}\r\n\r\n.reply-item .bottom-line {\r\n margin-left: 80px;\r\n border-bottom: 1px solid var(--graph_bg_thick);\r\n margin-top: 14px;\r\n}\r\n\r\n.reply-item .reply-dynamic-card {\r\n position: absolute;\r\n z-index: 10;\r\n top: 30px;\r\n left: 400px;\r\n}\r\n\r\n@keyframes enterAnimation-jumpReply-7041f671 {\r\n 0% {\r\n background-color: #dff6fb;\r\n }\r\n\r\n to {\r\n background-color: #dff6fb00;\r\n }\r\n}\r\n\r\n.reply-list {\r\n margin-top: 14px;\r\n padding-bottom: 100px;\r\n}\r\n\r\n.reply-list .reply-end-mark {\r\n height: 100px;\r\n}\r\n\r\n.reply-list .reply-end,\r\n.reply-list .reply-loading,\r\n.reply-list .view-all-reply {\r\n margin-top: 20px;\r\n font-size: 13px;\r\n color: var(--text3);\r\n text-align: center;\r\n}\r\n\r\n.reply-list .view-all-reply:hover {\r\n color: var(--brand_blue);\r\n cursor: pointer;\r\n}\r\n\r\n.reply-list .login-prompt {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: calc(100% - 80px);\r\n height: 50px;\r\n margin: 16px 0 0 auto;\r\n border-radius: 6px;\r\n font-size: 14px;\r\n color: var(--brand_blue);\r\n background-color: var(--brand_blue_thin);\r\n transition: 0.2s;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-list .login-prompt:hover {\r\n background-color: var(--Lb2);\r\n}\r\n\r\n.user-card {\r\n position: absolute;\r\n top: var(--555c4a14);\r\n left: var(--8468e010);\r\n z-index: 10;\r\n width: 366px;\r\n border: 0.5px solid var(--graph_weak);\r\n border-radius: 8px;\r\n background-color: var(--bg1);\r\n box-shadow: 0 0 30px #0000001a;\r\n}\r\n\r\n.user-card .card-bg {\r\n width: 100%;\r\n height: 85px;\r\n border-radius: 8px 8px 0 0;\r\n overflow: hidden;\r\n background-image: var(--71924242);\r\n background-size: cover;\r\n background-repeat: no-repeat;\r\n background-position: center;\r\n}\r\n\r\n.user-card .user-card-avatar {\r\n display: flex;\r\n justify-content: center;\r\n position: absolute;\r\n width: 70px;\r\n margin-top: 10px;\r\n cursor: pointer;\r\n}\r\n\r\n.user-card .card-content {\r\n display: flex;\r\n flex-direction: column;\r\n padding: 12px 20px 16px 70px;\r\n}\r\n\r\n.user-card .card-content .card-user-info {\r\n display: flex;\r\n align-items: center;\r\n color: var(--text1);\r\n margin-bottom: 10px;\r\n}\r\n\r\n.user-card .card-content .card-user-info .card-user-name {\r\n max-width: 160px;\r\n margin-right: 5px;\r\n font-size: 16px;\r\n font-weight: 600;\r\n overflow: hidden;\r\n white-space: nowrap;\r\n text-overflow: ellipsis;\r\n color: var(--text1);\r\n color: var(--7ba58c95);\r\n text-decoration: none;\r\n}\r\n\r\n.user-card .card-content .card-user-info .card-user-sex {\r\n width: 16px;\r\n height: 16px;\r\n margin-right: 5px;\r\n}\r\n\r\n.user-card .card-content .card-user-info .card-user-level {\r\n margin-right: 5px;\r\n cursor: pointer;\r\n}\r\n\r\n.user-card .card-content .card-user-info .card-user-vip {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: var(--7a718880);\r\n height: 16px;\r\n padding: 1px 4px;\r\n border-radius: 2px;\r\n color: var(--612d8511);\r\n background-color: var(--29ab308e);\r\n cursor: default;\r\n}\r\n\r\n.user-card .card-content .card-user-info .card-user-vip .card-vip-text {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n font-size: 20px;\r\n transform-origin: center center;\r\n transform: scale(0.5);\r\n white-space: nowrap;\r\n font-style: normal;\r\n}\r\n\r\n.user-card .card-content .card-social-info {\r\n display: flex;\r\n align-items: center;\r\n font-size: 12px;\r\n color: var(--text1);\r\n}\r\n\r\n.user-card .card-content .card-social-info .card-user-attention,\r\n.user-card .card-content .card-social-info .card-user-fans,\r\n.user-card .card-content .card-social-info .card-user-like {\r\n margin-right: 18px;\r\n color: inherit;\r\n text-decoration: none;\r\n}\r\n\r\n.user-card\r\n .card-content\r\n .card-social-info\r\n .card-user-attention\r\n .social-info-title,\r\n.user-card .card-content .card-social-info .card-user-fans .social-info-title,\r\n.user-card .card-content .card-social-info .card-user-like .social-info-title {\r\n margin-left: 3px;\r\n color: var(--text3);\r\n}\r\n\r\n.user-card .card-content .card-verify-info {\r\n padding-top: 10px;\r\n font-size: 12px;\r\n color: var(--text3);\r\n}\r\n\r\n.user-card .card-content .card-verify-info .card-verify-icon {\r\n vertical-align: text-bottom;\r\n margin-right: 3px;\r\n}\r\n\r\n.user-card .card-content .card-sign {\r\n padding-top: 8px;\r\n font-size: 12px;\r\n color: var(--text3);\r\n word-break: break-all;\r\n}\r\n\r\n.user-card .card-content .card-btn-warp {\r\n display: flex;\r\n margin-top: 16px;\r\n font-size: 14px;\r\n}\r\n\r\n.user-card .card-content .card-btn-warp .card-attention-btn {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 100px;\r\n height: 30px;\r\n border-radius: 4px;\r\n margin-right: 8px;\r\n color: var(--text_white);\r\n background-color: var(--brand_blue);\r\n transition: 0.4s;\r\n cursor: pointer;\r\n}\r\n\r\n.user-card\r\n .card-content\r\n .card-btn-warp\r\n .card-attention-btn\r\n .cancel-attention-text {\r\n display: none;\r\n position: absolute;\r\n}\r\n\r\n.user-card .card-content .card-btn-warp .card-attention-btn.attention {\r\n color: var(--text2);\r\n background-color: var(--bg3);\r\n}\r\n\r\n.user-card\r\n .card-content\r\n .card-btn-warp\r\n .card-attention-btn.attention:hover\r\n .attention-text {\r\n display: none;\r\n}\r\n\r\n.user-card\r\n .card-content\r\n .card-btn-warp\r\n .card-attention-btn.attention:hover\r\n .cancel-attention-text {\r\n display: inline;\r\n}\r\n\r\n.user-card .card-content .card-btn-warp .card-message-btn {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 100px;\r\n height: 30px;\r\n border: 1px solid var(--graph_weak);\r\n border-radius: 4px;\r\n color: var(--text2);\r\n cursor: pointer;\r\n}\r\n\r\n.user-card .card-content .card-btn-warp .card-message-btn:hover {\r\n border-color: var(--brand_blue);\r\n color: var(--brand_blue);\r\n}\r\n\r\n.dynamic-card {\r\n display: flex;\r\n flex-direction: column;\r\n position: absolute;\r\n z-index: 10;\r\n top: var(--7b058890);\r\n left: 400px;\r\n width: 710px;\r\n height: 550px;\r\n border-radius: 6px;\r\n background-color: var(--bg1);\r\n box-shadow: 0 0 25px #00000026;\r\n}\r\n\r\n.dynamic-card .card-header {\r\n display: flex;\r\n align-items: center;\r\n flex-basis: 50px;\r\n padding: 0 10px;\r\n border-bottom: 0.5px solid var(--line_light);\r\n}\r\n\r\n.dynamic-card .card-header .card-title {\r\n flex: 1;\r\n text-align: center;\r\n font-size: 16px;\r\n color: var(--text1);\r\n}\r\n\r\n.dynamic-card .card-header .close-card {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 30px;\r\n height: 30px;\r\n border-radius: 6px;\r\n color: var(--text2);\r\n transition: 0.2s;\r\n cursor: pointer;\r\n}\r\n\r\n.dynamic-card .card-header .close-card:hover {\r\n background-color: var(--bg3);\r\n}\r\n\r\n.dynamic-card .card-content {\r\n flex: 1;\r\n}\r\n\r\n.dynamic-card .card-content::-webkit-scrollbar {\r\n width: 4px;\r\n border-radius: 4px;\r\n background-color: transparent;\r\n}\r\n\r\n.dynamic-card .card-content::-webkit-scrollbar-thumb {\r\n border-radius: 4px;\r\n background-color: var(--graph_bg_thick);\r\n transition: 0.3s ease-in-out;\r\n}\r\n\r\n.dynamic-card .card-content::-webkit-scrollbar-track {\r\n border-radius: 4px;\r\n background-color: transparent;\r\n}\r\n\r\n.dynamic-card .card-content .dynamic-card-iframe {\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.reply-view-image {\r\n position: fixed;\r\n z-index: 999999;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background: rgba(24, 25, 28, 0.85);\r\n transform: scale(1);\r\n user-select: none;\r\n cursor: default;\r\n -webkit-user-select: none;\r\n -moz-user-select: none;\r\n -ms-user-select: none;\r\n -webkit-user-drag: none;\r\n}\r\n\r\n.reply-view-image,\r\n.reply-view-image * {\r\n box-sizing: border-box;\r\n}\r\n\r\n.reply-view-image .operation-btn .operation-btn-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n position: absolute;\r\n z-index: 2;\r\n width: 42px;\r\n height: 42px;\r\n border-radius: 50%;\r\n color: var(--text_white);\r\n background: rgba(0, 0, 0, 0.58);\r\n transition: 0.2s;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-view-image .operation-btn .operation-btn-icon:hover {\r\n color: var(--brand_pink);\r\n}\r\n\r\n.reply-view-image .operation-btn .operation-btn-icon.close-container {\r\n top: 16px;\r\n right: 16px;\r\n}\r\n\r\n.reply-view-image .operation-btn .operation-btn-icon.last-image {\r\n top: 50%;\r\n left: 16px;\r\n transform: translateY(-50%);\r\n}\r\n\r\n.reply-view-image .operation-btn .operation-btn-icon.next-image {\r\n top: 50%;\r\n right: 16px;\r\n transform: translateY(-50%);\r\n}\r\n\r\n.reply-view-image .show-image-wrap {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n position: absolute;\r\n width: 100%;\r\n height: 100%;\r\n max-height: 100%;\r\n padding: 0 100px;\r\n overflow: auto;\r\n}\r\n\r\n.reply-view-image .show-image-wrap .loading-svga {\r\n position: absolute;\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n width: 42px;\r\n height: 42px;\r\n}\r\n\r\n.reply-view-image .show-image-wrap.vertical {\r\n flex-direction: column;\r\n justify-content: var(--c186e874);\r\n}\r\n\r\n.reply-view-image .show-image-wrap .image-content {\r\n width: calc(100vw - 200px);\r\n max-width: var(--34114ac9);\r\n -webkit-user-drag: none;\r\n}\r\n\r\n.reply-view-image .preview-list {\r\n display: flex;\r\n align-items: center;\r\n position: absolute;\r\n left: 50%;\r\n bottom: 30px;\r\n z-index: 2;\r\n padding: 6px 10px;\r\n border-radius: 8px;\r\n background: rgba(24, 25, 28, 0.8);\r\n backdrop-filter: blur(20px);\r\n transform: translate(-50%);\r\n}\r\n\r\n.reply-view-image .preview-list .preview-item-box {\r\n padding: 1px;\r\n border: 2px solid transparent;\r\n border-radius: 8px;\r\n transition: 0.3s;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-view-image .preview-list .preview-item-box.active {\r\n border-color: var(--brand_pink);\r\n}\r\n\r\n.reply-view-image .preview-list .preview-item-box .preview-item-wrap {\r\n display: flex;\r\n justify-content: center;\r\n overflow: hidden;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 6px;\r\n}\r\n\r\n.reply-view-image .preview-list .preview-item-box .preview-item-wrap.vertical {\r\n flex-direction: column;\r\n}\r\n\r\n.reply-view-image\r\n .preview-list\r\n .preview-item-box\r\n .preview-item-wrap.extra-long {\r\n justify-content: start;\r\n}\r\n\r\n.reply-view-image\r\n .preview-list\r\n .preview-item-box\r\n .preview-item-wrap\r\n .item-content {\r\n -webkit-user-drag: none;\r\n}\r\n\r\n.reply-view-image--transition-enter-active,\r\n.reply-view-image--transition-leave-active {\r\n transition: all 0.3s ease;\r\n}\r\n\r\n.reply-view-image--transition-enter-from,\r\n.reply-view-image--transition-leave-to {\r\n transform: scale(0.4);\r\n opacity: 0;\r\n}\r\n\r\n.reply-warp {\r\n position: relative;\r\n}\r\n\r\n.reply-warp .fixed-reply-box {\r\n position: fixed;\r\n bottom: 0;\r\n left: var(--3e88ddc5);\r\n z-index: 10;\r\n width: var(--d9a0b070);\r\n}\r\n\r\n.reply-warp .fixed-reply-box .reply-box-shadow {\r\n position: absolute;\r\n top: -10px;\r\n z-index: 1;\r\n width: 100%;\r\n height: 36px;\r\n border-radius: 50%;\r\n background-color: #00000014;\r\n filter: blur(10px);\r\n}\r\n\r\n.reply-warp .fixed-reply-box--transition-enter-active,\r\n.reply-warp .fixed-reply-box--transition-leave-active {\r\n transition: opacity 0.5s ease;\r\n}\r\n\r\n.reply-warp .fixed-reply-box--transition-enter-from,\r\n.reply-warp .fixed-reply-box--transition-leave-to {\r\n opacity: 0;\r\n}\r\n\r\n.bili-comment.browser-pc {\r\n background-color: var(--bg1);\r\n}\r\n\r\n.bili-comment.browser-pc * {\r\n font-family: PingFang SC, HarmonyOS_Regular, Helvetica Neue, Microsoft YaHei,\r\n sans-serif;\r\n font-weight: 400;\r\n box-sizing: border-box;\r\n -webkit-font-smoothing: antialiased;\r\n}\r\n\r\n@media (-webkit-max-device-pixel-ratio: 1) {\r\n .bili-comment.browser-pc * {\r\n font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica,\r\n Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, sans-serif;\r\n }\r\n}\r\n\r\n.bili-comment.browser-pc * ul {\r\n padding: 0;\r\n margin: 0;\r\n list-style: none;\r\n}\r\n\r\n.bili-comment.browser-pc * a {\r\n text-decoration: none;\r\n background-color: transparent;\r\n color: var(--text_link);\r\n cursor: pointer;\r\n}\r\n\r\n.bili-comment.browser-pc * a:hover {\r\n color: var(--Lb4);\r\n}\r\n\r\n.bili-comment.browser-pc * i {\r\n font-style: normal;\r\n}\r\n\r\n.bili-comment.browser-pc * p {\r\n margin: 0;\r\n padding: 0;\r\n}\r\n\r\n.bili-comment.browser-pc .comment-container {\r\n animation-name: enterAnimation-commentContainer;\r\n animation-duration: 1s;\r\n animation-fill-mode: forwards;\r\n}\r\n\r\n.reply-operation-client {\r\n display: inline-flex;\r\n position: relative;\r\n}\r\n\r\n.reply-operation-client .operation-icon {\r\n border-radius: 4px;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-operation-client .operation-icon:hover {\r\n background-color: var(--graph_bg_thick);\r\n}\r\n\r\n.reply-operation-client .operation-list {\r\n display: flex;\r\n flex-direction: column;\r\n position: absolute;\r\n top: 10px;\r\n right: 0;\r\n z-index: 10;\r\n width: 180px;\r\n padding: 12px 0;\r\n border-radius: 6px;\r\n font-size: 14px;\r\n color: var(--text2);\r\n background-color: var(--bg1_float);\r\n box-shadow: 0 0 5px #0003;\r\n}\r\n\r\n.reply-operation-client .operation-list .operation-option {\r\n display: flex;\r\n align-items: center;\r\n height: 40px;\r\n padding: 0 15px;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-operation-client .operation-list .operation-option:hover {\r\n background-color: var(--graph_bg_thick);\r\n}\r\n\r\n.reply-operation-client .operation-list .delete-reply-modal {\r\n position: absolute;\r\n top: 0;\r\n left: 50%;\r\n width: auto;\r\n padding: 10px 20px;\r\n border: 1px solid var(--graph_bg_thick);\r\n border-radius: 8px;\r\n margin-bottom: 100px;\r\n font-size: 12px;\r\n line-height: 12px;\r\n text-align: center;\r\n white-space: nowrap;\r\n background-color: var(--bg1);\r\n box-shadow: 0 0 5px #0003;\r\n transform: translate(-50%, -100%);\r\n}\r\n\r\n.reply-operation-client .operation-list .delete-reply-modal .delete-reply-btn {\r\n display: flex;\r\n justify-content: center;\r\n}\r\n\r\n.reply-operation-client\r\n .operation-list\r\n .delete-reply-modal\r\n .delete-reply-btn\r\n .comfirm-delete {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 40px;\r\n height: 20px;\r\n border-radius: 4px;\r\n margin-right: 20px;\r\n color: var(--text_white);\r\n background-color: var(--brand_blue);\r\n}\r\n\r\n.reply-operation-client\r\n .operation-list\r\n .delete-reply-modal\r\n .delete-reply-btn\r\n .comfirm-delete:hover {\r\n background-color: var(--Lb4);\r\n}\r\n\r\n.reply-operation-client\r\n .operation-list\r\n .delete-reply-modal\r\n .delete-reply-btn\r\n .cancel-delete {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 40px;\r\n height: 20px;\r\n}\r\n\r\n.reply-operation-client\r\n .operation-list\r\n .delete-reply-modal\r\n .delete-reply-btn\r\n .cancel-delete:hover {\r\n color: var(--brand_blue);\r\n}\r\n\r\n.select-reply-dialog-client .select-dialog-content {\r\n text-align: left;\r\n}\r\n\r\n.select-reply-dialog-client .cancel-select-reply {\r\n width: 130px;\r\n margin-right: 20px;\r\n}\r\n\r\n.select-reply-dialog-client .comfirm-select-reply {\r\n width: 130px;\r\n}\r\n\r\n.close-reply-dialog-client .close-reply-dialog-content {\r\n text-align: left;\r\n}\r\n\r\n.close-reply-dialog-client .cancel-close-reply {\r\n width: 130px;\r\n margin-right: 20px;\r\n}\r\n\r\n.close-reply-dialog-client .comfirm-close-reply {\r\n width: 130px;\r\n}\r\n\r\n.close-danmaku-dialog-client .close-danmaku-dialog-content {\r\n text-align: left;\r\n}\r\n\r\n.close-danmaku-dialog-client .cancel-close-danmaku {\r\n width: 130px;\r\n margin-right: 20px;\r\n}\r\n\r\n.close-danmaku-dialog-client .comfirm-close-danmaku {\r\n width: 130px;\r\n}\r\n\r\n.blacklist-dialog-client .blacklist-dialog-content {\r\n text-align: center;\r\n}\r\n\r\n.blacklist-dialog-client .comfirm-pull-blacklist {\r\n margin-right: 20px;\r\n}\r\n\r\n.reply-header-client .reply-notice {\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n height: 40px;\r\n padding: 11px 14px;\r\n margin-bottom: 10px;\r\n font-size: 12px;\r\n border-radius: 2px;\r\n color: var(--text_notice);\r\n background-color: var(--Or0);\r\n cursor: pointer;\r\n}\r\n\r\n.reply-header-client .reply-notice .notice-content {\r\n flex: 1;\r\n position: relative;\r\n padding: 0 5px;\r\n line-height: 18px;\r\n vertical-align: top;\r\n word-wrap: break-word;\r\n word-break: break-all;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n transition: 2s;\r\n}\r\n\r\n.reply-header-client .reply-navigation {\r\n margin: 12px 0;\r\n}\r\n\r\n.reply-header-client .reply-navigation .nav-bar {\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n list-style: none;\r\n margin: 0;\r\n padding: 0;\r\n}\r\n\r\n.reply-header-client .reply-navigation .nav-bar .nav-select-reply {\r\n font-size: 12px;\r\n color: var(--text1);\r\n}\r\n\r\n.reply-header-client .reply-navigation .nav-bar .nav-sort {\r\n display: flex;\r\n align-items: center;\r\n font-size: 12px;\r\n color: var(--text3);\r\n}\r\n\r\n.reply-header-client .reply-navigation .nav-bar .nav-sort .part-symbol {\r\n height: 10px;\r\n margin: 0 8px;\r\n border-left: solid 1px;\r\n}\r\n\r\n.reply-header-client .reply-navigation .nav-bar .nav-sort .hot-sort {\r\n cursor: pointer;\r\n}\r\n\r\n.reply-header-client .reply-navigation .nav-bar .nav-sort .hot-sort:hover {\r\n color: var(--brand_blue);\r\n}\r\n\r\n.reply-header-client .reply-navigation .nav-bar .nav-sort .time-sort {\r\n cursor: pointer;\r\n}\r\n\r\n.reply-header-client .reply-navigation .nav-bar .nav-sort .time-sort:hover {\r\n color: var(--brand_blue);\r\n}\r\n\r\n.reply-header-client .reply-navigation .nav-bar .nav-sort.hot .hot-sort,\r\n.reply-header-client .reply-navigation .nav-bar .nav-sort.time .time-sort {\r\n color: var(--text1);\r\n}\r\n\r\n.reply-header-client .reply-navigation .nav-operation-warp {\r\n position: absolute;\r\n right: 0;\r\n}\r\n\r\n.reply-box-client {\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.reply-box-client .reply-box-warp {\r\n position: relative;\r\n flex: 1;\r\n}\r\n\r\n.reply-box-client .reply-box-warp .reply-box-textarea {\r\n width: 100%;\r\n height: 32px;\r\n padding: 5px 12px;\r\n border: 1px solid transparent;\r\n border-radius: 6px;\r\n line-height: 20px;\r\n color: var(--text1);\r\n background-color: var(--bg2);\r\n resize: none;\r\n outline: none;\r\n transition: 0.2s;\r\n}\r\n\r\n.reply-box-client .reply-box-warp .reply-box-textarea::placeholder {\r\n color: var(--text4);\r\n}\r\n\r\n.reply-box-client .reply-box-warp .reply-box-textarea.focus,\r\n.reply-box-client .reply-box-warp .reply-box-textarea:hover {\r\n border-color: var(--brand_pink);\r\n}\r\n\r\n.reply-box-client .box-operation-warp {\r\n display: flex;\r\n align-items: center;\r\n margin-top: 10px;\r\n height: 32px;\r\n}\r\n\r\n.reply-box-client .box-operation-warp .reply-box-emoji {\r\n position: relative;\r\n margin-right: auto;\r\n}\r\n\r\n.reply-box-client .box-operation-warp .reply-box-emoji .box-emoji-icon {\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box-client .box-operation-warp .reply-box-send {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n position: relative;\r\n width: 70px;\r\n height: 100%;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box-client .box-operation-warp .reply-box-send .send-text {\r\n position: absolute;\r\n z-index: 1;\r\n color: var(--text_white);\r\n}\r\n\r\n.reply-box-client .box-operation-warp .reply-box-send:after {\r\n content: "";\r\n position: absolute;\r\n opacity: 0.5;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 4px;\r\n background-color: var(--brand_pink);\r\n}\r\n\r\n.reply-box-client .box-operation-warp .reply-box-send:hover:after {\r\n opacity: 1;\r\n}\r\n\r\n.reply-box-client.box-active .reply-box-warp .reply-box-textarea {\r\n height: 60px;\r\n}\r\n\r\n.reply-box-client.box-active .reply-box-send.send-active:after {\r\n opacity: 1;\r\n}\r\n\r\n.reply-box-client.disabled .reply-box-warp .disable-mask {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n z-index: 1;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 6px;\r\n font-size: 12px;\r\n color: var(--text3);\r\n background-color: var(--bg3);\r\n}\r\n\r\n.reply-box-client.disabled .reply-box-warp .disable-mask .no-login-mask {\r\n cursor: pointer;\r\n}\r\n\r\n.reply-box-client.disabled .box-operation-warp .reply-box-send {\r\n cursor: not-allowed;\r\n}\r\n\r\n.reply-box-client.disabled .box-operation-warp .reply-box-send .send-text {\r\n color: var(--text3);\r\n}\r\n\r\n.reply-box-client.disabled .box-operation-warp .reply-box-send:after {\r\n opacity: 1;\r\n background-color: var(--bg3);\r\n}\r\n\r\n.note-prefix {\r\n vertical-align: -3px;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 0 3px;\r\n line-height: 19px;\r\n border-radius: 4px;\r\n margin-right: 6px;\r\n font-size: 12px;\r\n color: var(--text3);\r\n background-color: var(--bg2);\r\n}\r\n\r\n.note-prefix .note-icon {\r\n width: 16px;\r\n height: 16px;\r\n}\r\n\r\n.reply-content-client {\r\n color: var(--text1);\r\n overflow: hidden;\r\n word-wrap: break-word;\r\n word-break: break-word;\r\n white-space: pre-wrap;\r\n vertical-align: baseline;\r\n transition: 0.2s;\r\n}\r\n\r\n.reply-content-client.root {\r\n line-height: 25px;\r\n}\r\n\r\n.reply-content-client.need-view-more {\r\n display: -webkit-box;\r\n -webkit-box-orient: vertical;\r\n overflow: hidden;\r\n}\r\n\r\n.reply-content-client.sub {\r\n line-height: 20px;\r\n}\r\n\r\n.reply-content-client .top-icon {\r\n display: inline-flex;\r\n justify-content: center;\r\n align-items: center;\r\n position: relative;\r\n width: 30px;\r\n height: 18px;\r\n border: 1px solid var(--brand_pink);\r\n border-radius: 3px;\r\n margin-right: 5px;\r\n font-size: 12px;\r\n color: var(--brand_pink);\r\n vertical-align: 1px;\r\n}\r\n\r\n.reply-content-client .emoji-small {\r\n width: 20px;\r\n height: 20px;\r\n vertical-align: text-bottom;\r\n}\r\n\r\n.reply-content-client .emoji-large {\r\n width: 36px;\r\n height: 36px;\r\n vertical-align: text-bottom;\r\n}\r\n\r\n.reply-content-client .jump-link {\r\n vertical-align: baseline;\r\n}\r\n\r\n.reply-content-client .icon {\r\n width: 20px;\r\n height: 20px;\r\n vertical-align: text-top;\r\n}\r\n\r\n.reply-content-client .icon.vote {\r\n width: 16px;\r\n height: 16px;\r\n margin-right: 3px;\r\n vertical-align: text-bottom;\r\n}\r\n\r\n.reply-content-client .icon.search-word {\r\n width: 12px;\r\n display: inline-block;\r\n background-size: contain;\r\n background-repeat: no-repeat;\r\n}\r\n\r\n.view-more-reply {\r\n font-size: 12px;\r\n color: var(--text_link);\r\n line-height: 17px;\r\n cursor: pointer;\r\n}\r\n\r\n.view-more-reply:hover {\r\n color: var(--Lb4);\r\n}\r\n\r\n.sub-reply-item-client {\r\n display: -webkit-box;\r\n -webkit-box-orient: vertical;\r\n -webkit-line-clamp: 2;\r\n position: relative;\r\n max-height: 42px;\r\n padding: 3px 0;\r\n font-size: 14px;\r\n overflow: hidden;\r\n}\r\n\r\n.sub-reply-item-client .sub-user-info {\r\n display: inline-flex;\r\n align-items: center;\r\n color: var(--text2);\r\n line-height: 20px;\r\n vertical-align: baseline;\r\n white-space: nowrap;\r\n}\r\n\r\n.sub-reply-item-client .sub-user-info .sub-user-name {\r\n margin-right: 5px;\r\n font-size: 14px;\r\n cursor: pointer;\r\n}\r\n\r\n.sub-reply-item-client .sub-user-info .sub-up-icon {\r\n margin-right: 4px;\r\n cursor: default;\r\n}\r\n\r\n.sub-reply-list-client {\r\n border-radius: 4px;\r\n padding: 7px 10px;\r\n margin-top: 12px;\r\n background-color: var(--bg2_float);\r\n}\r\n\r\n.sub-reply-list-client .view-more {\r\n margin-top: 4px;\r\n cursor: pointer;\r\n}\r\n\r\n.sub-reply-list-client .view-more .view-more-text {\r\n font-size: 12px;\r\n color: var(--text_link);\r\n}\r\n\r\n.sub-reply-list-client .view-more .view-more-text:hover {\r\n color: var(--Lb4);\r\n}\r\n\r\n.content-warp--blacklist .reply-content {\r\n display: inline-flex;\r\n align-items: center;\r\n padding: 4px;\r\n border-radius: 4px;\r\n color: var(--text1);\r\n background-color: var(--bg2_float);\r\n}\r\n\r\n.content-warp--blacklist .reply-content .ban-icon {\r\n margin-right: 4px;\r\n}\r\n\r\n.content-warp--blacklist .reply-header {\r\n display: flex;\r\n align-items: center;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.content-warp--blacklist .reply-header .root-reply-avatar {\r\n display: flex;\r\n justify-content: center;\r\n position: absolute;\r\n left: 0;\r\n cursor: pointer;\r\n}\r\n\r\n.content-warp--blacklist .reply-header .root-reply-avatar .blacklist-avatar {\r\n width: 30px;\r\n height: 30px;\r\n}\r\n\r\n.content-warp--blacklist .reply-header .reply-info .balcklist-name {\r\n color: var(--text1);\r\n}\r\n\r\n.reply-item-client {\r\n position: relative;\r\n padding: 10px 0 14px 42px;\r\n border-bottom: 1px solid var(--line_light);\r\n}\r\n\r\n.reply-item-client .content-warp {\r\n flex: 1;\r\n position: relative;\r\n}\r\n\r\n.reply-item-client .content-warp .reply-header {\r\n display: flex;\r\n align-items: center;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.reply-item-client .content-warp .reply-header .root-reply-avatar {\r\n display: flex;\r\n justify-content: center;\r\n position: absolute;\r\n left: -42px;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-item-client .content-warp .reply-header .reply-info {\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.reply-item-client .content-warp .reply-header .reply-info .user-info {\r\n display: flex;\r\n align-items: center;\r\n font-size: 13px;\r\n color: var(--text2);\r\n}\r\n\r\n.reply-item-client\r\n .content-warp\r\n .reply-header\r\n .reply-info\r\n .user-info\r\n .user-name {\r\n margin-right: 5px;\r\n color: var(--be794234);\r\n cursor: pointer;\r\n}\r\n\r\n.reply-item-client\r\n .content-warp\r\n .reply-header\r\n .reply-info\r\n .user-info\r\n .user-level {\r\n margin-right: 5px;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-item-client .content-warp .reply-header .reply-info .user-info .up-icon {\r\n cursor: default;\r\n}\r\n\r\n.reply-item-client .content-warp .reply-header .reply-info .reply-time {\r\n font-size: 12px;\r\n color: var(--text3);\r\n}\r\n\r\n.reply-item-client .content-warp .root-reply {\r\n position: relative;\r\n font-size: 15px;\r\n line-height: 25px;\r\n transition: 0.2s;\r\n}\r\n\r\n.reply-item-client .content-warp .root-reply .reply-operation-warp {\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n margin-top: 12px;\r\n font-size: 13px;\r\n color: var(--text3);\r\n line-height: 16px;\r\n}\r\n\r\n.reply-item-client .content-warp .root-reply .reply-operation-warp .reply-like {\r\n display: flex;\r\n align-items: center;\r\n margin-right: 19px;\r\n cursor: pointer;\r\n}\r\n\r\n.reply-item-client\r\n .content-warp\r\n .root-reply\r\n .reply-operation-warp\r\n .reply-like\r\n .like-icon {\r\n margin-right: 5px;\r\n color: var(--text3);\r\n}\r\n\r\n.reply-item-client\r\n .content-warp\r\n .root-reply\r\n .reply-operation-warp\r\n .reply-like\r\n .like-icon:hover,\r\n.reply-item-client\r\n .content-warp\r\n .root-reply\r\n .reply-operation-warp\r\n .reply-like\r\n .like-icon.liked {\r\n color: var(--brand_pink);\r\n}\r\n\r\n.reply-item-client\r\n .content-warp\r\n .root-reply\r\n .reply-operation-warp\r\n .reply-dislike {\r\n display: flex;\r\n align-items: center;\r\n margin-right: 19px;\r\n}\r\n\r\n.reply-item-client\r\n .content-warp\r\n .root-reply\r\n .reply-operation-warp\r\n .reply-dislike\r\n .dislike-icon {\r\n color: var(--text3);\r\n cursor: pointer;\r\n}\r\n\r\n.reply-item-client\r\n .content-warp\r\n .root-reply\r\n .reply-operation-warp\r\n .reply-dislike\r\n .dislike-icon:hover,\r\n.reply-item-client\r\n .content-warp\r\n .root-reply\r\n .reply-operation-warp\r\n .reply-dislike\r\n .dislike-icon.disliked {\r\n color: var(--brand_pink);\r\n}\r\n\r\n.reply-item-client .content-warp .root-reply .reply-operation-warp .reply-icon {\r\n color: var(--text3);\r\n cursor: pointer;\r\n}\r\n\r\n.reply-item-client\r\n .content-warp\r\n .root-reply\r\n .reply-operation-warp\r\n .reply-icon:hover {\r\n color: var(--brand_pink);\r\n}\r\n\r\n.reply-item-client\r\n .content-warp\r\n .root-reply\r\n .reply-operation-warp\r\n .more-operation {\r\n display: none;\r\n position: absolute;\r\n right: 20px;\r\n}\r\n\r\n.reply-item-client .content-warp .reply-item-box {\r\n margin-top: 12px;\r\n}\r\n\r\n.reply-item-client .content-warp .reply-tag-list {\r\n display: flex;\r\n align-items: center;\r\n margin-top: 12px;\r\n font-size: 12px;\r\n line-height: 14px;\r\n}\r\n\r\n.reply-item-client .content-warp .reply-tag-list .reply-tag-item {\r\n padding: 5px 6px;\r\n border-radius: 2px;\r\n margin-right: 10px;\r\n color: var(--text2);\r\n background-color: var(--bg2_float);\r\n}\r\n\r\n.reply-item-client:hover\r\n .content-warp\r\n .root-reply\r\n .reply-operation-warp\r\n .more-operation {\r\n display: block;\r\n}\r\n\r\n.reply-list {\r\n position: relative;\r\n margin-top: 14px;\r\n padding-bottom: 100px;\r\n}\r\n\r\n.reply-list .reply-empty {\r\n margin-top: 100px;\r\n text-align: center;\r\n font-size: 14px;\r\n color: var(--text3);\r\n}\r\n\r\n.reply-list .reply-end-mark {\r\n height: 100px;\r\n}\r\n\r\n.reply-list .reply-end,\r\n.reply-list .reply-loading {\r\n margin-top: 20px;\r\n font-size: 13px;\r\n color: var(--text3);\r\n text-align: center;\r\n}\r\n\r\n.fixed-reply-box {\r\n bottom: 0;\r\n z-index: 20;\r\n width: 100%;\r\n}\r\n\r\n.fixed-reply-box .reply-box-wrap {\r\n background-color: var(--bg1);\r\n padding: 14px 0;\r\n border-top: 1px solid var(--line_light);\r\n}\r\n\r\n.fixed-reply-box .reply-box-shadow {\r\n position: absolute;\r\n top: -10px;\r\n z-index: -1;\r\n height: 36px;\r\n border-radius: 50%;\r\n background-color: #00000014;\r\n filter: blur(10px);\r\n width: calc(100% - 72px);\r\n left: 50%;\r\n transform: translate(-50%);\r\n}\r\n\r\n.reply-detail {\r\n flex: 1;\r\n}\r\n\r\n.reply-detail .reply-header {\r\n display: flex;\r\n align-items: center;\r\n position: sticky;\r\n z-index: 9;\r\n top: 0;\r\n left: 0;\r\n height: 46px;\r\n border-bottom: 1px solid var(--line_light);\r\n margin-bottom: 14px;\r\n background-color: var(--bg1);\r\n}\r\n\r\n.reply-detail .reply-header .return-icon {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 4px;\r\n margin-right: 4px;\r\n color: var(--text1);\r\n cursor: pointer;\r\n}\r\n\r\n.reply-detail .reply-header .return-icon:hover {\r\n background-color: var(--graph_bg_thick);\r\n}\r\n\r\n.reply-detail .reply-header .reply-title {\r\n font-size: 16px;\r\n font-weight: 600;\r\n color: var(--text1);\r\n}\r\n\r\n.dialog-reply {\r\n flex: 1;\r\n}\r\n\r\n.dialog-reply .reply-header {\r\n display: flex;\r\n align-items: center;\r\n position: sticky;\r\n z-index: 9;\r\n top: 0;\r\n left: 0;\r\n height: 46px;\r\n border-bottom: 1px solid var(--line_light);\r\n margin-bottom: 14px;\r\n background-color: var(--bg1);\r\n}\r\n\r\n.dialog-reply .reply-header .return-icon {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 4px;\r\n margin-right: 4px;\r\n color: var(--text1);\r\n cursor: pointer;\r\n}\r\n\r\n.dialog-reply .reply-header .return-icon:hover {\r\n background-color: var(--graph_bg_thick);\r\n}\r\n\r\n.dialog-reply .reply-header .reply-title {\r\n font-size: 16px;\r\n font-weight: 600;\r\n color: var(--text1);\r\n}\r\n\r\n.bili-comment.client {\r\n background-color: var(--bg1);\r\n}\r\n\r\n.bili-comment.client * {\r\n box-sizing: border-box;\r\n font-family: PingFang SC, HarmonyOS_Regular, Helvetica Neue, Microsoft YaHei,\r\n sans-serif;\r\n -webkit-font-smoothing: antialiased;\r\n}\r\n\r\n.bili-comment.client * ul {\r\n list-style: none;\r\n}\r\n\r\n.bili-comment.client * a {\r\n text-decoration: none;\r\n background-color: transparent;\r\n color: var(--text_link);\r\n cursor: pointer;\r\n}\r\n\r\n.bili-comment.client * a:hover {\r\n color: var(--Lb4);\r\n}\r\n\r\n.bili-comment.client * i {\r\n font-style: normal;\r\n}\r\n';
class GestureBack {
/**
* 是否正在后退
*/
isBacking = false;
config;
constructor(config) {
this.config = config;
this.enterGestureBackMode = this.enterGestureBackMode.bind(this);
this.quitGestureBackMode = this.quitGestureBackMode.bind(this);
this.popStateEvent = this.popStateEvent.bind(this);
if (typeof this.config.backDelayTime !== "number" || isNaN(this.config.backDelayTime)) {
this.config.backDelayTime = 150;
}
if (this.config.win == null) {
this.config.win = self;
}
}
/**
* popstate事件函数
* @param event
*/
popStateEvent(event) {
Utils.preventEvent(event);
if (this.isBacking) {
return;
}
this.quitGestureBackMode(true);
}
/**
* 进入手势模式
*/
enterGestureBackMode() {
log$1.success("进入手势模式");
let pushUrl = this.config.hash;
if (!pushUrl.startsWith("#")) {
if (!pushUrl.startsWith("/")) {
pushUrl = "/" + pushUrl;
}
pushUrl = "#" + pushUrl;
}
if (this.config.useUrl) {
pushUrl = this.config.win.location.origin + this.config.win.location.pathname + this.config.win.location.search + pushUrl;
}
this.config.win.history.pushState({}, "", pushUrl);
log$1.success("监听popstate事件");
domUtils.on(this.config.win, "popstate", this.popStateEvent, {
capture: true
});
}
/**
* 退出手势模式
* @param isUrlChange 是否是url改变触发的
*/
async quitGestureBackMode(isUrlChange = false) {
this.isBacking = true;
log$1.success("退出手势模式");
if (typeof this.config.beforeHistoryBackCallBack === "function") {
this.config.beforeHistoryBackCallBack(isUrlChange);
}
let maxDate = Date.now() + 1e3 * 5;
while (true) {
if (Date.now() > maxDate) {
log$1.error("未知情况,history.back()失败,无法退出手势模式");
break;
}
if (this.config.win.location.hash.endsWith(this.config.hash)) {
log$1.info("history.back()");
this.config.win.history.back();
await Utils.sleep(this.config.backDelayTime || 150);
} else {
break;
}
}
log$1.success("移除popstate事件");
domUtils.off(this.config.win, "popstate", this.popStateEvent, {
capture: true
});
this.isBacking = false;
if (typeof this.config.afterHistoryBackCallBack === "function") {
this.config.afterHistoryBackCallBack(isUrlChange);
}
}
}
const BilibiliVideo = {
$data: {
/** 是否已添加美化CSS */
isAddBeautifyCSS: false,
/** 是否已经初始化评论模块 */
isInitCommentModule: false,
/** 是否已经初始化简介模块 */
isInitDescModule: false
},
init() {
BilibiliVideoPlayer.init();
Panel.execMenuOnce("bili-video-cover-bottomRecommendVideo", () => {
this.coverBottomRecommendVideo();
});
Panel.execMenuOnce("bili-video-cover-UpWrapper", () => {
this.coverUpWrapper();
});
Panel.execMenuOnce("bili-video-cover-seasonNew", () => {
this.coverSeasonNew();
});
domUtils.ready(() => {
Panel.execMenu("bili-video-addCommentModule", () => {
this.addCommentModule();
});
Panel.execMenu("bili-video-addDescModule", () => {
this.addDescModule();
});
});
},
/**
* 美化显示
*/
beautify() {
log$1.info("美化显示");
if (!this.$data.isAddBeautifyCSS) {
this.$data.isAddBeautifyCSS = true;
addStyle(
/*css*/
`
@charset "UTF-8";
${BilibiliData.className.video} .video-list .card-box {
--left-card-width: 33%;
--right-child-padding: 1.333vmin;
/* 开启了bili-video-beautify */
}
${BilibiliData.className.video} .video-list .card-box .v-card-toapp {
width: 100%;
border-bottom: 1px solid #b5b5b5;
padding-left: 0;
padding-right: 0;
}
${BilibiliData.className.video} .video-list .card-box .v-card-toapp > a {
display: flex;
flex-wrap: nowrap;
gap: var(--right-child-padding);
}
${BilibiliData.className.video} .video-list .card-box .v-card-toapp > a .card {
width: var(--left-card-width);
height: 80px;
flex: 0 auto;
}
${BilibiliData.className.video} .video-list .card-box .v-card-toapp > a .card .count {
background: transparent;
}
${BilibiliData.className.video} .video-list .card-box .v-card-toapp > a .card .count .left {
display: list-item;
}
${BilibiliData.className.video} .video-list .card-box .v-card-toapp > a .card .count .left span.item {
display: none;
}
${BilibiliData.className.video} .video-list .card-box .v-card-toapp > a .card .count .duration {
background: rgba(0, 0, 0, 0.4);
border-radius: 0.6vmin;
padding: 0px 0.5vmin;
right: 1vmin;
bottom: 1vmin;
}
${BilibiliData.className.video} .video-list .card-box .v-card-toapp > a .title {
/*flex: 1;*/
/*padding: var(--right-child-padding);*/
padding-top: 0;
margin-top: 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
${BilibiliData.className.video} .video-list .card-box .gm-right-container {
display: flex;
flex-direction: column;
width: calc(100% - var(--left-card-width));
justify-content: space-between;
}
${BilibiliData.className.video} .video-list .card-box .gm-right-container > * {
padding: var(--right-child-padding);
padding-bottom: 0;
}
${BilibiliData.className.video} .video-list .card-box .gm-right-container .left {
gap: 1rem;
}
${BilibiliData.className.video} .video-list .card-box .gm-right-container .left span {
display: flex;
align-items: safe center;
gap: 1vmin;
}
${BilibiliData.className.video} .video-list .card-box .gm-right-container .gm-up-name,
${BilibiliData.className.video} .video-list .card-box .gm-right-container .left {
color: #999;
font-size: 3vmin;
transform-origin: left;
display: flex;
/*align-items: safe center;*/
align-items: safe flex-end;
}
${BilibiliData.className.video} .video-list .card-box .gm-right-container .gm-up-name svg {
width: 3vmin;
height: 3vmin;
}
${BilibiliData.className.video} .video-list .card-box .gm-right-container .gm-up-name-text {
margin-left: 1vmin;
}
${BilibiliData.className.video} .video-list .card-box .gm-right-container .num {
margin-right: 4vmin;
}
${BilibiliData.className.video} .video-list .card-box > a.v-card {
width: 100%;
border-bottom: 1px solid #b5b5b5;
padding-left: 0;
padding-right: 0;
display: flex;
flex-wrap: nowrap;
}
${BilibiliData.className.video} .video-list .card-box > a.v-card .card {
width: var(--left-card-width);
height: 100%;
flex: 0 auto;
}
${BilibiliData.className.video} .video-list .card-box > a.v-card .card .count {
background: transparent;
}
${BilibiliData.className.video} .video-list .card-box > a.v-card .card .count span {
display: none;
}
${BilibiliData.className.video} .video-list .card-box > a.v-card .card .count .duration {
background-color: rgba(0, 0, 0, 0.3);
border-radius: 4px;
color: #fff;
font-size: 12px;
height: 16px;
line-height: 16px;
margin-left: auto;
padding-left: 4px;
padding-right: 4px;
}
${BilibiliData.className.video} .video-list .card-box > a.v-card .title {
flex: 1;
/*padding: var(--right-child-padding);*/
padding-top: 0;
margin-top: 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
`
);
}
utils.waitNode(
BilibiliData.className.video + " .bottom-tab .list-view .card-box",
1e4
).then(($cardBox) => {
if (!$cardBox) {
log$1.error("$cardBox is null");
return;
}
function handleVCardToApp($vCard) {
let $originTitle = $vCard.querySelector(".title");
let $originLeft = $vCard.querySelector(".count .left");
let isHandled = Boolean($vCard.querySelector(".gm-right-container"));
let vueObj = VueUtils.getVue($vCard);
if ($originTitle && $originLeft && vueObj && !isHandled) {
let upName = vueObj?.info?.owner?.name;
if (upName == null) {
log$1.error("美化显示-handleVCardToApp:获取up主名字失败");
return;
}
$vCard.querySelector(".count");
let $title = $originTitle.cloneNode(true);
let $left = $originLeft.cloneNode(true);
domUtils.hide($originTitle);
let $isOpenAppWeakened = $vCard.querySelector(".open-app.weakened");
if ($isOpenAppWeakened) {
domUtils.hide($isOpenAppWeakened);
}
let $upInfo = document.createElement("div");
$upInfo.className = "gm-up-name";
$upInfo.innerHTML = /*html*/
`
${upName}
`;
let $rightContainer = document.createElement("div");
let $rightBottom = document.createElement("div");
$rightContainer.className = "gm-right-container";
$rightBottom.className = "gm-right-bottom";
domUtils.after($originTitle, $rightContainer);
$rightContainer.appendChild($title);
$rightContainer.appendChild($rightBottom);
$rightBottom.appendChild($upInfo);
$rightBottom.appendChild($left);
}
}
function handleVCard($vCard) {
let $originTitle = $vCard.querySelector(".title");
let $originCount = $vCard.querySelector(".count");
let isHandled = Boolean($vCard.querySelector(".gm-right-container"));
let vueObj = VueUtils.getVue($vCard);
if ($originTitle && $originCount && vueObj && !isHandled) {
let duration = vueObj?.info?.duration;
if (duration == null) {
log$1.error("美化显示-handleVCard:获取视频时长失败");
return;
}
let upName = vueObj?.info?.owner?.name;
if (upName == null) {
log$1.error("美化显示-handleVCard:获取up主名字失败");
return;
}
let $cloneTitle = $originTitle.cloneNode(true);
let $cloneCount = $originCount.cloneNode(true);
domUtils.hide($originTitle);
let $duration = document.createElement("div");
$duration.className = "duration";
$duration.innerText = BilibiliUtils.parseDuration(duration);
$cloneCount.className = "left";
let $upInfo = document.createElement("div");
$originCount.appendChild($duration);
$upInfo.className = "gm-up-name";
$upInfo.innerHTML = /*html*/
`
${upName}
`;
let $rightContainer = document.createElement("div");
let $rightBottom = document.createElement("div");
$rightContainer.className = "gm-right-container";
$rightBottom.className = "gm-right-bottom";
domUtils.after($originTitle, $rightContainer);
$rightContainer.appendChild($cloneTitle);
$rightContainer.appendChild($rightBottom);
$rightBottom.appendChild($upInfo);
$rightBottom.appendChild($cloneCount);
}
}
let lockFunc = new utils.LockFunction(() => {
let $vCardList = document.querySelectorAll(
BilibiliData.className.video + " .bottom-tab .list-view .card-box .v-card-toapp"
);
let $vCardList_isLogon = document.querySelectorAll(
BilibiliData.className.video + " .bottom-tab .list-view .card-box>a.v-card"
);
$vCardList.forEach((_$vCard_) => {
handleVCardToApp(_$vCard_);
});
$vCardList_isLogon.forEach((_$vCard_) => {
handleVCard(_$vCard_);
});
}, 25);
let $videoRoot = document.querySelector(
BilibiliData.className.video
);
if ($videoRoot) {
utils.mutationObserver($videoRoot, {
config: {
subtree: true,
attributes: true,
childList: true
},
callback() {
lockFunc.run();
}
});
} else {
log$1.error("未找到视频根节点");
}
});
},
/**
* 修复视频底部区域高度
*/
repairVideoBottomAreaHeight() {
log$1.info("修复视频底部区域高度");
return addStyle(
/*css*/
`
${BilibiliData.className.video},
${BilibiliData.className.mVideo} {
/* 修复视频区域底部的高度 */
.natural-module .fixed-module-margin {
margin-top: 55.13333vmin;
}
/* 点击播放视频后的 */
.m-video-new:has(> div > .m-video-player) {
margin-top: 75vmin;
}
/* 未播放视频状态下的 */
.m-video-new:has(> div[style*="display:none"] > .m-video-player) {
margin-top: unset;
}
}
html.tiny-app{
${BilibiliData.className.video},
${BilibiliData.className.mVideo}{
.m-video-info-new{
margin-top: 72vmin;
}
}
}
`
);
},
/**
* 修复up主信息区域的点击事件
*/
coverUpWrapper() {
log$1.info(`修复up主信息区域的点击事件`);
domUtils.on(
document,
"click",
[
BilibiliData.className.video + " .bottom-wrapper .up-wrapper",
BilibiliData.className.mVideo + " .bottom-wrapper .up-wrapper"
],
function(event) {
let $click = event.target;
let $bottomWrapper = $click.closest(".bottom-wrapper");
if (!$bottomWrapper) {
log$1.error("获取元素.bottom-wrapper失败");
return;
}
let vueInstance = VueUtils.getVue($bottomWrapper);
if (!vueInstance) {
log$1.error("获取元素.bottom-wrapper的vue实例失败");
return;
}
let mid = vueInstance?.upInfo?.card?.mid;
if (typeof mid === "string") {
BilibiliUtils.goToUrl(BilibiliUrl.getUserSpaceUrl(mid));
} else {
Qmsg.error("获取mid失败");
}
},
{
capture: true
}
);
},
/**
* 覆盖视频标题区域的点击事件
*/
coverBottomRecommendVideo() {
log$1.info("覆盖 相关视频 点击事件");
domUtils.on(
document,
"click",
[
BilibiliData.className.video + " .list-view .card-box .launch-app-btn",
BilibiliData.className.mVideo + " .list-view .card-box .launch-app-btn"
],
function(event) {
let $click = event.target;
let vueObj = VueUtils.getVue($click);
if (!vueObj) {
Qmsg.error("获取相关视频的__vue__失败");
return;
}
let bvid = vueObj.bvid;
if (utils.isNull(bvid)) {
if (vueObj.$children && vueObj.$children[0] && utils.isNotNull(vueObj.$children[0].bvid)) {
bvid = vueObj.$children[0].bvid;
} else {
Qmsg.error("获取相关视频的bvid失败");
return;
}
}
log$1.info("相关视频的bvid: " + bvid);
BilibiliUtils.goToUrl(BilibiliUrl.getVideoUrl(bvid));
utils.preventEvent(event);
},
{
capture: true
}
);
},
/**
* 覆盖选集视频列表的点击事件
*/
coverSeasonNew() {
log$1.info("覆盖 选集视频列表 点击事件");
function ClickCallBack(event) {
let $click = event.target;
let vueObj = VueUtils.getVue($click);
if (!vueObj) {
Qmsg.error("获取选集视频的目标视频的__vue__失败");
return;
}
let bvid = vueObj.bvid;
if (utils.isNull(bvid)) {
Qmsg.error("获取相关视频的bvid失败");
return;
}
log$1.info("相关视频的bvid: " + bvid);
BilibiliUtils.goToUrl(BilibiliUrl.getVideoUrl(bvid));
utils.preventEvent(event);
}
domUtils.on(
document,
"click",
[
BilibiliData.className.video + " .m-video-season-new .video-card .launch-app-btn",
BilibiliData.className.mVideo + " .m-video-season-new .video-card .launch-app-btn"
],
ClickCallBack,
{
capture: true
}
);
domUtils.on(
document,
"click",
[
BilibiliData.className.video + " .m-video-season-panel .season-video-item .launch-app-btn",
BilibiliData.className.mVideo + " .m-video-season-panel .season-video-item .launch-app-btn"
],
ClickCallBack,
{
capture: true
}
);
},
/**
* 修复链接跳转
*/
repairLinkJump() {
log$1.info(`修复链接跳转`);
let lockFn = new utils.LockFunction(() => {
[
"a.member-link:not([href])[data-url]",
"a.jump-link:not([href])[data-url]"
].forEach((selector) => {
$$(selector).forEach(($el) => {
$el.href = $el.getAttribute("data-url");
});
});
});
utils.mutationObserver(document, {
config: {
subtree: true,
childList: true
},
callback: () => {
lockFn.run();
}
});
},
/**
* 手势返回关闭评论区
*/
gestureReturnToCloseCommentArea() {
log$1.info("手势返回关闭评论区,全局监听document点击.sub-reply-preview");
utils.waitNode("#app").then(($app) => {
utils.waitVueByInterval(
$app,
() => {
let vueObj = VueUtils.getVue($app);
if (vueObj == null) {
return false;
}
return typeof vueObj?.$router?.options?.scrollBehavior != null;
},
250,
1e4
).then((result) => {
let appVue = VueUtils.getVue($app);
if (!appVue) {
log$1.error("获取#app的vue属性失败");
return;
}
let oldScrollBehavior = appVue.$router.options.scrollBehavior;
appVue.$router.options.scrollBehavior = function(to, from, scrollInfo) {
if (to["hash"] === "#/seeCommentReply") {
log$1.info("当前操作为打开评论区,scrollBehavior返回null");
return null;
} else if (to["hash"] === "" && from["hash"] === "#/seeCommentReply") {
log$1.info("当前操作为关闭评论区,scrollBehavior返回null");
return null;
}
return oldScrollBehavior.call(this, ...arguments);
};
});
});
domUtils.on(document, "click", ".sub-reply-preview", function(event) {
let $app = document.querySelector("#app");
let appVue = VueUtils.getVue($app);
if (!appVue) {
log$1.error("获取#app元素失败");
return;
}
let hookGestureReturnByVueRouter = BilibiliUtils.hookGestureReturnByVueRouter({
vueObj: appVue,
hash: "#/seeCommentReply",
callback(isFromPopState) {
if (!isFromPopState) {
return false;
}
let $dialogCloseIcon = document.querySelector(".dialog-close-icon");
if ($dialogCloseIcon) {
$dialogCloseIcon.click();
} else {
log$1.error("评论区关闭失败,原因:元素dialog-close-icon获取失败");
}
return true;
}
});
utils.waitNode(".dialog-close-icon").then(($dialogCloseIcon) => {
domUtils.on(
$dialogCloseIcon,
"click",
function() {
hookGestureReturnByVueRouter.resumeBack(false);
},
{
capture: true,
once: true
}
);
});
});
},
/**
* 进入全屏
*/
enterVideoFullScreen() {
utils.waitNode(".mplayer-btn-widescreen", 5e3).then(($btnWideScreen) => {
if (!$btnWideScreen) {
log$1.error("获取全屏按钮失败");
Qmsg.error("获取全屏按钮失败");
return;
}
if ($btnWideScreen.closest(".mplayer-wide")) {
log$1.warn("当前的全屏按钮是【退出全屏】,不点击");
return;
}
log$1.info(`进入全屏`);
$btnWideScreen.click();
});
},
/**
* 优化滚动显示view
*/
optimizationScroll() {
let $mNavBar = null;
let $mVideoPlayer = null;
let $mVideoInfoNew = null;
let $bottomTab = null;
let $bottomTabVAffix = null;
let videoPlayerMaxHeight = 0;
let videoPlayerMaxPaddingTop = 0;
function checkNodeIsNull(checkNode) {
return !document.contains(checkNode);
}
domUtils.on(
document,
"scroll",
(event) => {
if (checkNodeIsNull($mVideoPlayer)) {
$mVideoPlayer = document.querySelector(".m-video-player");
if (checkNodeIsNull($mVideoPlayer)) {
return;
}
if (videoPlayerMaxHeight == 0) {
const videoPlayerRect = $mVideoPlayer.getBoundingClientRect();
videoPlayerMaxHeight = videoPlayerRect.height;
videoPlayerMaxPaddingTop = videoPlayerRect.top;
log$1.info(`视频区域的最大高度为 ${videoPlayerMaxHeight}px`);
log$1.info(`视频区域的最大top为 ${videoPlayerMaxPaddingTop}px`);
}
}
if (checkNodeIsNull($mVideoInfoNew)) {
$mVideoInfoNew = document.querySelector(".m-video-info-new");
if (checkNodeIsNull($mVideoInfoNew)) {
return;
}
}
if (checkNodeIsNull($mNavBar)) {
$mNavBar = document.querySelector(".m-navbar");
if (checkNodeIsNull($mNavBar)) {
return;
}
}
if (checkNodeIsNull($bottomTab)) {
$bottomTab = document.querySelector(".bottom-tab");
if (checkNodeIsNull($bottomTab)) {
return;
}
}
if (checkNodeIsNull($bottomTabVAffix)) {
$bottomTabVAffix = document.querySelector(".bottom-tab .v-affix");
if (checkNodeIsNull($bottomTabVAffix)) {
return;
}
}
let videoInfoNewTop = $mVideoInfoNew.getBoundingClientRect().top;
if (videoInfoNewTop >= 0) {
if (videoInfoNewTop <= videoPlayerMaxHeight) {
$mVideoPlayer.style.paddingTop = videoInfoNewTop + "px";
} else {
$mVideoPlayer.style.paddingTop = "";
}
} else {
$mVideoPlayer.style.paddingTop = "0px";
}
let navbarHeight = domUtils.height($mNavBar);
let bottomTabTop = $bottomTab.getBoundingClientRect().top;
if (bottomTabTop < navbarHeight) {
if ($bottomTabVAffix.hasAttribute("data-is-fixed")) ;
else {
$bottomTabVAffix.style.cssText = `position: fixed;left: 0px;top: ${navbarHeight}px;z-index: 10000;width: 100%;`;
$bottomTabVAffix.setAttribute("data-is-fixed", "true");
}
} else {
$bottomTabVAffix.style.cssText = "";
$bottomTabVAffix.removeAttribute("data-is-fixed");
}
},
{
passive: true
}
);
},
/**
* 禁止滑动切换tab
*/
disableSwipeTab() {
log$1.info(`禁止滑动切换tab`);
VueUtils.waitVuePropToSet(".m-video-bottom-tab", {
msg: "等待tab的vue属性touchstart、touchmove、touchend事件,_bindEvents函数",
check(vueInstance) {
return vueInstance?.slider?.el instanceof HTMLElement && typeof vueInstance?.slider?.events?.touchstart === "function" && typeof vueInstance?.slider?.events?.touchmove === "function" && typeof vueInstance?.slider?.events?.touchend === "function" && typeof vueInstance?.slider?._bindEvents === "function";
},
set(vueInstance) {
let $bindTarget = vueInstance.slider.el;
$bindTarget.removeEventListener(
"touchstart",
vueInstance.slider.events.touchstart
);
$bindTarget.removeEventListener(
"touchmove",
vueInstance.slider.events.touchmove
);
$bindTarget.removeEventListener(
"touchend",
vueInstance.slider.events.touchend
);
vueInstance.slider._bindEvents = () => {
};
log$1.success(
`成功禁用滑动,清除touchstart、touchmove、touchend事件,覆盖_bindEvents函数`
);
}
});
},
/**
* 新增评论模块
*
* + https://greasyfork.org/zh-CN/scripts/524844-bilibili-mobile-comment-module
*/
addCommentModule() {
log$1.info(`新增评论模块`);
if (!this.$data.isInitCommentModule) {
this.$data.isInitCommentModule = true;
CommonUtil.setGMResourceCSS(GM_RESOURCE_MAPPING.Viewer);
addStyle(MobileCommentModuleStyle);
addStyle(
/*css*/
`
.comment-container{
position: relative;
}
.comment-container .reply-header{
position: sticky;
top: 0;
z-index: 999;
left: 0;
right: 0;
background: #fff;
}
#comment-module-wrapper{
position: fixed;
top: 0;
left: 0;
z-index: 2000;
display: none;
width: 100vw;
height: 100vh;
background-color: #fff;
overflow-x: hidden;
}
.close-comment-module-btn{
position: fixed;
right: 20px;
bottom: 20px;
z-index: 2001;
display: none;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
color: #fff;
border-radius: 100%;
background-color: var(--bili-color);
}
`
);
addStyle(
/*css*/
`
.comment-module-show-btn{
display: flex;
justify-content: center;
align-items: center;
margin: 0 12px 20px 12px;
height: 40px;
color: #fff;
border-radius: 4px;
background-color: var(--bili-color);
}
`
);
}
utils.waitNode(".m-video-info", 1e4).then(($videoInfo) => {
if (!$videoInfo) {
log$1.error(`获取视频信息元素失败`);
return;
}
domUtils.remove(".comment-module-show-btn");
domUtils.remove(".close-comment-module-btn");
domUtils.remove("#comment-module-wrapper");
const history_hash = "comment-module";
let gestureBack = new GestureBack({
hash: history_hash,
useUrl: true,
beforeHistoryBackCallBack(isUrlChange) {
let $viewerClose = $(".viewer-button.viewer-close");
if ($viewerClose) {
$viewerClose.click();
}
if (isUrlChange) {
$closeCommentModuleBtn.click();
}
}
});
let $commentModuleShowBtn = domUtils.createElement("div", {
className: "comment-module-show-btn",
innerHTML: `查看评论`
});
let $closeCommentModuleBtn = domUtils.createElement("span", {
className: "close-comment-module-btn",
innerHTML: "×"
});
domUtils.on($commentModuleShowBtn, "click", (event) => {
utils.preventEvent(event);
domUtils.css($commentModuleWrapper, { display: "block" });
domUtils.css($closeCommentModuleBtn, { display: "flex" });
gestureBack.enterGestureBackMode();
});
domUtils.on($closeCommentModuleBtn, "click", (event) => {
utils.preventEvent(event);
domUtils.css($commentModuleWrapper, { display: "" });
domUtils.css($closeCommentModuleBtn, { display: "" });
gestureBack.quitGestureBackMode(false);
});
domUtils.append($videoInfo, $commentModuleShowBtn);
let $commentModuleWrapper = domUtils.createElement("div", {
id: "comment-module-wrapper"
});
domUtils.append(document.body, $commentModuleWrapper);
domUtils.after($commentModuleWrapper, $closeCommentModuleBtn);
MobileCommentModule.init($commentModuleWrapper);
});
},
/**
* 新增简介模块
*/
addDescModule() {
log$1.info(`新增简介模块`);
if (!this.$data.isInitDescModule) {
this.$data.isInitDescModule = true;
addStyle(
/*css*/
`
${BilibiliData.className.mVideo} .m-video-info .bottom-wrapper{
flex-direction: column;
align-items: flex-start;
height: auto;
}
`
);
addStyle(
/*css*/
`
.video-desc-wrapper {
color: #9499A0;
font-size: 14px;
width: 100%;
.video-desc-text {
margin: 10px 0px;
white-space: pre-wrap;
}
.video-view-info-wrapper {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 10px;
margin: 5px 0px;
.video-info-icon{
display: flex;
align-items: center;
gap: 2px;
}
.video-info-text{
display: flex;
align-items: center;
line-height: 1rem;
}
}
.video-desc-controls-wrapper{
margin: 10px 0px;
display: flex;
justify-content: space-around;
align-items: center;
.video-info-icon {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
}
}
}
`
);
}
domUtils.remove(
BilibiliData.className.mVideo + " .m-video-info .video-desc-wrapper"
);
VueUtils.waitVuePropToSet(
BilibiliData.className.mVideo + " .m-video-info .bottom-wrapper",
{
check(vueInstance) {
return typeof vueInstance?.info?.bvid === "string";
},
set(vueInstance, target) {
let info = vueInstance.info;
let upInfo = vueInstance.upInfo;
upInfo.follower;
upInfo.archive_count;
let view = info.stat.view;
let danmakuCount = info.stat.danmaku;
info.ctime;
let bvid = info.bvid;
let desc = info.desc;
let like = info.stat.like;
let coin = info.stat.coin;
let favorite = info.stat.favorite;
let share = info.stat.share;
let $descWrapper = domUtils.createElement("div", {
className: "video-desc-wrapper",
innerHTML: (
/*html*/
`
${BilibiliUtils.parseCount(
view
)}
${BilibiliUtils.parseCount(
danmakuCount
)}
${utils.formatTime(
info.ctime * 1e3,
"yyyy年MM月dd日 HH:mm:ss"
)}
${bvid}
${desc}
${BilibiliUtils.parseCount(like)}
${BilibiliUtils.parseCount(coin)}
${BilibiliUtils.parseCount(favorite)}
${BilibiliUtils.parseCount(share)}
`
)
});
target.appendChild($descWrapper);
}
}
);
}
};
const artPlayerCSS = ".artplayer-container {\r\n width: 100vw;\r\n height: 35vh;\r\n}";
const BilibiliOpenApp = {
getUrl($ele) {
if ($ele == null) {
return;
}
return $ele.getAttribute("universallink");
},
/**
* 直接跳转Url
* @param event
*/
jumpToUrl(event) {
let $click = event.target;
let $biliOpenApp = $click.querySelector("bili-open-app") || $click.querySelector("m-open-app");
if ($biliOpenApp) {
let url = BilibiliOpenApp.getUrl($biliOpenApp);
if (url) {
BilibiliUtils.goToUrl(url);
} else {
Qmsg.error("获取bili-open-app的Url失败");
log$1.error("获取bili-open-app的Url失败");
}
} else {
Qmsg.error("未获取到元素");
log$1.error("未获取到元素");
}
}
};
const BilibiliLogUtils = {
/**
* 过滤searchParam的敏感数据
*/
filteringSensitiveSearchParamData(data2) {
const sensitiveData = utils.assign({}, data2, true);
Reflect.deleteProperty(sensitiveData, "access_key");
Reflect.deleteProperty(sensitiveData, "access_token");
return sensitiveData;
},
/**
* 请求失败的信息弹窗
*/
failToast(data2) {
log$1.error(data2);
alert(JSON.stringify(data2, null, 4));
}
};
const BilibiliBangumiApi = {
/**
* 轮询获取番剧播放地址
*/
async getPlayUrl(option) {
let searchParamsData = {
avid: "",
cid: "",
ep_id: "",
// 8K 超高清
qn: 127,
/** 固定值 */
fnver: 0,
// dash且需求 av1 编码且需求 8K 分辨率
fnval: 16 | 1024 | 2048,
// mp4格式
// fnval: 1,
/** 是否允许4K视频 */
fourk: 1
};
searchParamsData = utils.assign(searchParamsData, option);
let serverHostList = BilibiliApiProxy.getBangumiProxyHost();
log$1.info(`番剧播放地址请求数据`);
let failReponseJSON = [];
let result = void 0;
const urlPath = "/pgc/player/web/playurl";
log$1.info(`请求路径:${urlPath}`);
for (let index = 0; index < serverHostList.length; index++) {
const serverHostInfo = serverHostList[index];
const serverHost = serverHostInfo.host;
const proxyServerSearchParamsData = {};
if (serverHost !== BilibiliApiConfig.web_host) {
utils.assign(
proxyServerSearchParamsData,
BilibiliApiProxy.getBangumiProxySearchParam({
area: serverHostInfo.area
}),
true
);
log$1.info(`代理服务器数据: ${JSON.stringify(serverHostInfo)}`);
log$1.info(
`代理服务器请求参数:${JSON.stringify(
BilibiliLogUtils.filteringSensitiveSearchParamData(
proxyServerSearchParamsData
)
)}`
);
}
let url = `https://${serverHost}${urlPath}?${utils.toSearchParamsStr(
searchParamsData
)}&${utils.toSearchParamsStr(proxyServerSearchParamsData)}`;
let getResponse = await httpx.get(url, {
responseType: "json",
fetch: false,
allowInterceptConfig: false,
headers: {
Referer: "https://www.bilibili.com/"
}
});
if (!getResponse.status) {
log$1.error(`代理服务器:${serverHost} 请求失败`);
continue;
}
let responseData = utils.toJSON(getResponse.data.responseText);
responseData.result;
if (!BilibiliApiResponseCheck.isWebApiSuccess(responseData) || BilibiliApiResponseCheck.isAreaLimit(responseData)) {
log$1.error(
`请求失败,当前代理服务器:${serverHost} ${JSON.stringify(
responseData
)}`
);
failReponseJSON.push(responseData);
continue;
}
result = responseData.result;
break;
}
if (result == null) {
BilibiliLogUtils.failToast(failReponseJSON);
}
return result;
},
/**
* 获取番剧播放地址-html5,获取的是mp4的
*/
async getPlayUrlHTML5(option) {
let searchParamsData = {
avid: "",
cid: "",
ep_id: "",
bsource: ""
// qn: 116,
// fnver: 0,
// fnval: 1,
// fourk: 1,
// from_client: "BROWSER",
// drm_tech_type: 2,
};
searchParamsData = utils.assign(searchParamsData, option);
log$1.info(`(原版api)番剧播放地址请求数据`);
const urlPath = "/pgc/player/web/playurl/html5";
let url = `https://${BilibiliApiConfig.web_host}${urlPath}?${utils.toSearchParamsStr(searchParamsData)}`;
let getResponse = await httpx.get(url, {
responseType: "json",
fetch: true,
headers: {
Host: "www.bilibili.com",
Referer: "https://www.bilibili.com"
}
});
if (!getResponse.status) {
return;
}
let responseData = utils.toJSON(getResponse.data.responseText);
if (!BilibiliApiResponseCheck.isWebApiSuccess(responseData)) {
BilibiliLogUtils.failToast(responseData);
return;
}
let responseResult = responseData.result;
return responseResult;
}
};
const TAG = "[artplayer-plugin-airborneHelper]:";
const AirborneHelperEvent = {
$data: {
tipJumpToastTimeoutId: void 0,
tipJumpToastInfo: void 0,
successJumpToastInfo: void 0
},
$event: {
"video:timeupdate": () => {
if (AirborneHelperEvent.$data.tipJumpToastTimeoutId != null) {
return;
}
if (!AirborneHelper.$data.art.playing) {
return;
}
const beforeToastTime = 5;
let currentTime = AirborneHelper.$data.art.currentTime;
let findIndex = AirborneHelper.$data.option.clip_info_list.findIndex(
(item) => {
let jumpTime = item.start;
if (jumpTime === 0) {
return currentTime <= 1;
} else {
return currentTime >= jumpTime - beforeToastTime && currentTime < jumpTime;
}
}
);
if (findIndex !== -1) {
let toastCloseCallBack = function() {
clearTimeout(AirborneHelperEvent.$data.tipJumpToastTimeoutId);
AirborneHelperEvent.$data.tipJumpToastTimeoutId = void 0;
AirborneHelperEvent.$data.tipJumpToastInfo?.close();
AirborneHelperEvent.$data.tipJumpToastInfo = void 0;
AirborneHelper.$data.option.clip_info_list.splice(findIndex, 1);
};
let findValue = AirborneHelper.$data.option.clip_info_list[findIndex];
let plugin_toast = AirborneHelper.$data.art.plugins[ArtPlayer_PLUGIN_TOAST_KEY];
let timeout = (findValue.start - currentTime) * 1e3;
AirborneHelperEvent.$data.tipJumpToastTimeoutId = setTimeout(() => {
AirborneHelper.$data.art.currentTime = findValue.end;
AirborneHelperEvent.$data.tipJumpToastTimeoutId = void 0;
if (AirborneHelperEvent.$data.successJumpToastInfo) {
AirborneHelperEvent.$data.successJumpToastInfo.close();
AirborneHelperEvent.$data.successJumpToastInfo = void 0;
}
AirborneHelperEvent.$data.successJumpToastInfo = plugin_toast.toast({
text: "空降成功~o(*≧▽≦)ツ┏━┓",
closeCallback() {
AirborneHelperEvent.$data.successJumpToastInfo = void 0;
}
});
}, timeout);
if (AirborneHelperEvent.$data.tipJumpToastInfo) {
AirborneHelperEvent.$data.tipJumpToastInfo.close();
AirborneHelperEvent.$data.tipJumpToastInfo = void 0;
}
AirborneHelperEvent.$data.tipJumpToastInfo = plugin_toast.toast({
text: typeof findValue.toastText === "string" ? findValue.toastText : "站稳扶好,准备起飞~",
timeout: timeout < 2e3 ? 2e3 : timeout,
showCloseBtn: false,
jumpText: typeof findValue.toastText === "string" ? "不跳过" : "坠机",
jumpClickCallback: () => {
toastCloseCallBack();
}
});
setTimeout(() => {
if (AirborneHelperEvent.$data.tipJumpToastInfo) {
AirborneHelperEvent.$data.tipJumpToastInfo.close();
AirborneHelperEvent.$data.tipJumpToastInfo = void 0;
}
}, (beforeToastTime + 3) * 1e3);
}
}
},
bind() {
Object.keys(this.$event).forEach((eventName) => {
AirborneHelper.$data.art.on(
eventName,
this.$event[eventName]
);
});
},
unbind() {
Object.keys(this.$event).forEach((eventName) => {
AirborneHelper.$data.art.off(
eventName,
this.$event[eventName]
);
});
clearTimeout(AirborneHelperEvent.$data.tipJumpToastTimeoutId);
AirborneHelperEvent.$data.tipJumpToastTimeoutId = void 0;
if (AirborneHelperEvent.$data.successJumpToastInfo) {
AirborneHelperEvent.$data.successJumpToastInfo.close();
AirborneHelperEvent.$data.successJumpToastInfo = void 0;
}
if (AirborneHelperEvent.$data.tipJumpToastInfo) {
AirborneHelperEvent.$data.tipJumpToastInfo.close();
AirborneHelperEvent.$data.tipJumpToastInfo = void 0;
}
}
};
const AirborneHelper = {
$key: {
plugin_KEY: "plugin-airborne-helper"
},
$data: {
art: null,
option: null
},
init(art, option) {
this.$data.art = art;
this.update(option);
},
update(option) {
this.$data.option = option;
console.log(TAG + "更新配置", option);
AirborneHelperEvent.unbind();
if (option.clip_info_list.length) {
AirborneHelperEvent.bind();
}
}
};
const artplayerPluginAirborneHelper = (option) => {
return (art) => {
AirborneHelper.init(art, option);
return {
name: AirborneHelper.$key.plugin_KEY,
update(option2) {
AirborneHelper.update(option2);
}
};
};
};
const ArtPlayer_PLUGIN_AIRBORNE_HELPER_KEY = AirborneHelper.$key.plugin_KEY;
const TAG_FLV = "[flvjs]:";
const generateBangumiVideoSelectSetting = (option) => {
return option.epList.map((item) => {
return {
isDefault: item.ep_id === option.ep_id && item.aid === option.aid && item.cid === option.cid,
title: GenerateArtPlayerEpTitle(item.long_title, item.title),
aid: item.aid,
bvid: item.bvid,
cid: item.cid,
ep_id: item.ep_id,
onSelect(selectItem, index) {
BlibiliBangumiPlayer.updateArtPlayerVideoInfo(item, option.epList);
}
};
});
};
const BilibiliBangumiArtPlayer = {
$data: {
art: null,
flv: null,
/** 当前的配置项 */
currentOption: null,
from: "bangumi"
},
/**
* 重置环境变量
*/
resetEnv(isInit) {
if (isInit) {
Reflect.set(this.$data, "art", null);
Reflect.set(this.$data, "flv", null);
}
Reflect.set(this.$data, "currentOption", null);
},
/**
* flv播放
*
* 切换url时自动调用
* @param videoInfoList 可能多个,可能只有一个
*/
flvPlayer() {
if (this.$data.currentOption == null) {
console.error(TAG_FLV + "获取当前配置为空");
return;
}
let flvInfoList = this.$data.currentOption.flvInfo;
if (this.$data.flv != null || flvInfoList == null) {
this.$data.flv?.detachMediaElement();
this.$data.flv?.destroy();
}
let currentOption = this.$data.currentOption;
console.log(TAG_FLV + "加载视频", flvInfoList);
if (flvInfoList.length > 1) {
this.$data.flv = flvjs.createPlayer(
{
type: "flv",
filesize: currentOption.flvTotalSize,
duration: currentOption.flvTotalDuration,
segments: flvInfoList.map((item) => {
return {
url: item.url,
duration: item.duration,
filesize: item.size
};
})
},
{
stashInitialSize: 1024 * 100
}
);
} else {
this.$data.flv = flvjs.createPlayer(
{
type: "flv",
url: flvInfoList[0].url
},
{
stashInitialSize: 1024 * 100
}
);
}
this.$data.flv.attachMediaElement(this.$data.art.video);
this.$data.flv.load();
},
/**
* 初始化播放器
* @param option
*/
async init(option) {
this.resetEnv(true);
this.$data.currentOption = option;
const localArtDanmakuOption_KEY = "artplayer-bangumi-danmaku-option";
const artPlayerDanmakuOptionHelper = new ArtPlayerDanmakuOptionHelper(
localArtDanmakuOption_KEY
);
const localArtDanmakuOption = artPlayerDanmakuOptionHelper.getLocalArtDanmakuOption();
const artOption = {
...ArtPlayerCommonOption(),
container: option.container,
/** 自定义设置列表 */
settings: [],
plugins: [
artplayerPluginToast(),
artplayPluginQuality({
from: BilibiliBangumiArtPlayer.$data.from,
qualityList: option.quality
})
]
};
if (option.isFlv) {
artOption.quality = [];
artOption.type = "flv";
if (option.flvInfo.length === 0) {
BilibiliLogUtils.failToast("视频播放地址为空,无法播放!");
return;
}
artOption.url = option.flvInfo[0].url;
artOption.customType = {
flv: (video, url, art) => {
if (!flvjs.isSupported()) {
art.notice.show = "Unsupported playback format: flv";
return;
}
this.flvPlayer();
}
};
} else {
artOption.type = "mp4";
}
if (Panel.getValue("artplayer-plugin-bangumi-danmaku-enable")) {
artOption.plugins.push(
artplayerPluginDanmuku({
...ArtPlayerDanmakuCommonOption(),
danmuku: option.danmukuUrl,
// 以下为非必填
// 弹幕持续时间,范围在[1 ~ 10]
speed: localArtDanmakuOption.speed,
// 弹幕上下边距,支持像素数字和百分比
margin: localArtDanmakuOption["margin"],
// 弹幕透明度,范围在[0 ~ 1]
opacity: localArtDanmakuOption["opacity"],
// 弹幕可见的模式
modes: localArtDanmakuOption["modes"],
// 弹幕字体大小,支持像素数字和百分比
fontSize: localArtDanmakuOption["fontSize"],
// 弹幕是否防重叠
antiOverlap: localArtDanmakuOption["antiOverlap"],
// 是否同步播放速度
synchronousPlayback: localArtDanmakuOption["synchronousPlayback"],
// 弹幕层是否可见
visible: localArtDanmakuOption["visible"],
// 手动发送弹幕前的过滤器,返回 true 则可以发送,可以做存库处理
beforeEmit(danmu) {
return new Promise((resolve) => {
console.log(danmu);
setTimeout(() => {
resolve(true);
}, 1e3);
});
}
})
);
}
if (Panel.getValue("artplayer-plugin-bangumi-m4sAudioSupport-enable")) {
artOption.plugins.push(
artplayerPluginM4SAudioSupport({
from: BilibiliBangumiArtPlayer.$data.from,
audioList: option.audioList || []
})
);
}
if (Panel.getValue("artplayer-plugin-bangumi-epChoose-enable")) {
artOption.plugins.push(
artplayerPluginEpChoose({
EP_LIST: generateBangumiVideoSelectSetting(option),
automaticBroadcast: true
})
);
}
if (Panel.getValue("artplayer-plugin-bangumi-cc-subtitle-enable")) {
artOption.plugins.push(
artplayerPluginBilibiliCCSubTitle({
from: BilibiliBangumiArtPlayer.$data.from,
cid: option.cid,
aid: option.aid,
bvid: option.bvid,
ep_id: option.ep_id
})
);
}
if (Panel.getValue("artplayer-plugin-bangumi-toptoolbar-enable")) {
artOption.plugins.push(
artplayerPluginTopToolBar({
onlineInfoParams: {
aid: option.aid,
cid: option.cid,
bvid: option.bvid
},
title: option.videoTitle,
showWrap: true,
showTitle: true,
showOnlineTotal: true
})
);
}
if (Panel.getValue("artplayer-plugin-bangumi-airborneHelper-enable")) {
artOption.plugins.push(
artplayerPluginAirborneHelper({
clip_info_list: option.clip_info_list
})
);
}
if (Panel.getValue("artplayer-plugin-bangumi-statistics-enable")) {
artOption.plugins.push(
artplayerPluginVideoStatistics({
data: []
})
);
}
this.$data.art = new Artplayer(artOption);
artPlayerDanmakuOptionHelper.onConfigChange(this.$data.art);
return this.$data.art;
},
/**
* 更新新的播放信息
* @param art
* @param option
*/
async update(art, option) {
this.resetEnv(false);
this.$data.currentOption = option;
log$1.info(`更新新的播放信息`, option);
art.pause();
log$1.info(`暂停视频`);
art.currentTime = 0;
log$1.info(`重置播放进度`);
this.updatePluginInfo(art, option);
art.play();
log$1.info("播放");
},
/**
* 更新插件数据
* @param art
* @param option
*/
updatePluginInfo(art, option) {
let plugin_quality = art.plugins[ArtPlayer_PLUGIN_QUALITY_KEY];
plugin_quality.update({
from: BilibiliBangumiArtPlayer.$data.from,
qualityList: option.quality
});
log$1.info(`更新画质`, option.quality);
if (Panel.getValue("artplayer-plugin-bangumi-danmaku-enable")) {
art.plugins.artplayerPluginDanmuku.config({
danmuku: option.danmukuUrl
});
art.plugins.artplayerPluginDanmuku.load();
log$1.info(`更新弹幕姬`, option.danmukuUrl);
}
if (Panel.getValue("artplayer-plugin-bangumi-m4sAudioSupport-enable")) {
let plugin_m4sAudioSupport = art.plugins[ArtPlayer_PLUGIN_M4S_AUDIO_SUPPORT_KEY];
plugin_m4sAudioSupport.update({
from: BilibiliBangumiArtPlayer.$data.from,
audioList: option.audioList || []
});
log$1.info(`更新音频`, option.audioList);
}
if (Panel.getValue("artplayer-plugin-bangumi-epChoose-enable")) {
let plugin_epChoose = art.plugins[ArtPlayer_PLUGIN_EP_CHOOSE_KEY];
plugin_epChoose.update({
EP_LIST: generateBangumiVideoSelectSetting(option),
automaticBroadcast: true
});
log$1.info(`更新选集信息`, option.epList);
}
if (Panel.getValue("artplayer-plugin-bangumi-cc-subtitle-enable")) {
let plugin_bilibiliCCSubTitle = art.plugins[ArtPlayer_PLUGIN_BILIBILI_CC_SUBTITLE_KEY];
const subTitleOption = {
from: BilibiliBangumiArtPlayer.$data.from,
cid: option.cid,
aid: option.aid,
ep_id: option.ep_id
};
plugin_bilibiliCCSubTitle.update(subTitleOption);
log$1.info(`更新字幕`, subTitleOption);
}
if (Panel.getValue("artplayer-plugin-bangumi-toptoolbar-enable")) {
let plugin_topToolBar = art.plugins[ArtPlayer_PLUGIN_TOP_TOOLBAR_KEY];
const topToolBarOption = {
showRight: true,
showRightFollow: true,
showWrap: true,
showTitle: true,
showOnlineTotal: true,
title: option.videoTitle,
onlineInfoParams: {
aid: option.aid,
cid: option.cid,
bvid: option.bvid
}
};
plugin_topToolBar.update(topToolBarOption);
log$1.info(`更新顶部标题`, topToolBarOption);
}
if (Panel.getValue("artplayer-plugin-bangumi-airborneHelper-enable")) {
let plugin_airborneHelper = art.plugins[ArtPlayer_PLUGIN_AIRBORNE_HELPER_KEY];
plugin_airborneHelper.update({
clip_info_list: option.clip_info_list
});
log$1.info(`更新空降助手信息`, option.clip_info_list);
}
}
};
const ReactUtils = {
/**
* 等待react某个属性并进行设置
* @param $el 需要检测的元素对象
* @param reactPropNameOrNameList react属性的名称
* @param checkOption 检测的配置项
*/
async waitReactPropsToSet($el, reactPropNameOrNameList, checkOption) {
if (!Array.isArray(reactPropNameOrNameList)) {
reactPropNameOrNameList = [reactPropNameOrNameList];
}
if (!Array.isArray(checkOption)) {
checkOption = [checkOption];
}
function getTarget() {
let __target__ = null;
if (typeof $el === "string") {
__target__ = domUtils.selector($el);
} else if (typeof $el === "function") {
__target__ = $el();
} else if ($el instanceof HTMLElement) {
__target__ = $el;
}
return __target__;
}
if (typeof $el === "string") {
let $ele = await utils.waitNode($el, 1e4);
if (!$ele) {
return;
}
}
checkOption.forEach((needSetOption) => {
if (typeof needSetOption.msg === "string") {
log$1.info(needSetOption.msg);
}
function checkTarget() {
let $targetEl = getTarget();
if ($targetEl == null) {
return {
status: false,
isTimeout: true,
inst: null,
$el: $targetEl
};
}
let reactInst = utils.getReactObj($targetEl);
if (reactInst == null) {
return {
status: false,
isTimeout: false,
inst: null,
$el: $targetEl
};
}
let findPropNameIndex = Array.from(reactPropNameOrNameList).findIndex(
(__propName__) => {
let reactPropInst2 = reactInst[__propName__];
if (!reactPropInst2) {
return false;
}
let checkResult = needSetOption.check(reactPropInst2, $targetEl);
checkResult = Boolean(checkResult);
return checkResult;
}
);
let reactPropName = reactPropNameOrNameList[findPropNameIndex];
let reactPropInst = reactInst[reactPropName];
return {
status: findPropNameIndex !== -1,
isTimeout: false,
inst: reactPropInst,
$el: $targetEl
};
}
utils.waitPropertyByInterval(
() => {
return getTarget();
},
() => checkTarget().status,
250,
1e4
).then(() => {
let checkTargetResult = checkTarget();
if (checkTargetResult.status) {
let reactInst = checkTargetResult.inst;
needSetOption.set(reactInst, checkTargetResult.$el);
} else {
if (typeof needSetOption.failWait === "function") {
needSetOption.failWait(checkTargetResult.isTimeout);
}
}
});
});
}
};
function handleDashVideoQualityInfo(dashInfo) {
let acceptVideoQualityInfoList = [];
dashInfo.video.forEach((dashVideoInfo) => {
if (!dashInfo.accept_quality.includes(dashVideoInfo.id)) {
return;
}
let findSupportFormat = dashInfo.support_formats.find(
(formatsItem) => formatsItem.quality === dashVideoInfo.id
);
let videoUrl = BilibiliCDNProxy.findBetterCDN(
dashVideoInfo.base_url,
dashVideoInfo.baseUrl,
dashVideoInfo.backup_url,
dashVideoInfo.backupUrl
);
videoUrl = BilibiliCDNProxy.replaceBangumiVideoCDN(videoUrl);
let qualityName = findSupportFormat?.new_description;
acceptVideoQualityInfoList.push({
name: qualityName,
url: videoUrl,
type: dashVideoInfo.mimeType,
id: dashVideoInfo.id,
size: dashVideoInfo.size,
quality: dashVideoInfo.id,
vip: Boolean(findSupportFormat?.need_vip),
bandwidth: dashVideoInfo.bandwidth,
frameRate: dashVideoInfo.frameRate,
codecid: dashVideoInfo.codecid,
codecs: dashVideoInfo.codecs
});
});
return acceptVideoQualityInfoList;
}
const GenerateVideoTitle = (ep_id, title) => {
return `第${ep_id}话 ${title}`;
};
const handleQueryVideoQualityData = (bangumiInfo, userChooseVideoCodingCode) => {
let qualityInfoList = [];
if (bangumiInfo?.dash?.video?.length) {
let dashBangumiInfo = bangumiInfo;
qualityInfoList = [
...handleDashVideoQualityInfo({
accept_quality: dashBangumiInfo.accept_quality,
support_formats: dashBangumiInfo.support_formats,
video: dashBangumiInfo.dash.video
})
];
if (qualityInfoList.length === 0) {
if (dashBangumiInfo.dash.video.length !== 0) {
log$1.warn(
`当前选择的视频编码id为: ${userChooseVideoCodingCode},但是过滤出的视频没有一个符合的,所以直接放弃使用自定义选择视频编码`
);
qualityInfoList = [
...handleDashVideoQualityInfo({
accept_quality: dashBangumiInfo.accept_quality,
support_formats: dashBangumiInfo.support_formats,
video: dashBangumiInfo.dash.video
})
];
}
}
} else {
let mp4BangumiInfo = bangumiInfo;
if (mp4BangumiInfo.durls.length === 0) {
if (mp4BangumiInfo.durl != null) {
mp4BangumiInfo.durls.push({
quality: mp4BangumiInfo.quality,
durl: mp4BangumiInfo.durl
});
}
}
mp4BangumiInfo.durls.forEach((durlInfo) => {
if (!mp4BangumiInfo.accept_quality.includes(durlInfo.quality)) {
return;
}
if (!durlInfo.durl.length) {
return;
}
let currentDurl = durlInfo["durl"][0];
let findSupportFormat = bangumiInfo.support_formats.find(
(formatsItem) => formatsItem.quality === durlInfo.quality
);
let videoUrl = BilibiliCDNProxy.findBetterCDN(
currentDurl.url,
currentDurl.backup_url
);
let qualityName = findSupportFormat?.new_description;
qualityInfoList.push({
name: qualityName,
url: videoUrl,
type: "audio/mp4",
id: durlInfo.quality,
size: currentDurl.size,
quality: durlInfo.quality,
vip: Boolean(findSupportFormat?.need_vip),
bandwidth: 0,
frameRate: "",
codecid: 0,
codecs: ""
});
});
}
return qualityInfoList;
};
const GenerateArtPlayerOption = async (EP_INFO, EP_LIST) => {
const { aid, bvid, cid, ep_id, title, long_title } = EP_INFO;
log$1.info(`解析番剧信息 aid:${aid} cid:${cid} ep_id:${ep_id}`);
const videoTitle = GenerateVideoTitle(title, long_title);
const audioInfo = [];
let qualityInfo = [];
let clip_info_list = [];
let isFlv = false;
let flvInfo = [];
let flvTotalDuration = 0;
let flvTotalSize = 0;
if (Panel.getValue("bili-bangumi-unlockAreaLimit")) {
const bangumiInfo = await BilibiliBangumiApi.getPlayUrl({
avid: aid,
cid,
ep_id
});
if (!bangumiInfo) {
return;
}
if (Array.isArray(bangumiInfo?.clip_info_list)) {
clip_info_list = bangumiInfo.clip_info_list;
} else if (Array.isArray(bangumiInfo?.clip_info)) {
clip_info_list = // @ts-ignore
bangumiInfo.clip_info;
}
if (bangumiInfo.type.toLowerCase() === "flv") {
isFlv = true;
bangumiInfo.durl.forEach((durlInfo) => {
let videoUrl = BilibiliCDNProxy.findBetterCDN(
durlInfo.url,
durlInfo.backup_url
);
videoUrl = BilibiliCDNProxy.replaceBangumiVideoCDN(videoUrl);
flvTotalDuration += durlInfo.length;
flvTotalSize += durlInfo.size;
flvInfo.push({
order: durlInfo.order,
url: videoUrl,
duration: durlInfo.length,
length: durlInfo.length,
size: durlInfo.size
});
});
} else if (bangumiInfo.type.toLowerCase() === "dash" || bangumiInfo.type.toLowerCase() === "mp4") {
(bangumiInfo?.dash?.audio || []).forEach((item) => {
let audioUrl = BilibiliCDNProxy.findBetterCDN(
item.baseUrl,
item.base_url,
item.baseUrl,
item.backup_url
);
if (Panel.getValue("bili-bangumi-uposServerSelect-applyAudio")) {
audioUrl = BilibiliCDNProxy.replaceBangumiVideoCDN(audioUrl);
}
audioInfo.push({
url: audioUrl,
id: item.id,
size: item.size,
text: VideoSoundQualityCode[item.id] || "",
bandwidth: item.bandwidth,
codecs: item.codecs,
mimeType: item.mimeType || item.mime_type
});
});
log$1.info(`ArtPlayer: 获取的音频信息`, audioInfo);
qualityInfo = qualityInfo.concat(
handleQueryVideoQualityData(bangumiInfo)
);
log$1.info(`ArtPlayer: 获取的视频画质信息`, qualityInfo);
} else {
BilibiliLogUtils.failToast(
"暂未适配的视频格式:" + bangumiInfo["format"]
);
return;
}
} else {
const bangumiInfo = await BilibiliBangumiApi.getPlayUrlHTML5({
avid: aid,
cid,
ep_id
});
if (!bangumiInfo) {
return;
}
if (Array.isArray(bangumiInfo?.clip_info_list)) {
clip_info_list = // @ts-ignore
bangumiInfo.clip_info_list;
} else if (Array.isArray(bangumiInfo?.clip_info)) {
clip_info_list = bangumiInfo.clip_info;
}
qualityInfo = qualityInfo.concat(handleQueryVideoQualityData(bangumiInfo));
}
const currentVideoQuality = qualityInfo.map((item, index) => {
return {
html: item.name,
url: item.url,
quality: item.quality,
mimeType: item.type,
codecid: item.codecid,
codecs: item.codecs,
frameRate: item.frameRate,
bandwidth: item.bandwidth
};
});
const artPlayerOption = {
// @ts-ignore
container: null,
epList: EP_LIST,
cid,
aid,
bvid,
ep_id,
videoTitle,
danmukuUrl: `https://api.bilibili.com/x/v1/dm/list.so?oid=${cid}`,
quality: currentVideoQuality,
clip_info_list,
isFlv,
flvInfo,
flvTotalDuration,
flvTotalSize
};
artPlayerOption.url = qualityInfo?.[0]?.url;
if (audioInfo.length) {
artPlayerOption.audioList = audioInfo.map((item, index) => {
return {
isDefault: index === 0,
url: item.url,
soundQualityCode: item.id,
soundQualityCodeText: item.text,
bandwidth: item.bandwidth,
codecs: item.codecs,
mimeType: item.mimeType,
size: item.size
};
});
}
return artPlayerOption;
};
const BlibiliBangumiPlayer = {
$data: {
art: null
},
/**
* 更新播放器的信息
*/
updateArtPlayerVideoInfo(ep_info, ep_list) {
const that = this;
ReactUtils.waitReactPropsToSet(
BilibiliData.className.bangumi_new + ` [class^="Player_container"]`,
"reactFiber",
{
check(reactInstance) {
return typeof reactInstance?.return?.memoizedState?.queue?.lastRenderedState?.[0]?.epInfo?.bvid === "string";
},
async set(reactInstance) {
let epInfo = reactInstance?.return?.memoizedState?.queue?.lastRenderedState?.[0]?.epInfo;
const $playerWrapper = $("#bilibiliPlayer");
if (ep_info == null) {
ep_info = epInfo;
}
if (ep_list == null) {
ep_list = [];
let $epList = $(
BilibiliData.className.bangumi_new + ` [class^="EpisodeList_episodeListWrap"]`
);
if ($epList) {
let react = utils.getReactObj($epList);
let epList = react?.reactFiber?.return?.memoizedState?.memoizedState?.[0]?.episodes;
if (Array.isArray(epList)) {
ep_list = epList;
}
}
}
const artPlayerOption = await GenerateArtPlayerOption(
ep_info,
ep_list
);
if (artPlayerOption == null) {
return;
}
let $artPlayer = $("#artplayer");
if (!$artPlayer) {
const $artPlayerContainer = domUtils.createElement("div", {
className: "artplayer-container",
innerHTML: (
/*html*/
`
`
)
});
$artPlayer = $artPlayerContainer.querySelector("#artplayer");
domUtils.after($playerWrapper, $artPlayerContainer);
}
artPlayerOption.container = $artPlayer;
if (that.$data.art == null) {
let art = await BilibiliBangumiArtPlayer.init(artPlayerOption);
if (art) {
that.$data.art = art;
} else {
return;
}
that.$data.art.volume = 1;
} else {
BilibiliBangumiArtPlayer.update(that.$data.art, artPlayerOption);
}
}
}
);
}
};
const BilibiliBangumi = {
$data: {
art: null
},
init() {
Panel.execMenuOnce("bili-bangumi-initialScale", () => {
BilibiliUtils.initialScale();
});
Panel.execMenuOnce("bili-bangumi-hook-callApp", () => {
this.hookCallApp();
});
Panel.execMenu("bili-bangumi-cover-clicl-event-chooseEp", () => {
this.setChooseEpClickEvent();
});
Panel.execMenu("bili-bangumi-cover-clicl-event-other", () => {
this.setClickOtherVideo();
});
Panel.execMenu("bili-bangumi-cover-clicl-event-recommend", () => {
this.setRecommendClickEvent();
});
this.coverVideoPlayer();
},
/**
* 阻止唤醒App
*/
hookCallApp() {
let oldSetTimeout = _unsafeWindow.setTimeout;
_unsafeWindow.setTimeout = function(...args) {
let callString = args[0].toString();
if (callString.includes("autoOpenApp")) {
log$1.success("阻止唤醒App", args);
return;
}
return Reflect.apply(oldSetTimeout, this, args);
};
},
/**
* 覆盖【选集】的点击事件
*/
setChooseEpClickEvent() {
utils.waitNode(
BilibiliData.className.bangumi + " .ep-list-pre-wrapper ul.ep-list-pre-container"
).then(($preContainer) => {
log$1.info("覆盖【选集】的点击事件");
domUtils.on(
$preContainer,
"click",
"li.episode-item",
function(event) {
utils.preventEvent(event);
BilibiliOpenApp.jumpToUrl(event);
},
{
capture: true
}
);
});
utils.waitNode(
BilibiliData.className.bangumi + " .ep-list-pre-wrapper ul.season-list-wrapper"
).then(($listWapper) => {
log$1.info("覆盖【xx季】的点击事件");
domUtils.on(
$listWapper,
"click",
"li",
function(event) {
utils.preventEvent(event);
BilibiliOpenApp.jumpToUrl(event);
},
{
capture: true
}
);
});
utils.waitNode(
BilibiliData.className.bangumi + " .ep-list-pre-header"
).then(($preHeader) => {
log$1.info("覆盖【选集】右上角的【全xx话】Arrow的点击事件");
domUtils.on(
$preHeader,
"click",
function(event) {
utils.preventEvent(event);
},
{
capture: true
}
);
});
domUtils.on(
document,
"click",
[
BilibiliData.className.bangumi_new + ` [class^="EpisodeList_episodeListWrap"] m-open-app[universallink]`,
BilibiliData.className.bangumi_new + ` [class^="SeasonList_container"] m-open-app[universallink]`
],
(event, selectorTarget) => {
let url = BilibiliOpenApp.getUrl(selectorTarget);
if (!url) {
Qmsg.error("获取跳转链接失败");
return;
}
BilibiliUtils.goToUrl(url);
},
{
capture: true
}
);
},
/**
* 覆盖【PV&其他】、【预告】、【主题曲】的点击事件
*
* + https://m.bilibili.com/bangumi/play/ss48852
*/
setClickOtherVideo() {
utils.waitNode(
BilibiliData.className.bangumi + " .section-preview-wrapper ul.ep-list-pre-container"
).then(($preContainer) => {
log$1.info("覆盖【PV&其他】、【预告】、【主题曲】的点击事件");
domUtils.on(
$preContainer,
"click",
"li.section-preview-item",
function(event) {
utils.preventEvent(event);
BilibiliOpenApp.jumpToUrl(event);
},
{
capture: true
}
);
});
utils.waitNode(
BilibiliData.className.bangumi + " .section-preview-header"
).then(($previewHeader) => {
log$1.info(
"覆盖【PV&其他】、【预告】、【主题曲】右上角的Arrow的点击事件"
);
domUtils.on(
$previewHeader,
"click",
function(event) {
utils.preventEvent(event);
},
{
capture: true
}
);
});
domUtils.on(
document,
"click",
BilibiliData.className.bangumi_new + ` [class^="SectionPanel_container"] m-open-app[universallink]`,
(event, selectorTarget) => {
let url = BilibiliOpenApp.getUrl(selectorTarget);
if (!url) {
Qmsg.error("获取跳转链接失败");
return;
}
BilibiliUtils.goToUrl(url);
},
{
capture: true
}
);
},
/**
* 覆盖【更多推荐】番剧的点击事件
*/
setRecommendClickEvent() {
utils.waitNode(
BilibiliData.className.bangumi + " .recom-wrapper ul.recom-list"
).then(($recomList) => {
log$1.info("覆盖【更多推荐】番剧的点击事件");
domUtils.on(
$recomList,
"click",
"li.recom-item-v2",
function(event) {
utils.preventEvent(event);
BilibiliOpenApp.jumpToUrl(event);
},
{
capture: true
}
);
});
domUtils.on(
document,
"click",
BilibiliData.className.bangumi_new + ` [class^="Footer_container"] m-open-app[universallink]`,
(event, selectorTarget) => {
let url = BilibiliOpenApp.getUrl(selectorTarget);
if (!url) {
Qmsg.error("获取跳转链接失败");
return;
}
BilibiliUtils.goToUrl(url);
},
{
capture: true
}
);
},
/**
* 覆盖视频播放器
*/
coverVideoPlayer() {
if (document.querySelector("#artplayer")) {
log$1.warn("已存在播放器,更新播放信息");
} else {
addStyle(
/*css*/
`
.player-wrapper,
.open-app-bar,
${BilibiliData.className.bangumi_new} [class^="Player_videoWrap"] > div:not(.artplayer-container){
display: none !important;
}
${artPlayerCommonCSS}
${artPlayerCSS}
.artplayer-container{
height: -webkit-fill-available;
height: 100%;
}
`
);
let controlsPadding = Panel.getValue(
"bili-bangumi-artplayer-controlsPadding-left-right",
0
);
if (controlsPadding != 0) {
addStyle(
/*css*/
`
@media (orientation: landscape) {
.art-video-player .art-layers .art-layer-top-wrap,
/* 底部 */
.art-video-player .art-bottom{
padding-left: ${controlsPadding}px !important;
padding-right: ${controlsPadding}px !important;
}
/* 锁定图标 */
.art-video-player .art-layer-lock{
--art-lock-left-size: ${controlsPadding}px;
}
}
`
);
}
}
BlibiliBangumiPlayer.updateArtPlayerVideoInfo();
}
};
const BilibiliSearchApi = {
/**
* 获取输入框的placeholder的热点关键词
*/
async getSearchInputPlaceholder() {
let getResponse = await httpx.get(
"https://api.bilibili.com/x/web-interface/wbi/search/default",
{
fetch: true,
headers: {
accept: "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"cache-control": "no-cache",
pragma: "no-cache",
"sec-ch-ua": '""',
"sec-ch-ua-mobile": "?1",
"sec-ch-ua-platform": '""',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site"
},
allowInterceptConfig: false
}
);
if (!getResponse.status) {
return;
}
let responseData = utils.toJSON(getResponse.data.responseText);
if (!BilibiliApiResponseCheck.isWebApiSuccess(responseData)) {
return;
}
return responseData.data;
},
/**
* 从代理服务器拉取番剧搜索结果
*/
async getBangumiSearchResult(config) {
let searchParamsData = {
search_type: "media_bangumi",
keyword: config.keyword,
from_client: "BROWSER",
drm_tech_type: "2",
module: "bangumi",
area: config.area.toLowerCase(),
access_key: BilibiliQrCodeLogin.getAccessToken()
};
let url = `https://${config.host}/x/web-interface/search/type?${utils.toSearchParamsStr(searchParamsData)}`;
let getResponse = await httpx.get(url, {
fetch: false,
headers: {
"User-Agent": utils.getRandomAndroidUA()
}
});
if (!getResponse.status) {
return;
}
let data2 = utils.toJSON(getResponse.data.responseText);
if (!BilibiliApiResponseCheck.isWebApiSuccess(data2)) {
log$1.error(`请求失败,当前代理服务器信息:${JSON.stringify(config.host)}`);
log$1.error(`请求失败,当前请求的响应信息:${JSON.stringify(data2)}`);
return {
isSuccess: false,
data: data2
};
}
return {
isSuccess: true,
data: data2.data.result
};
}
};
const beautifyCSS = "#app .m-search {\r\n --card-img-width: 90px;\r\n --card-img-height: calc(var(--card-img-width) * 1.33);\r\n --card-desc-color: #808080;\r\n --card-desc-size: 0.8em;\r\n --card-badge-item-size: 0.7em;\r\n --card-badge-item-padding: 0.1em 0.2em;\r\n --card-badge-item-border-radius: 3px;\r\n --card-ep-item-border-radius: 4px;\r\n --card-ep-item-padding-top-bottom: 13px;\r\n --card-ep-item-padding-left-right: 13px;\r\n --card-ep-item-badge-padding: 2px;\r\n}\r\n.gm-result-panel {\r\n padding-top: 23.46667vmin;\r\n background: #f4f4f4;\r\n}\r\n.gm-card-cover {\r\n position: relative;\r\n}\r\n.gm-card-cover img {\r\n width: var(--card-img-width);\r\n height: var(--card-img-height);\r\n border-radius: 8px;\r\n}\r\n.gm-card-container {\r\n display: flex;\r\n gap: 15px;\r\n}\r\n\r\n.gm-card-box {\r\n padding: 0px 10px;\r\n}\r\n\r\n.gm-card-item em {\r\n color: var(--bili-color);\r\n font-style: unset;\r\n}\r\n\r\n.gm-card-title {\r\n font-family: 微软雅黑;\r\n font-size: 1em;\r\n}\r\n\r\n.gm-card-display-info,\r\n.gm-card-styles,\r\nspan.gm-card-media_score-user_count {\r\n font-size: var(--card-desc-size);\r\n color: var(--card-desc-color);\r\n}\r\n\r\n.gm-card-info-container {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 3px;\r\n justify-content: flex-start;\r\n}\r\n.gm-card-info {\r\n display: flex;\r\n flex-direction: column;\r\n justify-content: space-between;\r\n}\r\nspan.gm-card-media_score-score {\r\n color: #f77c2e;\r\n font-size: 1.2em;\r\n font-weight: bold;\r\n}\r\n\r\n.gm-card-media_score {\r\n display: flex;\r\n align-items: flex-end;\r\n gap: 0.5em;\r\n}\r\n.gm-card-item {\r\n padding: 1.6vmin;\r\n background: #fff;\r\n margin: 10px 0px;\r\n border-radius: 6px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 15px;\r\n overflow: hidden;\r\n}\r\n.gm-card-badges {\r\n background: var(--bili-color);\r\n color: #fff;\r\n padding: 3px;\r\n font-size: 12px;\r\n border-radius: 3px;\r\n white-space: nowrap;\r\n position: absolute;\r\n top: 5px;\r\n right: 5px;\r\n}\r\n.gm-card-badge-info-item {\r\n font-size: var(--card-badge-item-size);\r\n padding: var(--card-badge-item-padding);\r\n border-radius: var(--card-badge-item-border-radius);\r\n}\r\n.gm-card-eps {\r\n display: flex;\r\n overflow: auto;\r\n gap: 10px;\r\n}\r\n\r\n.gm-card-ep-conatiner {\r\n text-align: center;\r\n white-space: nowrap;\r\n padding: var(--card-ep-item-padding-top-bottom)\r\n var(--card-ep-item-padding-left-right);\r\n background: #edeff3;\r\n border-radius: var(--card-ep-item-border-radius);\r\n font-size: 14px;\r\n position: relative;\r\n}\r\n\r\n.gm-card-ep-badges-container {\r\n position: absolute;\r\n top: 0;\r\n right: 0;\r\n font-size: calc(\r\n var(--card-ep-item-padding-top-bottom) - var(--card-ep-item-badge-padding)\r\n );\r\n}\r\n\r\n.gm-card-ep-badge-top-right {\r\n border-top-right-radius: var(--card-ep-item-border-radius);\r\n border-bottom-left-radius: var(--card-ep-item-border-radius);\r\n padding: var(--card-ep-item-badge-padding);\r\n}\r\n.gm-card-ep-info-container {\r\n min-width: 30px;\r\n}\r\n";
const BilibiliExtraSearch = {
$flag_css: {
enableOtherAreaSearchBangumi: false
},
init() {
addStyle(beautifyCSS);
domUtils.ready(() => {
Panel.execMenu("bili-search-enableOtherAreaSearchBangumi", () => {
this.enableOtherAreaSearchBangumi();
});
});
},
/**
* 初始化搜索的tab
*/
enableOtherAreaSearchBangumi() {
if (!this.$flag_css.enableOtherAreaSearchBangumi) {
this.$flag_css.enableOtherAreaSearchBangumi = true;
addStyle(
/*css*/
`
.m-search-result .tabs{
overflow: auto;
white-space: nowrap;
}
.m-search-result .tabs .tab-item{
display: inline-block;
height: 8vmin;
line-height: 8vmin;
color: #757575;
font-size: 3.73333vmin;
margin-top: 1.86667vmin;
padding: 0 2.33vmin;
}
.m-search-result .tabs .tab-item:first-child{
padding-left: 0;
}
.m-search-result .tabs .tab-item:last-child{
padding-right: 0;
}
.m-search-result .tabs .tab-item.on{
color: var(--bili-color);
border-bottom: 0.53333vmin solid var(--bili-color);
}
`
);
}
utils.waitNode(".m-search-result .tabs:not(:has(.gm-tab-item))").then(($tabs) => {
let enableSearchServer = BilibiliApiProxy.getSearchProxyHost();
enableSearchServer.forEach((proxyServerInfo) => {
let $tab = domUtils.createElement(
"a",
{
className: "tab-item gm-tab-item",
innerHTML: `番剧(${proxyServerInfo.name})`
},
{
"data-area": proxyServerInfo.area,
"data-host": proxyServerInfo.host
}
);
$tabs.appendChild($tab);
});
const refreshTabActive = ($tab) => {
$tabs.querySelectorAll(".tab-item").forEach(($ele) => $tab != $ele && $ele.classList.remove("on"));
$tab.classList.add("on");
};
domUtils.on($tabs, "click", ".tab-item", async (event) => {
let $tab = event.target;
refreshTabActive($tab);
let $resultPanel = document.querySelector(".result-panel");
let $oldGmResultPanel = document.querySelector(".gm-result-panel");
if ($oldGmResultPanel) {
$oldGmResultPanel.remove();
domUtils.show($resultPanel);
}
if (!$tab.classList.contains("gm-tab-item")) {
return;
}
let area = $tab.dataset.area;
let host = $tab.dataset.host;
let $searchResult = document.querySelector(".m-search-result");
let searchResultVueIns = VueUtils.getVue($searchResult);
searchResultVueIns.switchTab(233);
domUtils.hide($resultPanel);
let keyword = searchResultVueIns.keyword;
let $loading = Qmsg.loading("搜索中,请稍后...");
let searchBangumiResultInfo = await BilibiliSearchApi.getBangumiSearchResult({
keyword,
area,
host
});
$loading.close();
if (!searchBangumiResultInfo) {
return;
}
if (!searchBangumiResultInfo.isSuccess) {
alert(JSON.stringify(searchBangumiResultInfo.data, null, 2));
return;
}
let searchBangumiResultData = searchBangumiResultInfo.data;
log$1.info("搜索结果:", searchBangumiResultData);
let $gmResultPanel = domUtils.createElement("div", {
className: "gm-result-panel",
innerHTML: (
/*html*/
`
`
)
});
let $gmCardBox = $gmResultPanel.querySelector(".gm-card-box");
searchBangumiResultData.forEach((searchBangumiResultItem) => {
$gmCardBox.appendChild(
this.createSearchResultVideoItem(searchBangumiResultItem)
);
});
$searchResult.appendChild($gmResultPanel);
});
});
},
/**
* 创建搜索结果项
*/
createSearchResultVideoItem(option) {
let $item = domUtils.createElement(
"div",
{
className: "gm-card-item",
innerHTML: (
/*html*/
`
${option.season_type_name}
${option.title}
${option.styles || Reflect.get(option, "style") || Reflect.get(option, "styles_v2") || ""}
`
)
},
{
"data-url": option.url,
"data-type": option.type,
"data-media_id": option.media_id,
"data-pgc_season_id": option.pgc_season_id,
"data-is_follow": option.is_follow,
"data-is_selection": option.is_selection
}
);
Reflect.set($item, "data-option", option);
domUtils.on($item, "click", (event) => {
utils.preventEvent(event);
window.open(option.url, "_blank");
});
let $displayInfo = $item.querySelector(
".gm-card-display-info"
);
let totalDisplayInfo = [];
if (Array.isArray(option?.display_info)) {
totalDisplayInfo = totalDisplayInfo.concat(option.display_info);
}
if (Array.isArray(option?.badges)) {
totalDisplayInfo = totalDisplayInfo.concat(option.badges);
}
totalDisplayInfo = utils.uniqueArray(totalDisplayInfo, (item) => item.text);
totalDisplayInfo.forEach((displayInfo) => {
let $displayInfoItem = domUtils.createElement("span", {
className: "gm-card-badge-info-item",
innerText: displayInfo.text
});
if (typeof displayInfo.border_color === "string") {
$displayInfoItem.style.border = `1px solid ${displayInfo.border_color}`;
$displayInfoItem.style.color = displayInfo.border_color;
}
domUtils.append($displayInfo, $displayInfoItem);
});
if (option.pubtime) {
domUtils.append(
$displayInfo,
/*html*/
`
${utils.formatTime(option.pubtime * 1e3, "yyyy")}
`
);
}
let areas = option.areas || Reflect.get(option, "area");
if (areas) {
if ($displayInfo.children.length) {
domUtils.append(
$displayInfo,
/*html*/
`
|
`
);
}
domUtils.append(
$displayInfo,
/*html*/
`
${areas}
`
);
}
let $mediaScore = $item.querySelector(".gm-card-media_score");
if (option.media_score && option.media_score.user_count) {
domUtils.append(
$mediaScore,
/*html*/
`
${option.media_score?.score || 0}分
${option.media_score?.user_count || 0}人参与
`
);
}
let $eps = $item.querySelector(".gm-card-eps");
let epsList = [
...option.eps || [],
...Reflect.get(option, "episodes_new") || []
].filter((item) => utils.isNotNull(item));
epsList.forEach((epsItem) => {
let title = epsItem.title || epsItem.long_title;
let url = epsItem.url || Reflect.get(epsItem, "uri");
let $epItem = domUtils.createElement(
"div",
{
className: "gm-card-ep-conatiner",
innerHTML: (
/*html*/
`
${title}
`
)
},
{
"data-id": epsItem.id,
"data-url": url,
"data-title": title,
"data-long_title": epsItem.long_title
}
);
let $epBadges = $epItem.querySelector(
".gm-card-ep-badges-container"
);
$epItem.querySelector(
".gm-card-ep-info-container"
);
if (Array.isArray(epsItem.badges) && epsItem.badges.length) {
let epItemBadgeInfo = epsItem.badges[0];
let $badge = domUtils.createElement("span", {
className: "gm-card-ep-badge-top-right",
innerText: epItemBadgeInfo.text
});
if (typeof epItemBadgeInfo.bg_color === "string") {
$badge.style.backgroundColor = epItemBadgeInfo.bg_color;
}
if (typeof epItemBadgeInfo.text_color === "string") {
$badge.style.color = epItemBadgeInfo.text_color;
}
domUtils.append($epBadges, $badge);
}
domUtils.on($epItem, "click", (event) => {
utils.preventEvent(event);
window.open(url, "_blank");
});
$eps.appendChild($epItem);
});
return $item;
},
/**
* 搜索番剧(从自定义服务器拉取搜索结果)
*/
searchBangumi() {
}
};
const BilibiliSearchBeautify = {
$flag: { mutationSearchResult: false },
init() {
this.mutationSearchResult();
},
/**
* 监听搜索结果改变
*/
mutationSearchResult() {
if (this.$flag.mutationSearchResult) {
return;
}
this.$flag.mutationSearchResult = true;
addStyle(
/*css*/
`
.bangumi-list{
padding: 0 10px;
}
`
);
utils.mutationObserver(document, {
config: {
subtree: true,
childList: true
},
callback: utils.debounce(() => {
document.querySelectorAll(".m-search-bangumi-item").forEach(($bangumiItem) => {
let vueIns = VueUtils.getVue($bangumiItem);
if (!vueIns) {
return;
}
let info = vueIns.info;
if (!info) {
return;
}
let $newBangumiItem = BilibiliExtraSearch.createSearchResultVideoItem(info);
domUtils.after($bangumiItem, $newBangumiItem);
$bangumiItem.remove();
});
})
});
}
};
const BilibiliSearchVueProp = {
init() {
Panel.execMenuOnce("bili-search-vue-prop-noCallApp", () => {
this.noCallApp();
});
Panel.execMenuOnce("bili-search-vue-prop-openAppDialog", () => {
this.openAppDialog();
});
},
/**
* 该属性会让点击搜索结果弹出打开哔哩哔哩app的弹窗
* + __vue__.noCallApp
*/
noCallApp() {
let lockFn = new utils.LockFunction(() => {
$$(
".video-list .card-box > div:not([data-gm-inject-no-call-app])"
).forEach(($card) => {
let vueIns = VueUtils.getVue($card);
if (!vueIns) {
return;
}
if (typeof vueIns.noCallApp === "boolean") {
Object.defineProperty(vueIns, "noCallApp", {
value: true,
writable: false,
enumerable: true,
configurable: true
});
$card.setAttribute("data-gm-inject-no-call-app", "true");
}
});
});
utils.mutationObserver(document, {
config: {
subtree: true,
childList: true
},
callback() {
lockFn.run();
}
});
},
/**
* 该属性会让点击搜索结果弹出打开哔哩哔哩app的弹窗
* + __vue__.openAppDialog
*/
openAppDialog() {
let lockFn = new utils.LockFunction(() => {
$$(
".video-list .card-box > div:not([data-gm-inject-openAppDialog])"
).forEach(($card) => {
let vueIns = VueUtils.getVue($card);
if (!vueIns) {
return;
}
if (typeof vueIns.openAppDialog === "boolean") {
Object.defineProperty(vueIns, "openAppDialog", {
value: false,
writable: false,
enumerable: true,
configurable: true
});
$card.setAttribute("data-gm-inject-openAppDialog", "true");
}
});
});
utils.mutationObserver(document, {
config: {
subtree: true,
childList: true
},
callback() {
lockFn.run();
}
});
}
};
const BilibiliSearch = {
init() {
if (BilibiliRouter.isSearchResult()) {
BilibiliExtraSearch.init();
}
BilibiliSearchVueProp.init();
Panel.execMenuOnce("bili-search-cover-cancel", () => {
this.coverCancel();
});
Panel.execMenu("bili-search-beautifySearchResult", () => {
BilibiliSearchBeautify.init();
});
Panel.execMenuOnce("bili-search-cover-card-result-click-event", () => {
this.coverCardResultClickEvent();
});
domUtils.ready(() => {
Panel.execMenu("bili-search-inputAutoFocus", () => {
this.inputAutoFocus();
});
});
},
/**
* 覆盖【取消】按钮的点击事件
*/
coverCancel() {
log$1.info("覆盖【取消】按钮的点击事件");
domUtils.on(
document,
"click",
"a.cancel",
(event) => {
log$1.info(`点击取消按钮`);
utils.preventEvent(event);
window.history.back();
},
{ capture: true }
);
},
/**
* 输入框自动获取焦点
*/
inputAutoFocus() {
let searchParams = new URLSearchParams(window.location.search);
if (searchParams.has("keyword")) {
log$1.warn(`当前在搜索结果页面,不执行输入框自动获取焦点`);
return;
}
log$1.info(`输入框自动获取焦点`);
utils.waitNode(
`.m-search .m-search-search-bar input[type="search"]`,
1e4
).then(($input) => {
if (!$input) {
log$1.error("获取输入框失败");
return;
}
$input.focus();
});
},
/**
* 覆盖搜索结果点击事件
*/
coverCardResultClickEvent() {
log$1.info(`覆盖搜索结果点击事件`);
domUtils.on(
document,
"click",
".video-list .card-box > div",
(evt, selectorTarget) => {
let $card = selectorTarget;
let vueIns = VueUtils.getVue($card);
if (!vueIns) {
return;
}
let cardClick = vueIns.cardClick;
if (typeof cardClick !== "function") {
return;
}
utils.preventEvent(evt);
cardClick(evt);
},
{
capture: true
}
);
}
};
const BilibiliLiveBlockNode = {
init() {
Panel.execMenuOnce("bili-live-block-chatRoom", () => {
return this.blockChatRoom();
});
Panel.execMenuOnce("bili-live-block-brush-prompt", () => {
return this.blockBrushPrompt();
});
Panel.execMenuOnce("bili-live-block-control-panel", () => {
return this.blockControlPanel();
});
},
/**
* 屏蔽聊天室
*/
blockChatRoom() {
log$1.info("屏蔽聊天室");
return CommonUtil.addBlockCSS("#chat-items");
},
/**
* 屏蔽xxx进入直播间
*/
blockBrushPrompt() {
log$1.info("屏蔽xxx进入直播间");
return CommonUtil.addBlockCSS("#brush-prompt");
},
/**
* 屏蔽底部工具栏
*/
blockControlPanel() {
log$1.info("屏蔽底部工具栏");
return CommonUtil.addBlockCSS(".control-panel");
}
};
const BilibiliLive = {
init() {
BilibiliLiveBlockNode.init();
Panel.execMenuOnce("bili-live-prevent-openAppBtn", () => {
this.preventOpenAppBtn();
});
},
/**
* 阻止触发打开App
*/
preventOpenAppBtn() {
utils.waitNode("body").then(($body) => {
log$1.info("阻止.open-app-btn元素触发点击事件");
domUtils.on(
$body,
"click",
".open-app-btn",
function(event) {
utils.preventEvent(event);
},
{
capture: true
}
);
domUtils.on(
$body,
"click",
"#web-player-controller-wrap-el",
function(event) {
utils.preventEvent(event);
},
{
capture: true
}
);
});
}
};
const BilibiliOpusVariable = {
$data: {
dispatchCallBackList: []
},
init() {
Panel.execMenu("bili-opus-variable-autoOpenApp", () => {
this.autoOpenApp();
});
Panel.execMenu("bili-opus-variable-go404", () => {
this.go404();
});
Panel.execMenu("bili-opus-variable-handleFallback", () => {
this.dispatch((vueInstance, fnName) => {
if (typeof fnName === "string" && fnName === "opus/handleFallback" && ![1, 2].includes(vueInstance.fallback.type)) {
log$1.success(`禁止调用handleFallback函数前往404`);
if (typeof vueInstance?.showComment === "boolean" && vueInstance.showComment && typeof vueInstance?.initFullComment === "function") {
vueInstance.initFullComment();
}
return false;
}
});
});
},
/**
* isLimit=false
*
* 作用:自动展开全文
*/
isLimit() {
log$1.info(`等待 观察并覆盖变量isLimit`);
VueUtils.watchVuePropChange(
BilibiliData.className.opus,
(vueInstance) => vueInstance.isLimit,
(vueInstance) => {
vueInstance.isLimit = false;
log$1.success(`观察者:覆盖变量isLimit=false`);
}
);
},
/**
* 覆盖函数autoOpenApp
*/
autoOpenApp() {
VueUtils.waitVuePropToSet(BilibiliData.className.opus, {
msg: "等待 覆盖函数autoOpenApp",
check(vueInstance) {
return typeof vueInstance?.autoOpenApp === "function";
},
set(vueInstance) {
log$1.success(`成功 覆盖函数autoOpenApp`);
vueInstance.autoOpenApp = function() {
log$1.success(`禁止调用autoOpenApp函数`);
};
}
});
},
/**
* 覆盖函数go404
*/
go404() {
VueUtils.waitVuePropToSet(BilibiliData.className.opus, {
msg: "等待 覆盖函数go404",
check(vueInstance) {
return typeof vueInstance?.go404 === "function";
},
set(vueInstance) {
log$1.success(`成功 覆盖函数go404`);
vueInstance.go404 = function() {
log$1.success(`禁止调用go404函数`);
};
}
});
},
/**
* 覆盖对象fallback
*
*/
fallback() {
VueUtils.waitVuePropToSet(BilibiliData.className.opus, {
msg: "等待 覆盖对象fallback",
check(vueInstance) {
return typeof vueInstance?.fallback?.type === "number";
},
set(vueInstance) {
log$1.success(`成功 覆盖对象fallback`);
vueInstance.$watch(
() => vueInstance?.fallback,
() => {
vueInstance.fallback = null;
log$1.success(`覆盖对象fallback`);
},
{
deep: true,
immediate: true
}
);
}
});
},
/**
* 覆盖函数dispatch
*/
dispatch(callback) {
let callbackStr = callback.toString();
for (let index = 0; index < this.$data.dispatchCallBackList.length; index++) {
const fn = this.$data.dispatchCallBackList[index];
if (fn.toString() === callbackStr) {
return;
}
}
log$1.info(`添加dispatch回调判断`);
this.$data.dispatchCallBackList.push(callback);
if (this.$data.dispatchCallBackList.length > 1) {
return;
}
const that = this;
VueUtils.waitVuePropToSet(BilibiliData.className.opus, {
msg: "等待 覆盖函数dispatch",
check(vueInstance) {
return typeof vueInstance?.$store?.dispatch === "function";
},
set(vueInstance) {
log$1.success(`成功 覆盖函数dispatch`);
let originDispatch = vueInstance.$store.dispatch;
vueInstance.$store.dispatch = function(...args) {
let fnName = args[0];
for (let index = 0; index < that.$data.dispatchCallBackList.length; index++) {
const fn = that.$data.dispatchCallBackList[index];
if (typeof fn === "function") {
let result = fn(vueInstance, fnName);
if (typeof result === "boolean" && !result) {
return;
}
}
}
return Reflect.apply(originDispatch, this, args);
};
}
});
}
};
const BilibiliOpus = {
init() {
BilibiliOpusVariable.init();
Panel.execMenuOnce("bili-opus-cover-topicJump", () => {
this.coverTopicJump();
});
Panel.execMenuOnce("bili-opus-automaticallyExpandToReadFullText", () => {
BilibiliOpusVariable.isLimit();
return this.automaticallyExpandToReadFullText();
});
Panel.execMenuOnce("bili-opus-cover-header", () => {
this.coverHeaderJump();
});
},
/**
* 覆盖话题跳转点击事件
*/
coverTopicJump() {
log$1.info("覆盖话题跳转点击事件");
domUtils.on(
document,
"click",
BilibiliData.className.opus + " .launch-app-btn.opus-module-topic",
function(event) {
let $click = event.target;
let vueObj = VueUtils.getVue($click);
if (!vueObj) {
Qmsg.error("获取话题的__vue__失败");
return;
}
let data2 = vueObj?.$props?.data;
let jump_url = data2?.jump_url;
if (utils.isNull(jump_url)) {
Qmsg.error("获取话题的jump_url失败");
return;
}
log$1.info("话题的跳转信息: ", data2);
BilibiliUtils.goToUrl(jump_url);
},
{
capture: true
}
);
},
/**
* 自动展开阅读全文
*/
automaticallyExpandToReadFullText() {
log$1.info("自动展开阅读全文");
let result = [
CommonUtil.addBlockCSS(BilibiliData.className.opus + " .opus-read-more"),
addStyle(
/*css*/
`
${BilibiliData.className.opus} .opus-module-content{
overflow: unset !important;
max-height: unset !important;
}
`
)
];
return result;
},
/**
* 覆盖header点击事件
*/
coverHeaderJump() {
log$1.info("覆盖header点击事件");
domUtils.on(
document,
"click",
BilibiliData.className.opus + " .opus-module-author",
function(event) {
utils.preventEvent(event);
let $click = event.target;
let vueObj = VueUtils.getVue($click);
if (!vueObj) {
Qmsg.error("获取vue属性失败");
return;
}
let mid = vueObj?.data?.mid;
if (!mid) {
Qmsg.error("获取mid失败");
return;
}
BilibiliUtils.goToUrl(BilibiliUrl.getUserSpaceUrl(mid));
},
{
capture: true
}
);
}
};
const BilibiliDynamic = {
init() {
Panel.execMenuOnce("bili-dynamic-cover-topicJump", () => {
this.coverTopicJump();
});
Panel.execMenuOnce("bili-dynamic-cover-atJump", () => {
this.coverAtJump();
});
Panel.execMenuOnce("bili-dynamic-cover-referenceJump", () => {
this.coverReferenceJump();
});
Panel.execMenuOnce("bili-dynamic-cover-header", () => {
this.coverHeaderJump();
});
},
/**
* 覆盖header点击事件
*/
coverHeaderJump() {
log$1.info("覆盖header点击事件");
domUtils.on(
document,
"click",
BilibiliData.className.dynamic + " .launch-app-btn .dyn-header",
function(event) {
utils.preventEvent(event);
let $click = event.target;
let vueObj = VueUtils.getVue($click);
if (!vueObj) {
Qmsg.error("获取vue属性失败");
return;
}
let url = vueObj.url;
if (!url) {
Qmsg.error("获取url失败");
return;
}
BilibiliUtils.goToUrl(url);
},
{
capture: true
}
);
},
/**
* 覆盖话题跳转点击事件
*/
coverTopicJump() {
log$1.info("覆盖话题跳转点击事件");
domUtils.on(
document,
"click",
BilibiliData.className.dynamic + " .launch-app-btn .bili-dyn-topic",
function(event) {
utils.preventEvent(event);
let $click = event.target;
let vueObj = VueUtils.getVue($click);
if (!vueObj) {
Qmsg.error("获取vue属性失败");
return;
}
let data2 = vueObj?.$props?.data;
let jump_url = data2?.jump_url;
if (utils.isNull(jump_url)) {
Qmsg.error("获取jump_url失败");
return;
}
log$1.info("话题的跳转信息: ", data2);
BilibiliUtils.goToUrl(jump_url);
},
{
capture: true
}
);
},
/**
* 覆盖@ 跳转
*/
coverAtJump() {
log$1.info("覆盖@ 跳转");
domUtils.on(
document,
"click",
BilibiliData.className.dynamic + " .at",
function(event) {
utils.preventEvent(event);
let $click = event.target;
let oid = $click.getAttribute("data-oid") || VueUtils.getVue($click)?.$props?.rid;
if (utils.isNull(oid)) {
Qmsg.error("获取data-oid或rid失败");
return;
}
log$1.info("用户的oid: " + oid);
BilibiliUtils.goToUrl(BilibiliUrl.getUserSpaceDynamicUrl(oid));
},
{
capture: true
}
);
},
/**
* 覆盖引用的点击事件
*/
coverReferenceJump() {
log$1.info("覆盖引用的点击事件");
domUtils.on(
document,
"click",
BilibiliData.className.dynamic + " .dyn-content .reference .dyn-orig-author",
function(event) {
utils.preventEvent(event);
let $click = event.target;
let url = $click.getAttribute("data-url");
if (!url) {
Qmsg.error("获取data-url失败");
return;
}
BilibiliUtils.goToUrl(url);
},
{
capture: true
}
);
domUtils.on(
document,
"click",
BilibiliData.className.dynamic + " .dyn-content .reference .dyn-archive",
function(event) {
utils.preventEvent(event);
let $click = event.target;
let vueObj = VueUtils.getVue($click);
if (!vueObj) {
Qmsg.error("获取vue属性失败");
return;
}
let jump_url = vueObj?.data?.jump_url;
if (utils.isNull(jump_url)) {
Qmsg.error("获取jump_url失败");
return;
}
BilibiliUtils.goToUrl(jump_url);
},
{
capture: true
}
);
}
};
const BilibiliHook = {
$isHook: {
windowPlayerAgent: false,
hookWebpackJsonp_openApp: false,
overRideLaunchAppBtn_Vue_openApp: false,
overRideBiliOpenApp: false,
overRideWxTaghandleClick: false
},
$data: {
setTimeout: []
},
/**
* 劫持webpack
* @param webpackName 当前全局变量的webpack名
* @param mainCoreData 需要劫持的webpack的顶部core,例如:(window.webpackJsonp = window.webpackJsonp || []).push([["core:0"],{}])
* @param checkCallBack 如果mainCoreData匹配上,则调用此回调函数
*/
windowWebPack(webpackName = "webpackJsonp", mainCoreData, checkCallBack) {
let originObject = void 0;
OriginPrototype.Object.defineProperty(_unsafeWindow, webpackName, {
get() {
return originObject;
},
set(newValue) {
log$1.success("成功劫持webpack,当前webpack名:" + webpackName);
originObject = newValue;
const originPush = originObject.push;
originObject.push = function(...args) {
let _mainCoreData = args[0][0];
if (mainCoreData == _mainCoreData || Array.isArray(mainCoreData) && Array.isArray(_mainCoreData) && JSON.stringify(mainCoreData) === JSON.stringify(_mainCoreData)) {
Object.keys(args[0][1]).forEach((keyName) => {
let originSwitchFunc = args[0][1][keyName];
args[0][1][keyName] = function(..._args) {
let result = originSwitchFunc.call(this, ..._args);
_args[0] = checkCallBack(_args[0]);
return result;
};
});
}
return originPush.call(this, ...args);
};
}
});
},
/**
* 劫持全局setTimeout
* + 视频页面/video
*
* window.setTimeout
* @param matchStr 需要进行匹配的函数字符串
*/
setTimeout(matchStr) {
this.$data.setTimeout.push(matchStr);
if (this.$data.setTimeout.length > 1) {
log$1.info("window.setTimeout hook新增劫持判断参数:" + matchStr);
return;
}
_unsafeWindow.setTimeout = function(...args) {
let callBackString = args[0].toString();
if (callBackString.match(matchStr)) {
log$1.success("劫持setTimeout的函数", callBackString);
return;
}
return OriginPrototype.setTimeout.apply(this, args);
};
},
/**
* 覆盖元素.launch-app-btn上的openApp
*
* 页面上有很多
*/
overRideLaunchAppBtn_Vue_openApp() {
if (this.$isHook.overRideLaunchAppBtn_Vue_openApp) {
return;
}
this.$isHook.overRideLaunchAppBtn_Vue_openApp = true;
function overrideOpenApp(vueObj) {
if (typeof vueObj.openApp !== "function") {
return;
}
let openAppStr = vueObj.openApp.toString();
if (openAppStr.includes("阻止唤醒App")) {
return;
}
vueObj.openApp = function(...args) {
log$1.success("openApp:阻止唤醒App", args);
};
}
utils.mutationObserver(document, {
config: {
subtree: true,
childList: true,
attributes: true
},
immediate: true,
callback() {
document.querySelectorAll(".launch-app-btn").forEach(($launchAppBtn) => {
let vueObj = VueUtils.getVue($launchAppBtn);
if (!vueObj) {
return;
}
overrideOpenApp(vueObj);
if (vueObj.$children && vueObj.$children.length) {
vueObj.$children.forEach(($child) => {
overrideOpenApp($child);
});
}
});
}
});
},
/**
* 覆盖元素bili-open-app上的opener.open
*
* 页面上有很多
*/
overRideBiliOpenApp() {
if (this.$isHook.overRideBiliOpenApp) {
return;
}
this.$isHook.overRideBiliOpenApp = true;
utils.mutationObserver(document, {
config: {
subtree: true,
childList: true,
attributes: true
},
immediate: true,
callback() {
[
...Array.from($$("bili-open-app")),
...Array.from($$("m-open-app"))
].forEach(($biliOpenApp) => {
if ($biliOpenApp.hasAttribute("data-inject-opener-open")) {
return;
}
let opener = Reflect.get($biliOpenApp, "opener");
if (opener == null) {
return;
}
let originOpen = opener?.open;
if (typeof originOpen === "function") {
Reflect.set(opener, "open", (config) => {
log$1.success(
`拦截bili-open-app.open跳转: ${JSON.stringify(config)}`
);
if (typeof config?.universalLink === "string") {
BilibiliUtils.goToUrl(config.universalLink);
}
});
$biliOpenApp.setAttribute("data-inject-opener-open", "true");
}
});
}
});
},
/**
* 覆盖页面上的className为wx-tag的元素的点击事件
*/
overRideWxTaghandleClick() {
if (this.$isHook.overRideWxTaghandleClick) {
return;
}
this.$isHook.overRideWxTaghandleClick = true;
utils.mutationObserver(document, {
config: {
subtree: true,
childList: true,
attributes: true
},
immediate: true,
callback() {
[...Array.from($$(".wx-tag"))].forEach(($el) => {
if ($el.hasAttribute("data-inject-vueins-handle-click")) {
return;
}
let vueIns = VueUtils.getVue($el);
if (vueIns) {
if (typeof vueIns?.handleClick === "function") {
vueIns.handleClick = function() {
if (typeof vueIns["goToVideo"] === "function") {
vueIns.goToVideo();
} else {
Qmsg.error(".wx-tag不存在goToVideo函数", {
consoleLogContent: true
});
}
};
$el.setAttribute("data-inject-vueins-handle-click", "true");
}
if (Array.isArray(vueIns?.$children) && vueIns.$children.length && typeof vueIns.$children[0].handleClick === "function") {
vueIns.$children[0].handleClick = vueIns.handleClick;
}
}
});
}
});
}
};
const BilibiliRecommendCSS = '#app .m-head .m-recommend-view {\r\n display: none;\r\n}\r\n\r\n#app\r\n .m-head\r\n .suspension\r\n .channel-menu:has(.recommend-tag.is-avtive)\r\n .v-switcher__header__anchor {\r\n display: none !important;\r\n}\r\n#app\r\n .m-head\r\n .suspension\r\n .channel-menu:has(.recommend-tag.is-avtive)\r\n a.v-switcher__header__tabs__item {\r\n color: #505050 !important;\r\n}\r\n#app\r\n .m-head\r\n .suspension\r\n .channel-menu:has(.recommend-tag.is-avtive)\r\n a.recommend-tag {\r\n color: var(--bili-color) !important;\r\n}\r\n#app\r\n .m-head\r\n .suspension\r\n .channel-menu:has(.recommend-tag.is-avtive)\r\n a.recommend-tag\r\n span:after {\r\n content: " ";\r\n position: relative;\r\n background: var(--bili-color);\r\n width: 30.4375px;\r\n height: 0.53333vmin;\r\n display: block;\r\n bottom: 3px;\r\n}\r\n\r\n#app .m-head:has(.recommend-tag.is-avtive) .suspension + div {\r\n display: none;\r\n}\r\n#app .m-head:has(.recommend-tag.is-avtive) .m-recommend-view {\r\n display: unset;\r\n}\r\n\r\n#app .m-head .m-recommend-view {\r\n background-color: #f0f1f3;\r\n}\r\n#app .m-head .m-recommend-view .list-view .video-list-box .video-list {\r\n padding: 0 1.33333vmin;\r\n margin-bottom: 5.33333vmin;\r\n}\r\n#app\r\n .m-head\r\n .m-recommend-view\r\n .list-view\r\n .video-list-box\r\n .video-list\r\n .card-box {\r\n display: -webkit-box;\r\n display: -ms-flexbox;\r\n display: flex;\r\n -ms-flex-wrap: wrap;\r\n flex-wrap: wrap;\r\n}\r\n#app\r\n .m-head\r\n .m-recommend-view\r\n .list-view\r\n .video-list-box\r\n .video-list\r\n .card-box\r\n .v-card\r\n .card {\r\n position: relative;\r\n}\r\n#app\r\n .m-head\r\n .m-recommend-view\r\n .list-view\r\n .video-list-box\r\n .video-list\r\n .card-box\r\n .v-card\r\n .card\r\n .bfs-img-wrap {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n overflow: hidden;\r\n}\r\n#app\r\n .m-head\r\n .m-recommend-view\r\n .list-view\r\n .video-list-box\r\n .video-list\r\n .card-box\r\n .v-card\r\n .card\r\n .bfs-img-wrap\r\n .bfs-img.b-img {\r\n position: relative;\r\n width: 100%;\r\n height: 100%;\r\n overflow: hidden;\r\n background: transparent;\r\n}\r\n#app\r\n .m-head\r\n .m-recommend-view\r\n .list-view\r\n .video-list-box\r\n .video-list\r\n .card-box\r\n .v-card\r\n .card\r\n .bfs-img-wrap\r\n .bfs-img.b-img\r\n picture.b-img__inner {\r\n display: block;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n#app\r\n .m-head\r\n .m-recommend-view\r\n .list-view\r\n .video-list-box\r\n .video-list\r\n .card-box\r\n .v-card\r\n .card\r\n .bfs-img-wrap\r\n .bfs-img.b-img\r\n picture.b-img__inner\r\n img {\r\n width: 100%;\r\n height: 100%;\r\n -o-object-fit: cover;\r\n object-fit: cover;\r\n}\r\n#app\r\n .m-head\r\n .m-recommend-view\r\n .list-view\r\n .video-list-box\r\n .video-list\r\n .card-box\r\n .v-card\r\n .card\r\n .count {\r\n position: absolute;\r\n bottom: 0;\r\n left: 0;\r\n width: 100%;\r\n font-size: 3.2vmin;\r\n padding: 1.33333vmin 1.6vmin;\r\n display: -webkit-box;\r\n display: -ms-flexbox;\r\n display: flex;\r\n -webkit-box-pack: justify;\r\n -ms-flex-pack: justify;\r\n justify-content: space-between;\r\n color: #fff;\r\n background: linear-gradient(0deg, rgba(0, 0, 0, 0.85), transparent);\r\n}\r\n#app\r\n .m-head\r\n .m-recommend-view\r\n .list-view\r\n .video-list-box\r\n .video-list\r\n .card-box\r\n .v-card\r\n .title {\r\n font-size: 3.2vmin;\r\n color: #212121;\r\n margin-top: 1.6vmin;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n display: -webkit-box;\r\n -webkit-line-clamp: 2;\r\n -webkit-box-orient: vertical;\r\n}\r\n#app\r\n .m-head\r\n .m-recommend-view\r\n .list-view\r\n .video-list-box\r\n .video-list\r\n .card-box\r\n .v-card\r\n .gm-up-info\r\n .gm-up-name\r\n .gm-picture-text {\r\n padding: 1px 4px;\r\n border: 1px solid var(--bili-color);\r\n color: var(--bili-color);\r\n border-radius: 2px;\r\n margin-right: 4px;\r\n font-size: 2vmin;\r\n}\r\n\r\n#app\r\n .m-head\r\n .m-recommend-view\r\n .list-view\r\n .video-list-box\r\n .video-list\r\n .card-box\r\n .v-card\r\n .count\r\n > span {\r\n display: flex;\r\n align-items: center;\r\n gap: 1.33333vmin;\r\n}\r\n';
var XOR_CODE = 23442827791579n;
var MAX_AID = 1n << 51n;
var BASE = 58n;
var data = "FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf";
function av2bv(aid) {
const bytes = ["B", "V", "1", "0", "0", "0", "0", "0", "0", "0", "0", "0"];
let bvIndex = bytes.length - 1;
let tmp = (MAX_AID | BigInt(aid)) ^ XOR_CODE;
while (tmp > 0) {
bytes[bvIndex] = data[Number(tmp % BigInt(BASE))];
tmp = tmp / BASE;
bvIndex -= 1;
}
[bytes[3], bytes[9]] = [bytes[9], bytes[3]];
[bytes[4], bytes[7]] = [bytes[7], bytes[4]];
return bytes.join("");
}
const fixCover = (url) => {
if (url.startsWith("http://")) {
url = url.replace(/^http/, "https");
}
return url;
};
const BilibiliRecommend = {
$flag: {
/** 是否已初始化CSS */
isInitCSS: false,
/** 是否正在加载下一页 */
isLoadingNextPage: false
},
$data: {
/** 监听滚动的观察器 */
intersectionObserver: null,
/** 加载推荐视频次数 */
loadNums: 0
},
$ele: {
$listView: null,
$videoListBox: null,
$videoList: null,
$cardBox: null,
$listViewShim: null
},
$cardGoto: {
av: "av",
picture: "picture"
},
init() {
this.setCSS();
domUtils.ready(() => {
this.addRecommendTag();
});
},
setCSS() {
if (this.$flag.isInitCSS) {
return;
}
this.$flag.isInitCSS = true;
addStyle(BilibiliRecommendCSS);
},
/**
* 重置状态
*/
reset() {
log$1.info("重置状态");
this.$flag.isLoadingNextPage = false;
this.removeScrollEvent();
Object.keys(this.$ele).forEach((key) => {
this.$ele[key] = null;
});
},
/**
* 添加推荐标签
*/
addRecommendTag() {
if (document.querySelector(".channel-menu a.recommend-tag")) {
return;
}
let $vSwitcher = document.querySelector(
".channel-menu .v-switcher"
);
if (!$vSwitcher) {
log$1.error("添加推荐标签失败,原因:.channel-menu .v-switcher不存在");
Qmsg.error("添加推荐标签失败,原因:.channel-menu .v-switcher不存在");
return;
}
let $recommendTag = domUtils.createElement(
"a",
{
className: "v-switcher__header__tabs__item recommend-tag",
innerHTML: "推荐 "
},
{
href: "javascript:;"
}
);
let $recommendView = domUtils.createElement("div", {
className: "m-recommend-view",
innerHTML: (
/*html*/
`
`
)
});
this.$ele.$listView = $recommendView.querySelector(
".list-view"
);
this.$ele.$videoListBox = $recommendView.querySelector(
".video-list-box"
);
this.$ele.$videoList = $recommendView.querySelector(
".video-list"
);
this.$ele.$cardBox = $recommendView.querySelector(
".card-box"
);
this.$ele.$listViewShim = $recommendView.querySelector(
".list-view__shim"
);
this.$ele.$listViewShim.style.cssText = `z-index:-1;user-select:none;pointer-events:none;background:transparent;left:0;bottom:0;width:100%;height:200px;`;
let $myHead = document.querySelector("#app .m-head");
if ($myHead) {
$myHead.appendChild($recommendView);
}
domUtils.on($recommendTag, "click", (event) => {
utils.preventEvent(event);
$recommendTag.classList.add("is-avtive");
this.recommendClickEvent();
});
domUtils.on(
$vSwitcher,
"click",
() => {
$recommendTag.classList.remove("is-avtive");
},
{
capture: true
}
);
domUtils.on(this.$ele.$cardBox, "click", ".v-card", (event) => {
utils.preventEvent(event);
let $click = event.target;
window.open($click.href, "_blank");
});
domUtils.before($vSwitcher, $recommendTag);
this.setScrollEvent();
if (window.location.hash === "#/recommend/") {
log$1.info("当前hash为推荐视频,出动触发");
$recommendTag.click();
}
},
/**
* 推荐标签的点击事件(切换router)
*/
async recommendClickEvent() {
BilibiliUtils.goToUrl("#/recommend/", true);
},
/**
* 设置滚动观察事件
*/
setScrollEvent() {
log$1.success("推荐视频监听滚动: IntersectionObserver");
this.$data.intersectionObserver = new IntersectionObserver(
async (entries) => {
if (!this.$flag.isLoadingNextPage && entries[0].isIntersecting) {
this.$flag.isLoadingNextPage = true;
let flag = await this.scrollEvent();
this.$flag.isLoadingNextPage = false;
if (this.$data.loadNums <= 1 && flag) {
domUtils.hide(this.$ele.$listViewShim, false);
await utils.sleep(500);
domUtils.show(this.$ele.$listViewShim, false);
} else {
domUtils.show(this.$ele.$listViewShim, false);
}
}
},
{ threshold: 0, rootMargin: "0px 0px 0px 0px" }
);
this.$data.intersectionObserver.observe(this.$ele.$listViewShim);
},
/**
* 移除滚动观察事件
*/
removeScrollEvent() {
this.$data.intersectionObserver?.disconnect();
this.$data.intersectionObserver = null;
},
/**
* 滚动事件
*/
async scrollEvent() {
let videoInfo = await this.getRecommendVideoInfo();
if (!videoInfo) {
return false;
}
log$1.success("获取推荐视频信息", videoInfo);
let $fragment = document.createDocumentFragment();
let allowLoadPictureCard = Panel.getValue(
"bili-head-recommend-push-graphic"
);
videoInfo.forEach((videoInfoItem) => {
let $ele = null;
if (videoInfoItem.goto === this.$cardGoto.av) {
$ele = this.getRecommendItemAVElement(
videoInfoItem
);
} else if (videoInfoItem.goto === this.$cardGoto.picture) {
if (allowLoadPictureCard) {
$ele = this.getRecommendItemPictureElement(
videoInfoItem
);
} else {
return;
}
} else {
log$1.error("该goto暂未适配", videoInfoItem);
return;
}
$fragment.appendChild($ele);
});
this.$ele.$cardBox.appendChild($fragment);
this.$data.loadNums++;
return true;
},
/**
* 获取推荐视频信息
*/
async getRecommendVideoInfo() {
let getData = {
appkey: AppKeyInfo.ios.appkey,
access_key: BilibiliQrCodeLogin.getAccessTokenInfo()?.access_token || ""
};
let Api = "https://app.bilibili.com/x/v2/feed/index";
let getResp = await httpx.get(
Api + "?" + utils.toSearchParamsStr(getData),
{
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
}
);
if (!getResp.status) {
return;
}
let data2 = utils.toJSON(
getResp.data.responseText
);
if (!BilibiliApiResponseCheck.isWebApiSuccess(data2)) {
Qmsg.error(data2["message"]);
return;
}
return data2.data.items;
},
/**
* 获取推荐视频的每一个元素 图文
* + picture
*/
getRecommendItemPictureElement(data2) {
let goto = data2.goto;
let param = data2.param;
let url = "/opus/" + param;
let upName = data2.args.up_name;
let title = data2.title;
let cover = fixCover(data2.cover);
let likeCount = data2.cover_left_text_1;
let $vCard = domUtils.createElement(
"a",
{
className: "v-card",
href: url,
innerHTML: `
${title}
`
},
{
"data-param": param,
"data-title": title,
"data-goto": goto
}
);
$vCard["data-picture"] = data2;
return $vCard;
},
/**
* 获取推荐视频的每一个元素
* + av
*/
getRecommendItemAVElement(data2) {
let goto = data2.goto;
let aid = data2?.player_args?.aid || data2.args.aid;
let bvid = av2bv(aid);
let url = "/video/" + bvid;
let upName = data2.args.up_name;
let title = data2.title;
let cover = fixCover(data2.cover);
let playCount = data2.cover_left_text_1;
let commentCount = data2.cover_left_text_2;
let videoTime = data2.cover_right_text;
let $vCard = domUtils.createElement(
"a",
{
className: "v-card",
href: url,
innerHTML: (
/*html*/
`
${playCount}
${commentCount}
${videoTime}
${title}
`
)
},
{
"data-aid": aid,
"data-title": title,
"data-goto": goto
}
);
$vCard["data-video"] = data2;
return $vCard;
}
};
const BilibiliHead = {
$flag: {
isInit_reconfigurationTinyAppSettingButton: false,
isInit_beautifyTopNavBar_css: false
},
init() {
Panel.execMenuOnce(
"bili-head-supplementaryVideoStreamingInformation",
() => {
this.addVideoListUPInfo();
}
);
Panel.execMenu("bili-head-recommend-enable", () => {
BilibiliRecommend.init();
});
},
/**
* 添加视频列表UP主信息
*/
addVideoListUPInfo() {
log$1.info("添加视频列表UP主信息");
addStyle(
/*css*/
`
${BilibiliData.className.head} .video-list .card-box .gm-up-info {
display: flex;
justify-content: space-between;
align-items: center;
margin: var(--pd-width);
}
${BilibiliData.className.head} .video-list .card-box .gm-up-info .gm-up-name {
display: flex;
align-items: center;
font-size: 3vmin;
color: #999A9E;
}
${BilibiliData.className.head} .video-list .card-box .gm-up-info .gm-up-name svg {
margin-right: calc(var(--pd-width) / 2);
width: 3vmin;
height: 3vmin;
}
${BilibiliData.className.head} .gm-video-duration{
margin: 0 auto;
}
`
);
utils.waitNode(
BilibiliData.className.head + " .video-list .card-box"
).then(() => {
let lockFunc = new utils.LockFunction(() => {
document.querySelectorAll(
BilibiliData.className.head + " .video-list .card-box .v-card"
).forEach(($vcard) => {
let vueObj = VueUtils.getVue($vcard);
let upName = vueObj?.info?.author?.name || vueObj?.info?.owner?.name;
let duration = vueObj?.info?.duration;
if (upName && !$vcard.querySelector(".gm-up-info")) {
let $upInfo = document.createElement("div");
$upInfo.innerHTML = /*html*/
`
`;
$upInfo.className = "gm-up-info";
$vcard.appendChild($upInfo);
}
if (duration) {
let $count = $vcard.querySelector(".count");
if ($count && !$count.querySelector(".gm-video-duration")) {
let showDuration = typeof duration === "string" ? duration : BilibiliUtils.parseDuration(duration);
let $duration = document.createElement("span");
$duration.className = "gm-video-duration";
$duration.innerHTML = showDuration;
$count.appendChild($duration);
}
}
});
}, 25);
utils.mutationObserver(document.body, {
config: {
subtree: true,
childList: true,
attributes: true
},
callback() {
lockFunc.run();
}
});
});
},
/**
* 重构tinyApp右上角的设置按钮图标,改为用户头像什么的
*/
async reconfigurationTinyAppSettingButton() {
log$1.info(`重构tinyApp右上角的设置按钮图标`);
if (!this.$flag.isInit_reconfigurationTinyAppSettingButton) {
this.$flag.isInit_reconfigurationTinyAppSettingButton = true;
addStyle(
/*css*/
`
.nav-bar .right{
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.gm-face{
width: 6.4vmin;
height: 6.4vmin;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
margin-right: 3.2vmin;
border-radius: 3.2vmin;
overflow: hidden;
}
.gm-face-avatar{
width: 100%;
height: 100%;
overflow: hidden;
}
.gm-face-avatar img{
width: 100%;
height: 100%;
-o-object-fit: cover;
object-fit: cover;
}
`
);
}
let $iconConfig = await utils.waitNode(".nav-bar .icon-config", 1e4);
if (!$iconConfig) {
log$1.error("未找到设置按钮图标,无法重构");
return;
}
$iconConfig.outerHTML = /*html*/
`
`;
let isLogin = false;
let uid = null;
let $gmFace = document.querySelector(".gm-face");
let $img = $gmFace.querySelector("img");
VueUtils.waitVuePropToSet("#app", [
{
check(vueIns) {
return typeof vueIns?.$store?.state?.common?.userInfo?.isLogin === "boolean";
},
set(vueIns) {
let userInfo = vueIns?.$store?.state?.common?.userInfo;
isLogin = userInfo?.isLogin;
if (isLogin) {
uid = userInfo?.mid;
if (uid == null) {
log$1.warn(`当前是脚本设置的isLogin但其实未登录账号`);
isLogin = false;
return;
}
userInfo?.uname;
$img.src = userInfo?.face || $img.src;
} else {
log$1.warn(`经检测,Bilibili尚未登录账号`);
}
}
}
]);
domUtils.on($gmFace, "click", (event) => {
utils.preventEvent(event);
if (isLogin) {
if (uid != null) {
let url = BilibiliUrl.getUserSpaceUrl(uid);
BilibiliUtils.goToUrl(url, false);
} else {
Qmsg.error("获取用户id失败");
}
} else {
BilibiliUtils.goToLogin(window.location.href);
}
});
},
/**
* 美化顶部navbar
*/
beautifyTopNavBar() {
log$1.info(`美化顶部navbar`);
if (!this.$flag.isInit_beautifyTopNavBar_css) {
this.$flag.isInit_beautifyTopNavBar_css = true;
addStyle(
/*css*/
`
/* 隐藏logo */
.${BilibiliData.className.head} .m-navbar .logo,
/* 隐藏原有的搜索图标 */
.${BilibiliData.className.head} .m-navbar .icon-search{
display: none !important;
}
/* 设置右侧的宽度撑开、逆反 */
.${BilibiliData.className.head} .m-navbar .right{
width: 100%;
display: flex;
flex-direction: row-reverse;
justify-content: flex-end;
}
/* 头像 */
.${BilibiliData.className.head} .m-navbar .gm-face{
flex: 0 auto;
margin-top: 1.86667vmin;
}
/* 新的输入框 */
.${BilibiliData.className.head} .m-navbar .gm-input-area{
flex: 1;
margin-top: 1.86667vmin;
height: 8vmin;
line-height: 8vmin;
padding: 0 3.2vmin;
background: #f4f4f4;
border-radius: 4.53333vmin;
display: flex;
}
/* 输入框前面的搜索图标 */
.${BilibiliData.className.head} .m-navbar .gm-input-area .ic_search_tab{
color: #a0a0a0;
vertical-align: middle;
font-size: 4.33333vmin;
}
/* 输入框内容 */
.${BilibiliData.className.head} .m-navbar .gm-input-area input[type="search"]{
font-size: 3.46667vmin;
color: #505050;
border: none;
background: transparent;
width: 61.33333vmin;
user-select: none !important;!i;!;
padding-left: 2.122vmin;
pointer-events: none;
}
/* 调整首页顶部搜索框的样式 */
.${BilibiliData.className.head} .m-navbar .right .search {
border: 1px solid #ccc;
width: 100% !important;
height: auto !important;
border-radius: 1rem;
display: flex;
align-items: center;
padding: 2px 6px;
}
`
);
}
utils.waitNode(".m-head .m-navbar .icon-search", 1e4).then(async ($iconSearch) => {
if (!$iconSearch) {
return;
}
if ($iconSearch.parentElement.querySelector(".gm-input-area")) {
return;
}
let $inputAreaContainer = domUtils.createElement("div", {
className: "gm-input-area",
innerHTML: (
/*html*/
`
`
)
});
let $input = $inputAreaContainer.querySelector("input");
domUtils.on($inputAreaContainer, "click", (event) => {
utils.preventEvent(event);
BilibiliUtils.goToUrl("/search", true);
});
domUtils.after($iconSearch, $inputAreaContainer);
let hotWordInfo = await BilibiliSearchApi.getSearchInputPlaceholder();
if (hotWordInfo != null) {
log$1.info(`热点信息:`, hotWordInfo);
$input.placeholder = hotWordInfo.show_name || hotWordInfo.name;
}
});
}
};
const BilibiliReadMobile = {
init() {
this.removeAds();
Panel.onceExec("bili-pc-read-mobile-autoExpand", () => {
return this.autoExpand();
});
},
removeAds() {
CommonUtil.addBlockCSS(
/* 底部的打开客户端阅读 */
"body>.h5-download-bar"
);
},
/**
* 自动展开
*/
autoExpand() {
log$1.info("自动展开");
return [
addStyle(
/*css*/
`
${BilibiliPCData.className.read.mobile} .limit{
overflow: unset !important;
max-height: unset !important;
}`
),
// 屏蔽 【展开阅读全文】
CommonUtil.addBlockCSS(
BilibiliPCData.className.read.mobile + " .read-more"
)
];
}
};
const BilibiliSpace = {
init() {
Panel.execMenuOnce("bili-space-repairRealJump", () => {
this.repairRealJump();
});
Panel.execMenuOnce("bili-space-coverDynamicStateCardVideo", () => {
this.coverDynamicStateCardVideo();
});
},
/**
* 修复视频|动态的正确跳转
*/
repairRealJump() {
let lockFn = new utils.LockFunction(() => {
$$(BilibiliData.className.space + " .wx-tag.open-app-wrapper").forEach(
($el) => {
let vueIns = VueUtils.getVue($el);
if (typeof vueIns?.disabled === "boolean") {
vueIns.disabled = false;
}
}
);
});
utils.mutationObserver(document, {
config: {
subtree: true,
childList: true
},
immediate: true,
callback: () => {
lockFn.run();
}
});
return;
},
/**
* 覆盖动态视频的点击事件
*/
coverDynamicStateCardVideo() {
log$1.info(`覆盖动态视频的点击事件`);
domUtils.on(
document,
"click",
".card-content .main .wings",
(event) => {
let $wings = event.target;
let $card = $wings.closest(".card");
if (!$card) {
Qmsg.error("未找到对应的.card元素");
return;
}
let vueIns = VueUtils.getVue($card);
if (!vueIns) {
Qmsg.error("未找到对应的vue实例");
return;
}
let url = vueIns?.shareData?.default?.url;
if (!url) {
Qmsg.error("未找到对应的url");
return;
}
BilibiliUtils.goToUrl(url);
},
{
capture: true
}
);
}
};
const BilibiliVueProp = {
init() {
Panel.execMenu("bili-noCallApp", () => {
this.noCallApp();
});
Panel.execMenu("bili-setLogin", () => {
this.setLogin();
});
Panel.execMenu("bili-setIsClient", () => {
this.setIsClient();
});
},
/**
* 禁止调用app
*/
noCallApp() {
VueUtils.waitVuePropToSet("#app", [
{
msg: "设置参数 $store.state.common.noCallApp",
check(vueIns) {
return typeof vueIns?.$store?.state?.common?.noCallApp === "boolean";
},
set(vueIns) {
log$1.success("成功设置参数 $store.state.common.noCallApp=true");
vueIns.$store.state.common.noCallApp = true;
}
}
]);
},
/**
* 设置登录
*
* + $store.state.common.noCallApp
* + $store.state.common.userInfo.isLogin
* + $store.state.loginInfo.isLogin
*/
setLogin() {
let GM_Cookie = new utils.GM_Cookie();
let cookie_DedeUserID = GM_Cookie.get("DedeUserID");
if (cookie_DedeUserID != null) {
log$1.info("Cookie DedeUserID已存在:", cookie_DedeUserID.value);
} else {
GM_Cookie.set(
{
name: "DedeUserID",
value: "2333"
},
(error) => {
if (error) {
log$1.error(error);
} else {
log$1.success("Cookie成功设置DedeUserID=>2333");
}
}
);
}
VueUtils.waitVuePropToSet("#app", [
{
msg: "设置参数 $store.state.common.userInfo.isLogin",
check(vueObj) {
return typeof vueObj?.$store?.state?.common?.userInfo?.isLogin === "boolean";
},
set(vueObj) {
log$1.success("成功设置参数 $store.state.common.userInfo.isLogin=true");
vueObj.$store.state.common.userInfo.isLogin = true;
}
},
{
msg: "设置参数 $store.state.loginInfo.isLogin",
check(vueObj) {
return typeof vueObj?.$store?.state?.loginInfo?.isLogin === "boolean";
},
set(vueObj) {
log$1.success("成功设置参数 $store.state.loginInfo.isLogin=true");
vueObj.$store.state.loginInfo.isLogin = true;
}
}
]);
},
/**
* 设置为客户端(不确定是否有用)
*
* + $store.state.video.isClient
* + $store.state.opus.isClient
* + $store.state.playlist.isClient
* + $store.state.ver.bili
* + $store.state.ver.biliVer 2333333
*/
setIsClient() {
VueUtils.waitVuePropToSet("#app", [
{
msg: "设置参数 $store.state.video.isClient",
check(vueIns) {
return typeof typeof vueIns?.$store?.state?.video?.isClient === "boolean";
},
set(vueIns) {
log$1.success("成功设置参数 $store.state.video.isClient=true");
vueIns.$store.state.video.isClient = true;
}
},
{
msg: "设置参数 $store.state.opus.isClient=true",
check(vueIns) {
return typeof vueIns?.$store?.state?.opus?.isClient === "boolean";
},
set(vueIns) {
log$1.success("成功设置参数 $store.state.opus.isClient");
vueIns.$store.state.opus.isClient = true;
}
},
{
msg: "设置参数 $store.state.playlist.isClient",
check(vueIns) {
return typeof vueIns?.$store?.state?.playlist?.isClient === "boolean";
},
set(vueIns) {
log$1.success("成功设置参数 $store.state.playlist.isClient=true");
vueIns.$store.state.playlist.isClient = true;
}
},
{
msg: "设置参数 $store.state.ver.bili",
check(vueIns) {
return typeof vueIns?.$store?.state?.ver?.bili === "boolean";
},
set(vueIns) {
log$1.success("成功设置参数 $store.state.ver.bili=true");
vueIns.$store.state.ver.bili = true;
}
},
{
msg: "设置参数 $store.state.ver.biliVer",
check(vueIns) {
return typeof vueIns?.$store?.state?.ver?.biliVer === "number";
},
set(vueIns) {
log$1.success("成功设置参数 $store.state.ver.biliVer=2333333");
vueIns.$store.state.ver.biliVer = 2333333;
}
}
]);
},
/**
* 设置为微应用(可以看评论且视频稿件变大)
*
* + __vue__.$store.state.common.tinyApp `true`
*/
setTinyApp() {
VueUtils.waitVuePropToSet("#app", [
{
msg: "设置参数 $store.state.common.tinyApp",
check(vueIns) {
return typeof vueIns?.$store?.state?.common?.tinyApp === "boolean";
},
set(vueIns) {
vueIns.$store.state.common.tinyApp = true;
log$1.success("成功设置参数 $store.state.common.tinyApp=true");
Panel.onceExec("bili-tinyApp-init-css", () => {
addStyle(
/*css*/
`
.tiny-app .reply-input,.tiny-app .reply-item .info .name .right,.tiny-app .reply-item .info .toolbar,.tiny-app .sub-reply-input {
display: block;
}
`
);
});
}
}
]);
}
};
const PanelComponents = {
$data: {
__storeApiFn: null,
get storeApiValue() {
if (!this.__storeApiFn) {
this.__storeApiFn = new Utils.Dictionary();
}
return this.__storeApiFn;
}
},
/**
* 获取自定义的存储接口
* @param type 组件类型
*/
getStorageApi(type) {
if (!this.hasStorageApi(type)) {
return;
}
return this.$data.storeApiValue.get(type);
},
/**
* 判断是否存在自定义的存储接口
* @param type 组件类型
*/
hasStorageApi(type) {
return this.$data.storeApiValue.has(type);
},
/**
* 设置自定义的存储接口
* @param type 组件类型
* @param storageApiValue 存储接口
*/
setStorageApi(type, storageApiValue) {
this.$data.storeApiValue.set(type, storageApiValue);
},
/**
* 初始化组件的存储接口属性
*
* @param type 组件类型
* @param config 组件配置,必须包含prop属性
* @param storageApiValue 存储接口
*/
initComponentsStorageApi(type, config, storageApiValue) {
let propsStorageApi;
if (this.hasStorageApi(type)) {
propsStorageApi = this.getStorageApi(type);
} else {
propsStorageApi = storageApiValue;
}
this.setComponentsStorageApiProperty(config, propsStorageApi);
},
/**
* 设置组件的存储接口属性
* @param config 组件配置,必须包含prop属性
* @param storageApiValue 存储接口
*/
setComponentsStorageApiProperty(config, storageApiValue) {
Reflect.set(config.props, PROPS_STORAGE_API, storageApiValue);
}
};
const UIInput = function(text, key, defaultValue, description, changeCallback, placeholder = "", isNumber, isPassword, afterAddToUListCallBack) {
let result = {
text,
type: "input",
isNumber: Boolean(isNumber),
isPassword: Boolean(isPassword),
attributes: {},
props: {},
description,
afterAddToUListCallBack,
getValue() {
let storageApiValue = this.props[PROPS_STORAGE_API];
return storageApiValue.get(key, defaultValue);
},
callback(event, value, valueAsNumber) {
if (typeof changeCallback === "function") {
let result2 = changeCallback(event, value, valueAsNumber);
if (result2) {
return;
}
}
let storageApiValue = this.props[PROPS_STORAGE_API];
storageApiValue.set(key, value);
},
placeholder
};
Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
PanelComponents.initComponentsStorageApi(
"input",
result,
{
get(key2, defaultValue2) {
return Panel.getValue(key2, defaultValue2);
},
set(key2, value) {
Panel.setValue(key2, value);
}
}
);
return result;
};
const UISwitch = function(text, key, defaultValue, clickCallback, description, afterAddToUListCallBack) {
let result = {
text,
type: "switch",
description,
attributes: {},
props: {},
getValue() {
let storageApiValue = this.props[PROPS_STORAGE_API];
return Boolean(storageApiValue.get(key, defaultValue));
},
callback(event, __value) {
let value = Boolean(__value);
log$1.success(`${value ? "开启" : "关闭"} ${text}`);
let storageApiValue = this.props[PROPS_STORAGE_API];
storageApiValue.set(key, value);
},
afterAddToUListCallBack
};
Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
PanelComponents.initComponentsStorageApi(
"switch",
result,
{
get(key2, defaultValue2) {
return Panel.getValue(key2, defaultValue2);
},
set(key2, value) {
Panel.setValue(key2, value);
}
}
);
return result;
};
const UITextArea = function(text, key, defaultValue, description, changeCallback, placeholder = "", disabled) {
let result = {
text,
type: "textarea",
attributes: {},
props: {},
description,
placeholder,
disabled,
getValue() {
let storageApiValue = this.props[PROPS_STORAGE_API];
let value = storageApiValue.get(key, defaultValue);
if (Array.isArray(value)) {
return value.join("\n");
}
return value;
},
callback(event, value) {
let storageApiValue = this.props[PROPS_STORAGE_API];
storageApiValue.set(key, value);
}
};
Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
PanelComponents.initComponentsStorageApi(
"switch",
result,
{
get(key2, defaultValue2) {
return Panel.getValue(key2, defaultValue2);
},
set(key2, value) {
Panel.setValue(key2, value);
}
}
);
return result;
};
class RuleEditView {
option;
constructor(option) {
this.option = option;
}
/**
* 显示视图
*/
async showView() {
let $dialog = __pops.confirm({
title: {
text: this.option.title,
position: "center"
},
content: {
text: (
/*html*/
`
`
),
html: true
},
btn: utils.assign(
{
ok: {
callback: async () => {
await submitSaveOption();
}
}
},
this.option.btn || {},
true
),
drag: true,
mask: {
enable: true
},
style: (
/*css*/
`
${__pops.config.cssText.panelCSS}
.rule-form-container {
}
.rule-form-container li{
display: flex;
align-items: center;
justify-content: space-between;
padding: 5px 20px;
gap: 10px;
}
.rule-form-ulist-dynamic{
--button-margin-top: 0px;
--button-margin-right: 0px;
--button-margin-bottom: 0px;
--button-margin-left: 0px;
display: flex;
flex-direction: column;
align-items: flex-start;
padding: 5px 0px 5px 20px;
}
.rule-form-ulist-dynamic__inner{
width: 100%;
}
.rule-form-ulist-dynamic__inner-container{
display: flex;
align-items: center;
}
.dynamic-forms{
width: 100%;
}
.pops-panel-item-left-main-text{
max-width: 150px;
}
.pops-panel-item-right-text{
padding-left: 30px;
}
.pops-panel-item-right-text,
.pops-panel-item-right-main-text{
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.pops-panel-item-left-desc-text{
line-height: normal;
margin-top: 6px;
font-size: 0.8em;
color: rgb(108, 108, 108);
}
${this.option?.style ?? ""}
`
),
width: typeof this.option.width === "function" ? this.option.width() : window.innerWidth > 500 ? "500px" : "88vw",
height: typeof this.option.height === "function" ? this.option.height() : window.innerHeight > 500 ? "500px" : "80vh"
});
let $form = $dialog.$shadowRoot.querySelector(
".rule-form-container"
);
$dialog.$shadowRoot.querySelector(
"input[type=submit]"
);
let $ulist = $dialog.$shadowRoot.querySelector(".rule-form-ulist");
let view = await this.option.getView(await this.option.data());
$ulist.appendChild(view);
const submitSaveOption = async () => {
let result = await this.option.onsubmit($form, await this.option.data());
if (!result.success) {
return;
}
$dialog.close();
await this.option.dialogCloseCallBack(true);
};
}
}
class RuleFilterView {
option;
constructor(option) {
this.option = option;
}
showView() {
let $alert = __pops.alert({
title: {
text: this.option.title,
position: "center"
},
content: {
text: (
/*html*/
`
`
)
},
btn: {
ok: {
text: "关闭",
type: "default"
}
},
drag: true,
mask: {
enable: true
},
width: window.innerWidth > 500 ? "350px" : "80vw",
height: window.innerHeight > 500 ? "300px" : "70vh",
style: (
/*css*/
`
.filter-container{
height: 100%;
display: flex;
flex-direction: column;
gap: 20px;
}
.filter-container button{
text-wrap: wrap;
padding: 8px;
height: auto;
text-align: left;
}
`
)
});
let $filterContainer = $alert.$shadowRoot.querySelector(".filter-container");
let $fragment = document.createDocumentFragment();
this.option.filterOption.forEach((filterOption) => {
let $button = document.createElement("button");
$button.innerText = filterOption.name;
let execFilterAndCloseDialog = async () => {
let allRuleInfo = await this.option.getAllRuleInfo();
allRuleInfo.forEach(async (ruleInfo) => {
let filterResult = await filterOption.filterCallBack(ruleInfo.data);
if (!filterResult) {
domUtils.hide(ruleInfo.$el, false);
} else {
domUtils.show(ruleInfo.$el, false);
}
});
if (typeof this.option.execFilterCallBack === "function") {
await this.option.execFilterCallBack();
}
$alert.close();
};
domUtils.on($button, "click", async (event) => {
utils.preventEvent(event);
if (typeof filterOption.callback === "function") {
let result = await filterOption.callback(
event,
execFilterAndCloseDialog
);
if (!result) {
return;
}
}
await execFilterAndCloseDialog();
});
$fragment.appendChild($button);
});
$filterContainer.appendChild($fragment);
}
}
class RuleView {
option;
constructor(option) {
this.option = option;
}
/**
* 显示视图
* @param filterCallBack 返回值为false隐藏,true则不隐藏(不处理)
*/
async showView(filterCallBack) {
let $popsConfirm = __pops.confirm({
title: {
text: this.option.title,
position: "center"
},
content: {
text: (
/*html*/
`
`
),
html: true
},
btn: {
merge: true,
reverse: false,
position: "space-between",
ok: {
enable: this.option?.bottomControls?.add?.enable || true,
type: "primary",
text: "添加",
callback: async (event) => {
this.showEditView(
false,
await this.option.getAddData(),
$popsConfirm.$shadowRoot
);
}
},
close: {
enable: true,
callback(event) {
$popsConfirm.close();
}
},
cancel: {
enable: this.option?.bottomControls?.filter?.enable || false,
type: "default",
text: "过滤",
callback: (details, event) => {
if (typeof this.option?.bottomControls?.filter?.callback === "function") {
this.option.bottomControls.filter.callback();
}
let getAllRuleElement = () => {
return Array.from(
$popsConfirm.$shadowRoot.querySelectorAll(
".rule-view-container .rule-item"
)
);
};
let $button = event.target.closest(".pops-confirm-btn").querySelector(".pops-confirm-btn-cancel span");
if (domUtils.text($button).includes("取消")) {
getAllRuleElement().forEach(($el) => {
domUtils.show($el, false);
});
domUtils.text($button, "过滤");
} else {
let ruleFilterView = new RuleFilterView({
title: this.option.bottomControls?.filter?.title ?? "过滤规则",
filterOption: this.option.bottomControls?.filter?.option || [],
execFilterCallBack() {
domUtils.text($button, "取消过滤");
},
getAllRuleInfo: () => {
return getAllRuleElement().map(($el) => {
return {
data: this.parseRuleItemElement($el).data,
$el
};
});
}
});
ruleFilterView.showView();
}
}
},
other: {
enable: this.option?.bottomControls?.clear?.enable || true,
type: "xiaomi-primary",
text: `清空所有(${(await this.option.data()).length})`,
callback: (event) => {
let $askDialog = __pops.confirm({
title: {
text: "提示",
position: "center"
},
content: {
text: "确定清空所有的数据?",
html: false
},
btn: {
ok: {
enable: true,
callback: async (popsEvent) => {
log$1.success("清空所有");
if (typeof this.option?.bottomControls?.clear?.callback === "function") {
this.option.bottomControls.clear.callback();
}
let data2 = await this.option.data();
if (data2.length) {
Qmsg.error("清理失败");
return;
} else {
Qmsg.success("清理成功");
}
await this.updateDeleteAllBtnText($popsConfirm.$shadowRoot);
this.clearContent($popsConfirm.$shadowRoot);
$askDialog.close();
}
},
cancel: {
text: "取消",
enable: true
}
},
mask: { enable: true },
width: "300px",
height: "200px"
});
}
}
},
mask: {
enable: true
},
width: window.innerWidth > 500 ? "500px" : "88vw",
height: window.innerHeight > 500 ? "500px" : "80vh",
style: (
/*css*/
`
${__pops.config.cssText.panelCSS}
.rule-item{
display: flex;
align-items: center;
line-height: normal;
font-size: 16px;
padding: 4px 8px;
gap: 8px;
}
.rule-name{
flex: 1;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.rule-controls{
display: flex;
align-items: center;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
gap: 8px;
padding: 0px;
}
.rule-controls-enable{
}
.rule-controls-edit{
}
.rule-controls-delete{
}
.rule-controls-edit,
.rule-controls-delete{
width: 16px;
height: 16px;
cursor: pointer;
}
`
)
});
let allData = await this.option.data();
let changeButtonText = false;
for (let index = 0; index < allData.length; index++) {
let item = allData[index];
let $ruleItemList = await this.appendRuleItemElement(
$popsConfirm.$shadowRoot,
item
);
let flag = typeof filterCallBack === "function" ? filterCallBack(item) : true;
if (!flag) {
changeButtonText = true;
$ruleItemList.forEach(($el) => {
domUtils.hide($el, false);
});
}
}
if (changeButtonText) {
let $button = $popsConfirm.$shadowRoot.querySelector(
".pops-confirm-btn-cancel span"
);
domUtils.text($button, "取消过滤");
}
}
/**
* 显示编辑视图
* @param isEdit 是否是编辑状态
* @param editData 编辑的数据
* @param $parentShadowRoot (可选)关闭弹窗后对ShadowRoot进行操作
* @param $editRuleItemElement (可选)关闭弹窗后对规则行进行更新数据
* @param updateDataCallBack (可选)关闭添加/编辑弹窗的回调(不更新数据)
* @param submitCallBack (可选)添加/修改提交的回调
*/
showEditView(isEdit, editData, $parentShadowRoot, $editRuleItemElement, updateDataCallBack, submitCallBack) {
let dialogCloseCallBack = async (isSubmit) => {
if (isSubmit) {
if (typeof submitCallBack === "function") {
let newData = await this.option.getData(editData);
submitCallBack(newData);
}
} else {
if (!isEdit) {
await this.option.deleteData(editData);
}
if (typeof updateDataCallBack === "function") {
let newData = await this.option.getData(editData);
updateDataCallBack(newData);
}
}
};
let editView = new RuleEditView({
title: isEdit ? "编辑" : "添加",
data: () => {
return editData;
},
dialogCloseCallBack,
getView: async (data2) => {
return await this.option.itemControls.edit.getView(data2, isEdit);
},
btn: {
ok: {
enable: true,
text: isEdit ? "修改" : "添加"
},
cancel: {
callback: async (detail, event) => {
detail.close();
await dialogCloseCallBack(false);
}
},
close: {
callback: async (detail, event) => {
detail.close();
await dialogCloseCallBack(false);
}
}
},
onsubmit: async ($form, data2) => {
let result = await this.option.itemControls.edit.onsubmit(
$form,
isEdit,
data2
);
if (result.success) {
if (isEdit) {
Qmsg.success("修改成功");
$parentShadowRoot && await this.updateRuleItemElement(
result.data,
$editRuleItemElement,
$parentShadowRoot
);
} else {
$parentShadowRoot && await this.appendRuleItemElement(
$parentShadowRoot,
result.data
);
}
} else {
if (isEdit) {
log$1.error("修改失败");
}
}
return result;
},
style: this.option.itemControls.edit.style,
width: this.option.itemControls.edit.width,
height: this.option.itemControls.edit.height
});
editView.showView();
}
/**
* 解析弹窗内的各个元素
*/
parseViewElement($shadowRoot) {
let $container = $shadowRoot.querySelector(
".rule-view-container"
);
let $deleteBtn = $shadowRoot.querySelector(
".pops-confirm-btn button.pops-confirm-btn-other"
);
return {
/** 容器 */
$container,
/** 左下角的清空按钮 */
$deleteBtn
};
}
/**
* 解析每一项的元素
*/
parseRuleItemElement($ruleElement) {
let $enable = $ruleElement.querySelector(
".rule-controls-enable"
);
let $enableSwitch = $enable.querySelector(".pops-panel-switch");
let $enableSwitchInput = $enable.querySelector(
".pops-panel-switch__input"
);
let $enableSwitchCore = $enable.querySelector(
".pops-panel-switch__core"
);
let $edit = $ruleElement.querySelector(".rule-controls-edit");
let $delete = $ruleElement.querySelector(
".rule-controls-delete"
);
return {
/** 启用开关 */
$enable,
/** 启用开关的container */
$enableSwitch,
/** 启用开关的input */
$enableSwitchInput,
/** 启用开关的core */
$enableSwitchCore,
/** 编辑按钮 */
$edit,
/** 删除按钮 */
$delete,
/** 存储在元素上的数据 */
data: Reflect.get($ruleElement, "data-rule")
};
}
/**
* 创建一条规则元素
*/
async createRuleItemElement(data2, $shadowRoot) {
let name = await this.option.getDataItemName(data2);
let $ruleItem = domUtils.createElement("div", {
className: "rule-item",
innerHTML: (
/*html*/
`
${name}
${__pops.config.iconSVG.edit}
${__pops.config.iconSVG.delete}
`
)
});
Reflect.set($ruleItem, "data-rule", data2);
let switchCheckedClassName = "pops-panel-switch-is-checked";
const {
$enable,
$enableSwitch,
$enableSwitchCore,
$enableSwitchInput,
$delete,
$edit
} = this.parseRuleItemElement($ruleItem);
if (this.option.itemControls.enable.enable) {
domUtils.on($enableSwitchCore, "click", async (event) => {
let isChecked = false;
if ($enableSwitch.classList.contains(switchCheckedClassName)) {
$enableSwitch.classList.remove(switchCheckedClassName);
isChecked = false;
} else {
$enableSwitch.classList.add(switchCheckedClassName);
isChecked = true;
}
$enableSwitchInput.checked = isChecked;
await this.option.itemControls.enable.callback(data2, isChecked);
});
if (await this.option.itemControls.enable.getEnable(data2)) {
$enableSwitch.classList.add(switchCheckedClassName);
}
} else {
$enable.remove();
}
if (this.option.itemControls.edit.enable) {
domUtils.on($edit, "click", (event) => {
utils.preventEvent(event);
this.showEditView(true, data2, $shadowRoot, $ruleItem, (newData) => {
data2 = null;
data2 = newData;
});
});
} else {
$edit.remove();
}
if (this.option.itemControls.delete.enable) {
domUtils.on($delete, "click", (event) => {
utils.preventEvent(event);
let $askDialog = __pops.confirm({
title: {
text: "提示",
position: "center"
},
content: {
text: "确定删除该条数据?",
html: false
},
btn: {
ok: {
enable: true,
callback: async (popsEvent) => {
log$1.success("删除数据");
let flag = await this.option.itemControls.delete.deleteCallBack(
data2
);
if (flag) {
Qmsg.success("成功删除该数据");
$ruleItem.remove();
await this.updateDeleteAllBtnText($shadowRoot);
$askDialog.close();
} else {
Qmsg.error("删除该数据失败");
}
}
},
cancel: {
text: "取消",
enable: true
}
},
mask: {
enable: true
},
width: "300px",
height: "200px"
});
});
} else {
$delete.remove();
}
return $ruleItem;
}
/**
* 添加一个规则元素
*/
async appendRuleItemElement($shadowRoot, data2) {
let { $container } = this.parseViewElement($shadowRoot);
let $ruleItem = [];
let iteratorData = Array.isArray(data2) ? data2 : [data2];
for (let index = 0; index < iteratorData.length; index++) {
let item = iteratorData[index];
let $item = await this.createRuleItemElement(item, $shadowRoot);
$container.appendChild($item);
$ruleItem.push($item);
}
await this.updateDeleteAllBtnText($shadowRoot);
return $ruleItem;
}
/**
* 更新弹窗内容的元素
*/
async updateRuleContaienrElement($shadowRoot) {
this.clearContent($shadowRoot);
const { $container } = this.parseViewElement($shadowRoot);
let data2 = await this.option.data();
await this.appendRuleItemElement($shadowRoot, data2);
await this.updateDeleteAllBtnText($shadowRoot);
}
/**
* 更新规则元素
*/
async updateRuleItemElement(data2, $oldRuleItem, $shadowRoot) {
let $newRuleItem = await this.createRuleItemElement(data2, $shadowRoot);
$oldRuleItem.after($newRuleItem);
$oldRuleItem.remove();
}
/**
* 清空内容
*/
clearContent($shadowRoot) {
const { $container } = this.parseViewElement($shadowRoot);
domUtils.html($container, "");
}
/**
* 设置删除按钮的文字
*/
setDeleteBtnText($shadowRoot, text, isHTML = false) {
const { $deleteBtn } = this.parseViewElement($shadowRoot);
if (isHTML) {
domUtils.html($deleteBtn, text);
} else {
domUtils.text($deleteBtn, text);
}
}
/**
* 更新【清空所有】的按钮的文字
* @param $shadowRoot
*/
async updateDeleteAllBtnText($shadowRoot) {
let data2 = await this.option.data();
this.setDeleteBtnText($shadowRoot, `清空所有(${data2.length})`);
}
}
const BilibiliComponentDetectionRule = {
$data: {
/** 白名单用户id */
whiteList: [],
/** 规则数据 */
ruleData: []
},
$key: {
STORAGE_KEY: "bili-componentDetection-rule"
},
/** 初始化数据 */
init() {
this.$data.whiteList = [];
this.$data.ruleData = [];
let allData = this.getData();
allData.forEach((data2) => {
if (!data2.enable) {
return;
}
this.$data.ruleData.push(data2);
});
},
/**
* 显示视图
*/
showView() {
let panelHandlerComponents = __pops.config.PanelHandlerComponents();
function generateStorageApi(data2, handler) {
return {
get(key, defaultValue) {
return data2[key] ?? defaultValue;
},
set(key, value) {
data2[key] = value;
}
};
}
let ruleView = new RuleView({
title: "成分检测",
data: () => {
return this.getData();
},
getAddData: () => {
return this.getTemplateData();
},
getDataItemName: (data2) => {
return data2["name"];
},
updateData: (data2) => {
return this.updateData(data2);
},
deleteData: (data2) => {
return this.deleteData(data2);
},
getData: (data2) => {
let allData = this.getData();
let findValue = allData.find((item) => item.uuid === data2.uuid);
return findValue ?? data2;
},
itemControls: {
enable: {
enable: true,
getEnable(data2) {
return data2.enable;
},
callback: (data2, enable) => {
data2.enable = enable;
this.updateData(data2);
}
},
edit: {
enable: true,
getView: (data2, isEdit) => {
let $fragment = document.createDocumentFragment();
let templateData = this.getTemplateData();
if (!isEdit) {
data2 = templateData;
}
let enable_template = UISwitch(
"启用",
"enable",
templateData.enable
);
Reflect.set(
enable_template.props,
PROPS_STORAGE_API,
generateStorageApi(data2)
);
let $enable = panelHandlerComponents.createSectionContainerItem_switch(
enable_template
);
let name_template = UIInput(
"规则名称",
"name",
"",
templateData.name,
void 0,
"必填"
);
Reflect.set(
name_template.props,
PROPS_STORAGE_API,
generateStorageApi(data2)
);
let $name = panelHandlerComponents.createSectionContainerItem_input(
name_template
);
let isShowDisplayName_template = UISwitch(
"是否显示标签名称",
"isShowDisplayName",
templateData.data.isShowDisplayName
);
Reflect.set(
isShowDisplayName_template.props,
PROPS_STORAGE_API,
generateStorageApi(data2.data)
);
let $isShowDisplayName = panelHandlerComponents.createSectionContainerItem_switch(
isShowDisplayName_template
);
let displayName_template = UIInput(
"标签名称",
"displayName",
templateData.data.displayName,
"例如:原神"
);
Reflect.set(
displayName_template.props,
PROPS_STORAGE_API,
generateStorageApi(data2.data)
);
let $displayName = panelHandlerComponents.createSectionContainerItem_input(
displayName_template
);
let isShowDisplayIcon_template = UISwitch(
"是否显示标签图标",
"isShowDisplayIcon",
templateData.data.isShowDisplayIcon
);
Reflect.set(
isShowDisplayIcon_template.props,
PROPS_STORAGE_API,
generateStorageApi(data2.data)
);
let $isShowDisplayIcon = panelHandlerComponents.createSectionContainerItem_switch(
isShowDisplayIcon_template
);
let displayIcon_template = UIInput(
"标签图标",
"displayIcon",
templateData.data.displayIcon,
"Url或base64"
);
Reflect.set(
displayIcon_template.props,
PROPS_STORAGE_API,
generateStorageApi(data2.data)
);
let $displayIcon = panelHandlerComponents.createSectionContainerItem_input(
displayIcon_template
);
let keywords_template = UITextArea(
"关键词",
"keywords",
"",
"用于匹配标题、简介、转发内容的关键词",
void 0,
"多个关键词换行"
);
Reflect.set(keywords_template.props, PROPS_STORAGE_API, {
get(key, defaultValue) {
let value = data2.data[key] ?? defaultValue;
if (typeof value === "string") {
return value.split("\n");
}
return value;
},
set(key, value) {
if (typeof value === "string") {
value = value.split("\n");
}
data2.data[key] = value;
}
});
let $keywords = panelHandlerComponents.createSectionContainerItem_textarea(
keywords_template
);
let followings_template = UITextArea(
"关注的用户",
"followings",
"",
"用户id",
void 0,
"多个用户id换行"
);
Reflect.set(followings_template.props, PROPS_STORAGE_API, {
get(key, defaultValue) {
let value = data2.data[key] ?? defaultValue;
if (typeof value === "string") {
return value.split("\n").map((it) => Number(it)).filter((it) => !isNaN(it));
}
return value;
},
set(key, value) {
if (typeof value === "string") {
value = value.split("\n").map((it) => Number(it)).filter((it) => !isNaN(it));
}
data2.data[key] = value;
}
});
let $followings = panelHandlerComponents.createSectionContainerItem_textarea(
followings_template
);
let blacklist_template = UITextArea(
"黑名单",
"blacklist",
"",
"",
void 0,
"多个用户id换行"
);
Reflect.set(blacklist_template.props, PROPS_STORAGE_API, {
get(key, defaultValue) {
let value = data2.data[key] ?? defaultValue;
if (typeof value === "string") {
return value.split("\n").map((it) => Number(it)).filter((it) => !isNaN(it));
}
return value;
},
set(key, value) {
if (typeof value === "string") {
value = value.split("\n").map((it) => Number(it)).filter((it) => !isNaN(it));
}
data2.data[key] = value;
}
});
let $blacklist = panelHandlerComponents.createSectionContainerItem_textarea(
blacklist_template
);
$fragment.append(
$enable,
$name,
$isShowDisplayName,
$displayName,
$isShowDisplayIcon,
$displayIcon,
$keywords,
$followings,
$blacklist
);
return $fragment;
},
onsubmit: ($form, isEdit, editData) => {
let $ulist_li = $form.querySelectorAll(
".rule-form-ulist > li"
);
let data2 = this.getTemplateData();
if (isEdit) {
data2.uuid = editData.uuid;
}
try {
$ulist_li.forEach(($li) => {
let formConfig = Reflect.get($li, "__formConfig__");
let attrs = Reflect.get(formConfig, "attributes");
let storageApi = Reflect.get($li, PROPS_STORAGE_API);
let key = Reflect.get(attrs, ATTRIBUTE_KEY);
let defaultValue = Reflect.get(attrs, ATTRIBUTE_DEFAULT_VALUE);
let value = storageApi.get(key, defaultValue);
if (Reflect.has(data2, key)) {
Reflect.set(data2, key, value);
} else if (Reflect.has(data2.data, key)) {
Reflect.set(data2.data, key, value);
} else {
log$1.error(`${key}不在数据中`);
}
});
if (data2.name.trim() === "") {
Qmsg.error("规则名称不能为空");
return {
success: false,
data: data2
};
}
if (isEdit) {
return {
success: this.updateData(data2),
data: data2
};
} else {
return {
success: this.addData(data2),
data: data2
};
}
} catch (error) {
log$1.error(error);
return {
success: false,
data: data2
};
} finally {
this.init();
}
},
style: (
/*css*/
`
.pops-panel-textarea textarea{
height: 150px;
}
.pops-panel-item-left-desc-text{
line-height: normal;
margin-top: 6px;
font-size: 0.8em;
color: rgb(108, 108, 108);
max-width: 100px;
}
`
)
},
delete: {
enable: true,
deleteCallBack: (data2) => {
return this.deleteData(data2);
}
}
}
});
ruleView.showView();
},
/**
* 获取模板数据
*/
getTemplateData() {
return {
uuid: utils.generateUUID(),
enable: true,
name: "",
data: {
isShowDisplayIcon: true,
displayIcon: "",
isShowDisplayName: true,
displayName: "",
keywords: [],
blacklist: [],
followings: []
}
};
},
/**
* 获取数据
*/
getData() {
return _GM_getValue(this.$key.STORAGE_KEY, []);
},
/**
* 设置数据
* @param data
*/
setData(data2) {
_GM_setValue(this.$key.STORAGE_KEY, data2);
},
/**
* 添加数据
* @param data
*/
addData(data2) {
let localData = this.getData();
let findIndex = localData.findIndex((item) => item.uuid == data2.uuid);
if (findIndex === -1) {
localData.push(data2);
_GM_setValue(this.$key.STORAGE_KEY, localData);
return true;
} else {
return false;
}
},
/**
* 更新数据
* @param data
*/
updateData(data2) {
let localData = this.getData();
let index = localData.findIndex((item) => item.uuid == data2.uuid);
let updateFlag = false;
if (index !== -1) {
updateFlag = true;
localData[index] = data2;
}
this.setData(localData);
return updateFlag;
},
/**
* 删除数据
* @param data
*/
deleteData(data2) {
let localData = this.getData();
let index = localData.findIndex((item) => item.uuid == data2.uuid);
let deleteFlag = false;
if (index !== -1) {
deleteFlag = true;
localData.splice(index, 1);
}
this.setData(localData);
return deleteFlag;
},
/**
* 清空数据
*/
clearData() {
_GM_deleteValue(this.$key.STORAGE_KEY);
},
/**
* 导出规则
*/
exportRule(fileName = "rule.json") {
let allRule = this.getData();
let blob = new Blob([JSON.stringify(allRule, null, 4)]);
let blobUrl = window.URL.createObjectURL(blob);
let $a = document.createElement("a");
$a.href = blobUrl;
$a.download = fileName;
$a.click();
setTimeout(() => {
window.URL.revokeObjectURL(blobUrl);
}, 1500);
},
/**
* 导入规则
*/
importRule() {
let $alert = __pops.alert({
title: {
text: "请选择导入方式",
position: "center"
},
content: {
text: (
/*html*/
`
本地导入
网络导入
`
),
html: true
},
width: PanelUISize.info.width,
height: PanelUISize.info.height,
style: (
/*css*/
`
.import-mode{
display: inline-block;
margin: 10px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
cursor: pointer;
}
`
)
});
let $local = $alert.$shadowRoot.querySelector(
".import-mode[data-mode='local']"
);
let $network = $alert.$shadowRoot.querySelector(
".import-mode[data-mode='network']"
);
domUtils.on($local, "click", (event) => {
utils.preventEvent(event);
$alert.close();
let $input = domUtils.createElement("input", {
type: "file",
accept: ".json"
});
domUtils.on($input, ["propertychange", "input"], (event2) => {
if (!$input.files?.length) {
return;
}
let uploadFile = $input.files[0];
let fileReader = new FileReader();
fileReader.onload = () => {
let data2 = utils.toJSON(fileReader.result);
if (!Array.isArray(data2)) {
log$1.error("不是正确的规则文件", data2);
Qmsg.error("不是正确的规则文件");
return;
}
this.setData(data2);
Qmsg.success(`成功导入 ${data2.length}条规则`);
};
fileReader.readAsText(uploadFile, "UTF-8");
});
$input.click();
});
domUtils.on($network, "click", (event) => {
utils.preventEvent(event);
$alert.close();
__pops.prompt({
title: {
text: "网络导入",
position: "center"
},
content: {
text: "",
placeholder: "url",
focus: true
},
btn: {
ok: {
callback: async (eventDetails, event2) => {
let url = eventDetails.text;
if (utils.isNull(url)) {
Qmsg.error("请填入完整的url");
return;
}
let response = await httpx.get(url);
if (!response.status) {
return;
}
let data2 = utils.toJSON(response.data.responseText);
if (!Array.isArray(data2)) {
log$1.error("不是正确的规则文件", response, data2);
Qmsg.error("不是正确的规则文件");
return;
}
this.setData(data2);
eventDetails.close();
Qmsg.success(`成功导入 ${data2.length}条规则`);
}
}
},
width: PanelUISize.info.width,
height: "auto"
});
});
}
};
const BilibiliComponentDetection = {
$data: {
/** 查询图标svg */
searchIcon: (
/*html*/
`
`
)
},
init() {
BilibiliComponentDetectionRule.init();
addStyle(
/*css*/
`
.composition-checkable,
.composition-checked{
display: inline-flex;
vertical-align: middle;
}
/* 查询按钮 */
.composition-checkable .composition-badge-control {
display: inline-flex;
justify-content: center;
align-items: center;
width: fit-content;
background: #574AB830;
border-radius: 8px;
margin: 0 6px 0 6px;
font-family: PingFang SC, HarmonyOS_Regular, Helvetica Neue, Microsoft YaHei, sans-serif;
}
.composition-checkable .composition-name-control {
color: #7367F0;
padding: 2px 8px;
font-size: 0.8rem;
display: flex;
align-items: center;
height: 20px;
line-height: normal;
}
.composition-checkable .composition-name-control svg {
vertical-align: middle;
width: 1em;
height: 1em;
}
/* ↑查询按钮 */
/* 标签按钮 */
.composition-checked .composition-badge {
display: inline-flex;
justify-content: center;
align-items: center;
width: fit-content;
background: #574AB825;
border-radius: 10px;
margin: 0 6px 0 6px;
font-family: PingFang SC, HarmonyOS_Regular, Helvetica Neue, Microsoft YaHei, sans-serif;
font-weight: normal;
cursor: pointer;
}
.composition-checked .composition-name {
color: #574AB8;
padding: 2px 8px;
font-size: 0.8rem;
}
.composition-checked .composition-icon {
color: #574AB8 !important;
background: transparent !important;
border-radius: 50% !important;
width: 1.44rem !important;
height: 1.44rem !important;
border: 2px solid #574AB880 !important;
margin: -6px;
display: flex !important;
justify-content: center !important;
align-items: center !important;
font-size: 1rem !important;
}
.composition-checked .composition-badge > *:first-child{
margin-left: 6px;
}
.composition-checked .composition-badge > *:last-child{
margin-right: 6px;
}
.composition-checked .composition-badge .composition-icon,
.composition-checked .composition-badge .composition-name{
margin: 0;
}
`
);
domUtils.ready(() => {
let lockFn = new utils.LockFunction(async () => {
$$(".reply-item:not([data-is-inject-search-label])").forEach(
($replyItem) => {
$replyItem.setAttribute("data-is-inject-search-label", "");
let $floorTime = $replyItem.querySelector(".info .floor-time") || $replyItem.querySelector(".content-warp .user-info");
let { $container, $compositionNameControl } = this.createSearchButton(() => {
let $userName = $replyItem.querySelector(
".user-name[data-user-id]"
);
if (!$userName) {
throw new TypeError("获取用户名元素失败");
}
let mid = $userName.getAttribute("data-user-id");
if (mid == null) {
throw new TypeError("获取mid失败");
}
return mid;
});
domUtils.after($floorTime, $container);
}
);
[
...Array.from(
$$(
".reply-item .member-link[data-url]:not([data-is-inject-search-label])"
)
),
...Array.from(
$$(
".reply-item .jump-link.user[data-user-id]:not([data-is-inject-search-label])"
)
),
...Array.from(
$$(
".reply-item .sub-user-name[data-user-id]:not([data-is-inject-search-label])"
)
)
].forEach(($memberLink) => {
$memberLink.setAttribute("data-is-inject-search-label", "");
let {
$container: $memberContainer,
$compositionNameControl: $memberCompositionNameControl
} = this.createSearchButton(() => {
let spaceUrl = $memberLink.getAttribute("href");
let mid = spaceUrl.match(/space.bilibili.com\/([\d]+)/i)?.[1];
if (mid == null) {
throw new TypeError("获取mid失败");
}
return mid;
});
domUtils.after($memberLink, $memberContainer);
});
$$(
".m-space-info .base:not([data-is-inject-search-label])"
).forEach(($base) => {
$base.setAttribute("data-is-inject-search-label", "");
let $spaceInfo = $base.closest(".m-space-info");
let { $container } = this.createSearchButton(() => {
let vueIns = VueUtils.getVue($spaceInfo);
if (!vueIns) {
throw new TypeError("获取vue属性失败");
}
let mid = vueIns.info.mid;
if (mid == null) {
throw new TypeError("获取mid失败");
}
return mid;
});
domUtils.after($base, $container);
});
});
utils.mutationObserver(document, {
config: {
subtree: true,
childList: true
},
immediate: true,
callback: () => {
lockFn.run();
}
});
});
},
/**
* 查询用户信息
*
* 即提取需要判断的信息
* @param mid
*/
async queryUserInfo(mid) {
let followingPN = 1;
let allFollowingData = [];
while (true) {
log$1.info(`正在获取用户的关注:${mid} ==> 第${followingPN}页`);
let followingData = await BilibiliUserApi.following(mid, followingPN);
if (!followingData) {
log$1.error("获取关注列表失败");
break;
}
if (typeof followingData === "string") {
log$1.error("获取关注列表失败,原因:" + followingData);
break;
}
if (!followingData.list.length) {
break;
}
allFollowingData = allFollowingData.concat(followingData.list);
if (followingData.list.length === followingData.total && followingPN === 1) {
break;
}
followingPN++;
utils.sleep(250);
}
let spaceOffset = "";
let spacePNCount = 1;
let allSpaceContentData = [];
while (true) {
log$1.info(`正在获取用户的空间动态:${mid} ==> 偏移:${spaceOffset}`);
let spaceData = await BilibiliUserApi.space(mid, spaceOffset);
if (!spaceData) {
log$1.error("获取用户空间动态数据失败");
break;
}
if (typeof spaceData === "string") {
log$1.error("获取用户空间动态数据失败,原因:" + spaceData);
break;
}
if (spaceOffset === spaceData.offset && spaceOffset != "") {
break;
}
spaceOffset = spaceData.offset;
allSpaceContentData = allSpaceContentData.concat(spaceData.items);
if (!spaceData.has_more) {
break;
}
spacePNCount++;
if (spacePNCount > 5) {
log$1.info(`最多请求5页空间动态的数据`);
break;
}
utils.sleep(250);
}
let result = {
/** 关注列表信息 */
following: [],
/** 空间动态信息 */
space: []
};
allFollowingData.forEach((followingData) => {
result.following.push({
name: followingData.uname,
mid: followingData.mid,
sign: followingData.sign
});
});
allSpaceContentData.forEach((spaceData) => {
if (spaceData.orig == null) {
let contentInfo = {
title: spaceData.modules.module_dynamic.major?.archive?.title,
desc: spaceData.modules.module_dynamic.major?.archive?.desc || spaceData.modules.module_dynamic.desc?.text,
pub_ts: spaceData.modules.module_author.pub_ts * 1e3,
id_str: spaceData.id_str
};
result.space.push({
contentInfo
});
} else {
let contentInfo = {
title: null,
desc: spaceData.modules.module_dynamic.desc?.text,
pub_ts: spaceData.modules.module_author.pub_ts * 1e3,
id_str: spaceData.id_str
};
let forwardInfo = {
mid: spaceData.orig.modules.module_author.mid,
name: spaceData.orig.modules.module_author.name,
title: (
// 转发的内容的标题
spaceData.orig.modules.module_dynamic?.major?.archive?.title || null
),
desc: spaceData.orig.modules.module_dynamic.desc?.text ?? // 转发的内容的描述
spaceData.orig.modules.module_dynamic?.major?.archive?.desc,
pub_ts: spaceData.orig.modules.module_author.pub_ts * 1e3,
id_str: spaceData.orig.id_str
};
if (typeof forwardInfo.desc === "string" && Array.isArray(
spaceData.orig.modules.module_dynamic?.desc?.rich_text_nodes
)) {
spaceData.orig.modules.module_dynamic.desc.rich_text_nodes.forEach(
(richInfo) => {
if (richInfo.type === "RICH_TEXT_NODE_TYPE_AT") {
forwardInfo.desc = forwardInfo.desc?.replace(richInfo.text, "");
}
}
);
}
result.space.push({
contentInfo,
forwardInfo
});
}
});
return result;
},
/**
* 创建查询按钮
* @param queryMIDFn 查询mid的函数
*/
createSearchButton(queryMIDFn) {
let $compositionCheckable = domUtils.createElement("div", {
className: "composition-checkable",
innerHTML: (
/*html*/
`
${this.$data.searchIcon}
`
)
});
let $compositionNameControl = $compositionCheckable.querySelector(
".composition-name-control"
);
domUtils.on($compositionCheckable, "click", async (event) => {
utils.preventEvent(event);
if ($compositionCheckable.hasAttribute("data-is-searching")) {
log$1.error("正在搜索中,请稍后再试");
return;
}
$compositionCheckable.setAttribute("data-is-searching", "");
domUtils.html($compositionNameControl, "...");
try {
if (BilibiliComponentDetectionRule.$data.ruleData.length === 0) {
Qmsg.warning("未配置规则,请在设置中进行添加");
domUtils.html($compositionNameControl, this.$data.searchIcon);
return;
}
let mid = queryMIDFn();
this.clearLabel($compositionCheckable);
let userInfo = await this.queryUserInfo(mid);
this.handleShowLabel(mid, userInfo, $compositionCheckable);
domUtils.html($compositionNameControl, this.$data.searchIcon);
} catch (error) {
log$1.error(error);
Qmsg.error(error.message, {
timeout: 3500
});
domUtils.html($compositionNameControl, "重试");
} finally {
$compositionCheckable.removeAttribute("data-is-searching");
}
});
return {
$container: $compositionCheckable,
$compositionNameControl
};
},
/**
* 创建标签
* @param data
*/
createLabel(data2) {
let $label = domUtils.createElement("div", {
className: "composition-checked",
innerHTML: (
/*html*/
`
`
)
});
let $badge = $label.querySelector(".composition-badge");
if (data2.rule.data.isShowDisplayName) {
let $compositionName = domUtils.createElement("span", {
className: "composition-name",
innerHTML: data2.rule.data.displayName
});
domUtils.append($badge, $compositionName);
}
if (data2.rule.data.isShowDisplayIcon) {
let $compositionIcon = null;
if (data2.rule.data.displayIcon.startsWith("http")) {
$compositionIcon = domUtils.createElement(
"img",
{
className: "composition-icon",
src: data2.rule.data.displayIcon
},
{
referrer: "no-referrer",
referrerPolicy: "no-referrer"
}
);
} else {
$compositionIcon = domUtils.createElement("span", {
className: "composition-icon",
innerHTML: data2.rule.data.displayIcon
});
}
domUtils.append($badge, $compositionIcon);
}
domUtils.on($badge, "click", (event) => {
utils.preventEvent(event);
__pops.alert({
title: {
text: "识别信息",
html: false,
position: "center"
},
content: {
text: (
/*html*/
`
${data2.matchedInfoList.map((it) => {
let $el = domUtils.createElement("div", {
className: "reason-container",
innerHTML: (
/*html*/
`
原因: ${it.reason}
匹配: ${typeof it.reasonLink === "string" ? (
/*html*/
`
${it.reasonText}
`
) : it.reasonText}
`
)
});
if (typeof it.reasonTime === "number") {
let $reasonTime = domUtils.createElement("div", {
className: "reason-text",
innerHTML: (
/*html*/
`
时间: ${utils.formatTime(it.reasonTime)}
`
)
});
domUtils.append($el, $reasonTime);
}
return $el.outerHTML;
}).join("\n")}
`
),
html: true
},
btn: {
ok: { enable: false }
},
mask: {
enable: true,
clickEvent: {
toClose: true
}
},
width: PanelUISize.setting.width,
height: PanelUISize.setting.height,
style: (
/*css*/
`
.reason-container{
color: #7367F0;
margin: 10px 10px;
}
`
)
});
});
return $label;
},
/**
* 清空标签
* @param $ele
*/
clearLabel($ele) {
while (true) {
let $prev = domUtils.prev($ele);
if (!$prev) {
break;
}
if ($prev?.classList?.contains("composition-checked")) {
$prev.remove();
} else {
break;
}
}
},
/**
* 处理并显示标签
* @param mid 用户mid
* @param data
* @param $searchContainer
*/
handleShowLabel(mid, data2, $searchContainer) {
if (BilibiliComponentDetectionRule.$data.ruleData.length === 0) {
Qmsg.warning("未配置规则,请在设置中进行添加");
return;
}
mid = mid.toString();
if (BilibiliComponentDetectionRule.$data.whiteList.includes(mid)) {
return;
}
let matchedAllRule = [];
let pushMatchedRule = (rule, matchedInfo) => {
let findValue = matchedAllRule.find((it) => it.rule === rule);
if (findValue) {
findValue.matchedInfoList.push(matchedInfo);
} else {
matchedAllRule.push({
rule,
matchedInfoList: [matchedInfo]
});
}
};
BilibiliComponentDetectionRule.$data.ruleData.forEach((ruleData) => {
if (Array.isArray(ruleData.data.blacklist) && ruleData.data.blacklist.find((it) => it.toString() === mid)) {
pushMatchedRule(ruleData, {
reason: "黑名单用户",
reasonText: mid,
reasonLink: BilibiliUrl.getUserSpaceUrl(mid),
reasonTime: null
});
return;
}
if (Array.isArray(ruleData.data.followings)) {
let reason = "关注列表";
let reasonText = "";
let checkFlag = ruleData.data.followings.some((followId) => {
let __check__flag__ = data2.following.some((followingData) => {
return followingData.mid.toString() === followId.toString();
});
if (__check__flag__) {
reasonText = followId.toString();
}
return __check__flag__;
});
if (checkFlag) {
pushMatchedRule(ruleData, {
reason,
reasonText,
reasonLink: BilibiliUrl.getUserSpaceUrl(reasonText),
reasonTime: null
});
}
}
if (Array.isArray(ruleData.data.keywords)) {
ruleData.data.keywords.forEach((keyword) => {
for (let spaceIndex = 0; spaceIndex < data2.space.length; spaceIndex++) {
const spaceData = data2.space[spaceIndex];
let reason = "";
let reasonText = keyword;
let reasonLink = `/opus/${spaceData.contentInfo.id_str}`;
let reasonTime = spaceData.contentInfo.pub_ts;
if (spaceData.forwardInfo == null) {
if (typeof spaceData.contentInfo.desc === "string" && spaceData.contentInfo.desc.match(keyword)) {
reason = "投稿视频简介";
} else if (typeof spaceData.contentInfo.title === "string" && spaceData.contentInfo.title.match(keyword)) {
reason = "投稿视频标题";
}
} else {
if (typeof spaceData.contentInfo.desc === "string" && spaceData.contentInfo.desc.match(keyword)) {
reason = "空间动态转发";
} else if (typeof spaceData.forwardInfo?.title === "string" && spaceData.forwardInfo.title.match(keyword)) {
reason = "空间动态视频标题";
} else if (typeof spaceData.forwardInfo?.desc === "string" && spaceData.forwardInfo.desc.match(keyword)) {
reason = "空间动态视频简介";
}
}
if (reason !== "") {
pushMatchedRule(ruleData, {
reason,
reasonText,
reasonLink,
reasonTime
});
}
}
});
}
});
utils.sortListByProperty(
matchedAllRule,
(value) => {
return value.matchedInfoList.length;
},
true
);
matchedAllRule.forEach((it) => {
let $label = this.createLabel(it);
domUtils.before($searchContainer, $label);
});
}
};
const BilibiliPlayListPlayer = {
$flag: {
isWatchVideoChange: false
},
$data: {
art: null
},
init() {
},
/**
* 更新播放信息
* @param videoInfo
* @param isEpChoose 是否是从选集内调用的
*/
updateArtPlayerVideoInfo(videoInfo, isEpChoose) {
const that = this;
VueUtils.waitVuePropToSet(
BilibiliData.className.playlist + " .playlist-player",
{
msg: "等待覆盖playlist播放器",
check(vueInstance) {
return typeof vueInstance?.aid === "number" && typeof vueInstance?.cid === "number" && typeof vueInstance?.bvid === "string";
},
async set(vueInstance) {
$(".playlist-player .player-container")?.remove();
let $player = $(
BilibiliData.className.playlist + " .playlist-player"
);
let $playerContainer = $(
BilibiliData.className.playlist
);
let playerContainerVueInstance = VueUtils.getVue($playerContainer);
let { aid, cid, bvid } = vueInstance;
let { title, cover: pic } = playerContainerVueInstance.video;
log$1.info(`视频播放信息 => aid:${aid} bvid:${bvid} cid:${cid}`);
if (videoInfo == null) {
videoInfo = {
aid,
bvid,
cid,
pic,
title
};
}
const artPlayerOption = await GenerateArtPlayerOption$1(videoInfo);
if (artPlayerOption == null) {
return;
}
let $artPlayer = $("#artplayer");
if (!$artPlayer) {
const $artPlayerContainer = domUtils.createElement("div", {
className: "artplayer-container",
innerHTML: (
/*html*/
`
`
)
});
$artPlayer = $artPlayerContainer.querySelector("#artplayer");
domUtils.append($player, $artPlayerContainer);
}
artPlayerOption.container = $artPlayer;
if (that.$data.art == null) {
let art = await BilibiliVideoArtPlayer.init(artPlayerOption);
if (art) {
that.$data.art = art;
} else {
return;
}
that.$data.art.volume = 1;
that.$data.art.once("ready", () => {
Panel.execMenu(
"bili-video-playerAutoPlayVideoFullScreen",
async () => {
log$1.info(`自动进入全屏`);
that.$data.art.fullscreen = true;
that.$data.art.once("fullscreenError", () => {
log$1.warn(
"未成功进入全屏,需要用户交互操作,使用网页全屏代替"
);
that.$data.art.fullscreenWeb = true;
});
}
);
});
that.$data.art.on("video:ended", () => {
log$1.info("视频播放结束,自动下一集");
let $controlPanel = $(
BilibiliData.className.playlist + " .control-panel"
);
if (!$controlPanel) {
log$1.error("未找到播放列表,无法自动播放下一集");
return;
}
let controlVueInstance = VueUtils.getVue($controlPanel);
if (controlVueInstance == null) {
log$1.error("未找到播放列表的Vue实例,无法自动播放下一集");
return;
}
let { playMode, mediaList, videoIndex } = vueInstance.$store.state.playlist;
if (videoIndex >= mediaList.length - 1) {
log$1.info(`播放列表已播放完毕`);
} else {
let $currentVideoCard = $(
`.video-card[index="${videoIndex}"]`
);
let currentVideoCardVueInstance = VueUtils.getVue($currentVideoCard);
let p = currentVideoCardVueInstance.p;
if (p >= currentVideoCardVueInstance.video.page) {
let $nextVideoCard = $(
`.video-card[index="${videoIndex + 1}"]`
);
let nextVideoCardVueInstance = VueUtils.getVue($nextVideoCard);
nextVideoCardVueInstance.changeVideo();
log$1.info(
`当前播放列表共:${mediaList.length - 1}个,即将播放下一个视频,第${videoIndex + 2}个`
);
} else {
p++;
currentVideoCardVueInstance.changeVideo(p);
log$1.info(
`当前播放列表共:${mediaList.length - 1}个,即将播放第${videoIndex + 2}-${p}`
);
}
}
});
} else {
await BilibiliVideoArtPlayer.update(
that.$data.art,
artPlayerOption
);
}
}
}
);
VueUtils.waitVuePropToSet(
BilibiliData.className.playlist + " .playlist-player",
{
msg: "等待监听playlist播放列表改变",
check(vueInstance) {
return typeof vueInstance.$watch === "function";
},
set(vueInstance) {
if (!that.$flag.isWatchVideoChange) {
that.$flag.isWatchVideoChange = true;
vueInstance.$watch("cid", (newVal, oldVal) => {
log$1.info(`切换播放视频`);
that.updateArtPlayerVideoInfo();
});
}
}
}
);
}
};
const BilibiliPlayList = {
init() {
this.coverVideoPlayer();
},
/**
* 覆盖视频播放器
*/
coverVideoPlayer() {
if (document.querySelector("#artplayer")) {
log$1.warn("已存在播放器,更新播放信息");
} else {
addStyle(
/*css*/
`
#app .playlist .playlist-player .player-container{
display: none !important;
}
${artPlayerCommonCSS}
${artPlayerCSS$1}
`
);
}
BilibiliPlayListPlayer.updateArtPlayerVideoInfo();
}
};
const Bilibili = {
init() {
BilibiliGlobalData.init();
BilibiliVueProp.init();
Panel.execMenuOnce("bili-allowCopy", () => {
return addStyle(
/*css*/
`
.v-drawer{
-webkit-user-select: unset !important;
-moz-user-select: unset !important;
user-select: unset !important;
}
`
);
});
Panel.onceExec("listenRouterChange", () => {
this.listenRouterChange();
});
Panel.execMenuOnce("bili-hookSetTimeout_autoOpenApp", () => {
log$1.info("hook window.setTimeout autoOpenApp");
BilibiliHook.setTimeout("autoOpenApp");
BilibiliHook.setTimeout("bilibili://");
BilibiliHook.setTimeout("void 0 !== y && document[y]");
});
Panel.execMenuOnce("bili-overrideLaunchAppBtn_Vue_openApp", () => {
log$1.info("覆盖元素.launch-app-btn上的openApp");
BilibiliHook.overRideLaunchAppBtn_Vue_openApp();
});
Panel.execMenuOnce("bili-cover-bili-open-app-open", () => {
log$1.info(`覆盖元素bili-open-app上的opener.open`);
BilibiliHook.overRideBiliOpenApp();
});
Panel.execMenuOnce("bili-cover-wx-tag-handleClick", () => {
log$1.info(`覆盖元素.wx-tag的handleClick函数`);
BilibiliHook.overRideWxTaghandleClick();
});
Panel.execMenuOnce("bili-head-beautify", () => {
log$1.info("添加美化CSS");
return addStyle(BilibiliBeautifyCSS);
});
Panel.execMenuOnce("bili-componentDetection", () => {
BilibiliComponentDetection.init();
});
if (BilibiliRouter.isVideo()) {
log$1.info("Router: 视频稿件");
BilibiliVideo.init();
} else if (BilibiliRouter.isOpus()) {
log$1.info("Router: 专栏稿件");
BilibiliOpus.init();
} else if (BilibiliPCRouter.isReadMobile()) {
log$1.info("PC-Router: 专栏稿件");
BilibiliReadMobile.init();
} else if (BilibiliRouter.isDynamic()) {
log$1.info("Router: 动态");
BilibiliDynamic.init();
} else if (BilibiliRouter.isBangumi()) {
log$1.info("Router: 番剧");
BilibiliBangumi.init();
} else if (BilibiliRouter.isSearch()) {
log$1.info("Router: 搜索");
BilibiliSearch.init();
} else if (BilibiliRouter.isLive()) {
log$1.info("Router: 直播");
BilibiliLive.init();
} else if (BilibiliRouter.isTopicDetail()) {
log$1.info("Router: 话题");
} else if (BilibiliRouter.isHead()) {
log$1.info("Router: 首页之类的");
BilibiliHead.init();
} else if (BilibiliRouter.isSpace()) {
log$1.info("Router: 个人空间");
BilibiliSpace.init();
} else if (BilibiliRouter.isPlayList()) {
log$1.info(`Router: 播放列表`);
BilibiliPlayList.init();
} else {
log$1.error("该Router暂未适配,可能是首页之类:" + window.location.href);
}
domUtils.ready(() => {
});
},
/**
* 监听路由变化
*/
listenRouterChange() {
VueUtils.waitVuePropToSet("#app", {
msg: "监听路由变化",
check: (vueInstance) => {
return typeof vueInstance?.$router?.afterEach === "function";
},
set: (vueInstance) => {
log$1.success("成功设置监听路由变化");
vueInstance.$router.beforeHooks.splice(
0,
0,
(to, from, next) => {
log$1.info("路由变化 => 更新前", {
to,
from
});
if (to["hash"] === "#/seeCommentReply" || from["hash"] === "#/seeCommentReply") {
log$1.info("该路由变化判定为#/seeCommentReply");
next();
return;
}
if (Panel.getValue("bili-repairVueRouter404")) {
if (to.name === "space") {
log$1.info(`修复空间跳转404`);
window.location.href = to.fullPath;
return;
}
}
if (to.fullPath.startsWith("/video")) {
if (from.fullPath.startsWith("/video") && Panel.getValue("bili-video-forceThisPageToRefreshAndRedirect")) {
log$1.info(`强制本页刷新`);
window.location.href = to.fullPath;
return;
} else if (BilibiliRouter.isHead() && Panel.getValue("bili-head-openVideoInNewTab")) {
log$1.info(`当前是首页,新标签页打开`);
window.open(to.fullPath, "_blank");
return;
}
} else if (to.fullPath.startsWith("/bangumi")) {
if (from.fullPath.startsWith("/bangumi")) {
log$1.info(`番剧 => 番剧`);
window.location.href = to.fullPath;
return;
} else if (BilibiliRouter.isHead() && Panel.getValue("bili-head-openVideoInNewTab")) {
log$1.info(`首页 => 番剧`);
window.open(to.fullPath, "_blank");
return;
}
}
next();
}
);
vueInstance.$router.afterHooks.splice(
0,
0,
(to, from) => {
log$1.info("路由变化 => 更新后", {
to,
from
});
if (to["hash"] === "#/seeCommentReply" || from["hash"] === "#/seeCommentReply") {
log$1.info("该路由变化判定为#/seeCommentReply,不重载");
return;
}
Panel.execMenu("bili-listenRouterChange", () => {
Bilibili.init();
});
}
);
}
});
}
};
const UISelect = function(text, key, defaultValue, data2, changeCallback, description) {
let selectData = [];
if (typeof data2 === "function") {
selectData = data2();
} else {
selectData = data2;
}
let result = {
text,
type: "select",
description,
attributes: {},
props: {},
getValue() {
let storageApiValue = this.props[PROPS_STORAGE_API];
return storageApiValue.get(key, defaultValue);
},
callback(event, isSelectedValue, isSelectedText) {
let value = isSelectedValue;
log$1.info(`选择:${isSelectedText}`);
if (typeof changeCallback === "function") {
let result2 = changeCallback(event, value, isSelectedText);
if (result2) {
return;
}
}
let storageApiValue = this.props[PROPS_STORAGE_API];
storageApiValue.set(key, value);
},
data: selectData
};
Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
PanelComponents.initComponentsStorageApi(
"select",
result,
{
get(key2, defaultValue2) {
return Panel.getValue(key2, defaultValue2);
},
set(key2, value) {
Panel.setValue(key2, value);
}
}
);
return result;
};
const UIButton = function(text, description, buttonText, buttonIcon, buttonIsRightIcon, buttonIconIsLoading, buttonType, clickCallBack, afterAddToUListCallBack, disable) {
let result = {
text,
type: "button",
attributes: {},
props: {},
description,
buttonIcon,
buttonIsRightIcon,
buttonIconIsLoading,
buttonType,
buttonText,
callback(event) {
if (typeof clickCallBack === "function") {
clickCallBack(event);
}
},
afterAddToUListCallBack
};
Reflect.set(result.attributes, ATTRIBUTE_INIT, () => {
result.disable = Boolean(
disable
);
});
return result;
};
const SettingUICommon = {
id: "panel-common",
title: "通用",
forms: [
{
text: "",
type: "forms",
forms: [
{
text: "功能",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"监听路由-重载所有功能",
"bili-listenRouterChange",
true,
void 0,
"用于处理页面跳转(本页)时功能不生效问题"
),
UISwitch(
"修复VueRouter跳转404问题",
"bili-repairVueRouter404",
true,
void 0,
"例如:点击UP主正确进入空间"
),
UISwitch(
"新标签页打开",
"bili-go-to-url-blank",
false,
void 0,
"通过开启【覆盖点击事件】相关的设置,通过新标签页打开链接"
),
UISwitch(
"允许复制",
"bili-allowCopy",
true,
void 0,
"一般用于处理楼层的回复弹窗内无法选中复制问题"
)
// UISwitch(
// "自动删除Cookie buvid3",
// "common_auto_delete_cookie_buvid3",
// true
// ),
]
}
]
},
{
text: "变量设置",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"noCallApp",
"bili-noCallApp",
true,
void 0,
"$store.state.common.noCallApp=true"
),
UISwitch(
"isLogin",
"bili-setLogin",
true,
void 0,
[
"$store.state.common.userInfo.isLogin=true",
"$store.state.loginInfo.isLogin=true"
].join(" ")
),
UISwitch(
"isClient",
"bili-setIsClient",
true,
void 0,
[
"$store.state.video.isClient=true",
"$store.state.opus.isClient=true",
"$store.state.playlist.isClient=true",
"$store.state.ver.bili=true",
"$store.state.ver.biliVer=2333"
].join(" ")
)
// UISwitch(
// "tinyApp",
// "bili-setTinyApp",
// true,
// void 0,
// "$store.state.common.tinyApp=true"
// ),
]
}
]
},
{
text: "劫持/拦截",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"覆盖.launch-app-btn openApp",
"bili-overrideLaunchAppBtn_Vue_openApp",
true,
void 0,
"覆盖.launch-app-btn元素上的openApp函数,可阻止点击唤醒/下载App"
),
UISwitch(
"覆盖bili-open-app opener.open",
"bili-cover-bili-open-app-open",
true,
void 0,
"覆盖bili-open-app/m-open-app元素上的opener.open函数,可阻止点击唤醒/下载App,如果存在有效链接,会自动跳转"
),
UISwitch(
"覆盖.wx-tag的handleClick",
"bili-cover-wx-tag-handleClick",
true,
void 0,
"覆盖.wx-tag元素上的点击事件,让它直接打开视频"
),
UISwitch(
"劫持setTimeout-autoOpenApp",
"bili-hookSetTimeout_autoOpenApp",
true,
void 0,
"阻止自动调用App"
)
]
}
]
},
{
type: "deepMenu",
text: "成分检测",
forms: [
{
type: "forms",
text: "",
forms: [
UISwitch(
"启用",
"bili-componentDetection",
true,
void 0,
"启用后可检测用户的成分信息"
),
UIButton(
"自定义规则",
"检测用户成分的规则",
"管理",
void 0,
false,
false,
"primary",
() => {
BilibiliComponentDetectionRule.showView();
}
)
]
},
{
type: "forms",
text: "",
forms: [
UIButton(
"数据导入",
"导入自定义规则数据",
"导入",
void 0,
false,
false,
"primary",
() => {
BilibiliComponentDetectionRule.importRule();
}
),
UIButton(
"数据导出",
"导出自定义规则数据",
"导出",
void 0,
false,
false,
"primary",
() => {
BilibiliComponentDetectionRule.exportRule("成分检测.json");
}
)
]
}
]
}
]
},
{
text: "",
type: "forms",
forms: [
{
text: "数据配置",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UIInput(
"access_token",
"bili-head-recommend-access_token",
BilibiliQrCodeLogin.getAccessToken(),
"填入access_token,可用于获取推荐视频数据、番剧搜索、番剧播放等",
(event, value, valueAsNumber) => {
BilibiliQrCodeLogin.setAccessTokenInfo({
access_token: value,
expireAt: BilibiliQrCodeLogin.generateExpireAt()
});
},
void 0,
false,
true
)
]
}
]
},
{
text: "Toast配置",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISelect(
"Toast位置",
"qmsg-config-position",
"bottom",
[
{
value: "topleft",
text: "左上角"
},
{
value: "top",
text: "顶部"
},
{
value: "topright",
text: "右上角"
},
{
value: "left",
text: "左边"
},
{
value: "center",
text: "中间"
},
{
value: "right",
text: "右边"
},
{
value: "bottomleft",
text: "左下角"
},
{
value: "bottom",
text: "底部"
},
{
value: "bottomright",
text: "右下角"
}
],
(event, isSelectValue, isSelectText) => {
log$1.info("设置当前Qmsg弹出位置" + isSelectText);
},
"Toast显示在页面九宫格的位置"
),
UISelect(
"最多显示的数量",
"qmsg-config-maxnums",
3,
[
{
value: 1,
text: "1"
},
{
value: 2,
text: "2"
},
{
value: 3,
text: "3"
},
{
value: 4,
text: "4"
},
{
value: 5,
text: "5"
}
],
void 0,
"限制Toast显示的数量"
),
UISwitch(
"逆序弹出",
"qmsg-config-showreverse",
false,
void 0,
"修改Toast弹出的顺序"
)
]
}
]
},
{
text: "Cookie配置",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"启用",
"httpx-use-cookie-enable",
false,
void 0,
"启用后,将根据下面的配置进行添加cookie"
),
UISwitch(
"使用document.cookie",
"httpx-use-document-cookie",
false,
void 0,
"自动根据请求的域名来获取对应的cookie"
),
UITextArea(
"bilibili.com",
"httpx-cookie-bilibili.com",
"",
void 0,
void 0,
"Cookie格式:xxx=xxxx;xxx=xxxx"
)
]
}
]
}
]
}
]
};
const SettingUIHead = {
id: "panel-head",
title: "首页",
forms: [
{
text: "",
type: "forms",
forms: [
{
text: "功能",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"美化显示",
"bili-head-beautify",
true,
void 0,
"调整瀑布流视频卡片样式类似哔哩哔哩App"
),
UISwitch(
"美化顶部NavBar",
"bili-beautifyTopNavBar",
true,
void 0,
"类似哔哩哔哩App的样式"
),
UISwitch(
"补充推荐视频信息",
"bili-head-supplementaryVideoStreamingInformation",
true,
void 0,
"给视频添加UP主名,当前视频总时长信息"
),
UISwitch(
"新标签页打开",
"bili-head-openVideoInNewTab",
false,
void 0,
"包括视频、番剧"
)
]
}
]
},
{
text: "推荐视频",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"启用",
"bili-head-recommend-enable",
true,
void 0,
"添加【推荐】标签,数据来源为App端(如果填入了access_token的话)"
),
UISwitch(
"显示【图文】",
"bili-head-recommend-push-graphic",
true,
void 0,
"加载App端推送的【图文】卡片"
)
]
}
]
}
]
}
]
};
const UISlider = function(text, key, defaultValue, min, max, changeCallback, getToolTipContent, description, step) {
let result = {
text,
type: "slider",
description,
attributes: {},
props: {},
getValue() {
let storageApiValue = this.props[PROPS_STORAGE_API];
return storageApiValue.get(key, defaultValue);
},
getToolTipContent(value) {
if (typeof getToolTipContent === "function") {
return getToolTipContent(value);
} else {
return `${value}`;
}
},
callback(event, value) {
let storageApiValue = this.props[PROPS_STORAGE_API];
storageApiValue.set(key, value);
},
min,
max,
step
};
Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
PanelComponents.initComponentsStorageApi(
"slider",
result,
{
get(key2, defaultValue2) {
return Panel.getValue(key2, defaultValue2);
},
set(key2, value) {
Panel.setValue(key2, value);
}
}
);
return result;
};
const SettingUIVideo = {
id: "panel-video",
title: "视频",
isDefault() {
return BilibiliRouter.isVideo();
},
forms: [
{
text: "",
type: "forms",
forms: [
{
text: "功能",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
// UISwitch(
// "调整视频底部区域高度",
// "bili-video-repairVideoBottomAreaHeight",
// true,
// void 0,
// "添加margin-top"
// ),
// UISwitch(
// "美化底部推荐视频",
// "bili-video-beautify",
// true,
// void 0,
// "调整底部推荐视频卡片样式类似哔哩哔哩App"
// ),
// UISwitch(
// "手势返回关闭评论区",
// "bili-video-gestureReturnToCloseCommentArea",
// true,
// void 0,
// "当浏览器手势触发浏览器回退页面时,关闭评论区"
// ),
UISwitch(
"强制本页刷新跳转",
"bili-video-forceThisPageToRefreshAndRedirect",
false,
void 0,
"用于处理内存泄露问题"
),
// UISwitch(
// "修复链接跳转",
// "bili-video-repairLinkJump",
// true,
// void 0,
// "如@用户、搜索"
// ),
UISwitch(
"新增评论模块",
"bili-video-addCommentModule",
true,
void 0,
"用于查看当前视频的评论"
),
UISwitch(
"新增简介模块",
"bili-video-addDescModule",
true,
void 0,
"用于查看当前视频的播放量、简介、一键三连等信息"
)
]
}
// {
// type: "forms",
// text: "底部Tab",
// forms: [
// UISwitch(
// "滚动固钉Tab",
// "bili-video-optimizationScroll",
// true,
// void 0,
// "向下滚动时,自动跳转视频区域大小且对Tab进行吸附处理"
// ),
// UISwitch(
// "禁止滑动切换Tab",
// "bili-video-disableSwipeTab",
// false,
// void 0,
// "禁止左右滑动切换Tab"
// ),
// ],
// },
]
},
{
text: "ArtPlayer播放器",
type: "deepMenu",
forms: [
{
text: "功能",
type: "forms",
forms: [
UISwitch(
"启用",
"bili-video-enableArtPlayer",
true,
void 0,
"使用artplayer代替页面的播放器"
),
UISelect(
"播放的视频类型",
"bili-video-playType",
"mp4",
[
{
text: "mp4",
value: "mp4"
},
{
text: "dash",
value: "dash"
}
],
void 0,
"当选择dash时会有画质更高的选项"
),
UISwitch(
"自动播放视频",
"bili-video-playerAutoPlayVideo",
false,
void 0,
""
),
UISwitch(
"自动进入全屏",
"bili-video-playerAutoPlayVideoFullScreen",
false,
void 0,
""
)
]
},
{
text: "控件设置",
type: "forms",
forms: [
UISlider(
"controls左右边距",
"bili-video-artplayer-controlsPadding-left-right",
0,
0,
50,
void 0,
(value) => {
return value + "px";
},
"可用于全屏横屏适配屏幕",
1
)
]
},
{
text: "插件",
type: "forms",
forms: [
UISwitch(
"弹幕",
"artplayer-plugin-video-danmaku-enable",
true,
void 0,
"哔哩哔哩 (゜-゜)つロ 干杯~"
),
UISwitch(
"Dash Audio Support",
"artplayer-plugin-video-m4sAudioSupport-enable",
true,
void 0,
"视频类型为dash时,该插件可支持播放音频"
),
UISwitch(
"选集",
"artplayer-plugin-video-epChoose-enable",
true,
void 0,
"当视频播放完毕后会自动连播"
),
UISwitch(
"CC字幕",
"artplayer-plugin-video-cc-subtitle-enable",
true,
void 0,
"字幕支持插件,如果存在繁体字幕,则自动生成简体字幕"
),
UISwitch(
"顶部工具栏",
"artplayer-plugin-video-toptoolbar-enable",
true,
void 0,
"显示视频标题和当前观看人数"
),
UISwitch(
"视频统计信息",
"artplayer-plugin-video-statistics-enable",
true,
void 0,
"用于显示当前视频信息的弹窗"
)
]
},
{
text: "加速CDN设置",
type: "forms",
forms: [
UISelect(
"UPOS服务器设置",
"bili-video-uposServerSelect",
"",
BilibiliCDNProxy.getUposCDNServerList().map((item) => {
return {
text: item.name,
value: item.host
};
}),
void 0,
"设置视频流的服务器,可加快视频加载速度"
),
UISwitch(
"作用于Audio上",
"bili-video-uposServerSelect-applyAudio",
false,
void 0,
"把m4s类型的audio也进行upos替换"
)
]
}
]
},
{
text: "覆盖点击事件",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"UP主信息",
"bili-video-cover-UpWrapper",
true,
void 0,
"点击UP主头像/名称可跳转至UP主空间"
),
UISwitch(
"相关视频",
"bili-video-cover-bottomRecommendVideo",
true,
void 0,
"点击下面的相关视频可正确跳转至该视频"
),
UISwitch(
"选集",
"bili-video-cover-seasonNew",
true,
void 0,
"点击下面的选集列表内的视频可正确跳转至该视频"
)
]
}
]
},
{
text: "劫持/拦截",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"阻止调用App",
"bili-video-hook-callApp",
true,
void 0,
"处理函数: PlayerAgent"
)
]
}
]
}
]
}
]
};
const SettingUIOpus = {
id: "panel-opus",
title: "专栏",
isDefault() {
return BilibiliRouter.isOpus();
},
forms: [
{
text: "",
type: "forms",
forms: [
{
text: "功能",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"自动展开阅读全文",
"bili-opus-automaticallyExpandToReadFullText",
true,
void 0,
"屏蔽【展开阅读全文】按钮并自动处理全文高度"
)
]
}
]
},
{
text: "变量设置",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"autoOpenApp",
"bili-opus-variable-autoOpenApp",
true,
void 0,
"autoOpenApp函数置空"
),
UISwitch(
"go404",
"bili-opus-variable-go404",
true,
void 0,
"go404函数置空,可禁止前往404页面"
),
UISwitch(
"handleFallback",
"bili-opus-variable-handleFallback",
true,
void 0,
"禁止前往404页面"
)
]
}
]
},
{
text: "覆盖点击事件",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"话题",
"bili-opus-cover-topicJump",
true,
void 0,
"点击话题正确跳转"
),
UISwitch(
"header用户",
"bili-opus-cover-header",
true,
void 0,
"点击内容上的发布本动态的用户正确跳转个人空间"
)
]
}
]
}
]
}
]
};
const SettingUIDynamic = {
id: "panel-dynamic",
title: "动态",
isDefault() {
return BilibiliRouter.isDynamic();
},
forms: [
{
text: "",
type: "forms",
forms: [
{
text: "覆盖点击事件",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"话题",
"bili-dynamic-cover-topicJump",
true,
void 0,
"点击话题正确跳转"
),
UISwitch(
"header用户",
"bili-dynamic-cover-header",
true,
void 0,
"点击内容上的发布本动态的用户正确跳转个人空间"
),
UISwitch(
"@用户",
"bili-dynamic-cover-atJump",
true,
void 0,
"点击@用户正确跳转个人空间"
),
UISwitch(
"引用",
"bili-dynamic-cover-referenceJump",
true,
void 0,
"点击引用的视频|用户正确跳转"
)
]
}
]
}
]
}
]
};
const SettingUIBangumi = {
id: "panel-bangumi",
title: "番剧",
isDefault() {
return BilibiliRouter.isBangumi();
},
forms: [
{
text: "",
type: "forms",
forms: [
{
text: "功能",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"固定缩放倍率",
"bili-bangumi-initialScale",
true,
void 0,
""
)
]
}
]
},
{
text: "ArtPlayer播放器",
type: "deepMenu",
forms: [
{
text: "控件设置",
type: "forms",
forms: [
UISlider(
"controls左右边距",
"bili-bangumi-artplayer-controlsPadding-left-right",
0,
0,
50,
void 0,
(value) => {
return value + "px";
},
"可用于全屏横屏适配屏幕",
1
)
]
},
{
text: "插件",
type: "forms",
forms: [
UISwitch(
"弹幕",
"artplayer-plugin-bangumi-danmaku-enable",
true,
void 0,
"哔哩哔哩 (゜-゜)つロ 干杯~"
),
UISwitch(
"Dash Audio Support",
"artplayer-plugin-bangumi-m4sAudioSupport-enable",
true,
void 0,
"视频类型为dash时,该插件可支持播放音频"
),
UISwitch(
"选集",
"artplayer-plugin-bangumi-epChoose-enable",
true,
void 0,
"当视频播放完毕后会自动连播"
),
UISwitch(
"CC字幕",
"artplayer-plugin-bangumi-cc-subtitle-enable",
true,
void 0,
"字幕支持插件,如果存在繁体字幕,则自动生成简体字幕"
),
UISwitch(
"顶部工具栏",
"artplayer-plugin-bangumi-toptoolbar-enable",
true,
void 0,
"显示视频标题和当前观看人数"
),
UISwitch(
"空降助手",
"artplayer-plugin-bangumi-airborneHelper-enable",
true,
void 0,
"如果获取到的信息中存在空降信息,如跳过片头片尾,那么会自动跳过"
),
UISwitch(
"视频统计信息",
"artplayer-plugin-bangumi-statistics-enable",
true,
void 0,
"用于显示当前视频信息的弹窗"
)
]
},
{
text: "解除区域限制",
type: "forms",
forms: [
UISwitch(
"解锁番剧限制",
"bili-bangumi-unlockAreaLimit",
false,
void 0,
"使用户可以观看区域外版权番剧"
),
UISwitch(
"生成简中字幕",
"bili-bangumi-generateSimpleChineseSubtitle",
true,
void 0,
"根据繁体字幕自动生成简体中文字幕"
)
]
},
{
text: "加速CDN设置",
type: "forms",
forms: [
UISelect(
"UPOS服务器设置",
"bili-bangumi-uposServerSelect",
"",
BilibiliCDNProxy.getUposCDNServerList().map((item) => {
return {
text: item.name,
value: item.host
};
}),
void 0,
"设置解锁番剧的服务器,可加快视频加载速度"
),
UISwitch(
"作用于Audio上",
"bili-bangumi-uposServerSelect-applyAudio",
false,
void 0,
"把m4s类型的audio也进行upos替换"
)
]
},
{
text: "解析服务器 ",
type: "forms",
forms: [
UIInput(
"中国大陆",
"bili-bangumi-proxyApiServer-default",
"",
"用于请求播放地址的代理",
void 0,
"bilibili优化.example.com"
),
UIInput(
"香港",
"bili-bangumi-proxyApiServer-hk",
"",
"用于请求播放地址的代理",
void 0,
"bilibili优化.example.com"
),
UIInput(
"台湾",
"bili-bangumi-proxyApiServer-tw",
"",
"用于请求播放地址的代理",
void 0,
"bilibili优化.example.com"
),
UIInput(
"泰国/东南亚",
"bili-bangumi-proxyApiServer-tha-or-sea",
"",
"用于请求播放地址的代理",
void 0,
"bilibili优化.example.com"
)
]
}
]
},
{
text: "覆盖点击事件",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"【选集】",
"bili-bangumi-cover-clicl-event-chooseEp",
true,
void 0,
"让【选集】的视频列表可点击跳转"
),
UISwitch(
"【其它】",
"bili-bangumi-cover-clicl-event-other",
true,
void 0,
"让【PV&其他】、【预告】、【主题曲】、【香境剧场】等的视频列表可点击跳转"
),
UISwitch(
"【更多推荐】",
"bili-bangumi-cover-clicl-event-recommend",
true,
void 0,
"让【更多推荐】的视频列表可点击跳转"
)
]
}
]
},
{
text: "劫持/拦截",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"阻止调用App",
"bili-bangumi-hook-callApp",
true,
void 0,
""
)
]
}
]
}
]
}
]
};
const SettingUISearch = {
id: "panel-search",
title: "搜索",
isDefault() {
return BilibiliRouter.isSearch();
},
forms: [
{
type: "forms",
text: "",
forms: [
{
type: "deepMenu",
text: "功能",
forms: [
{
type: "forms",
text: "",
forms: [
UISwitch(
"搜索框自动获取焦点",
"bili-search-inputAutoFocus",
true,
void 0,
""
),
UISwitch(
"美化搜索结果",
"bili-search-beautifySearchResult",
true,
void 0,
"重构搜索结果的样式"
),
UISwitch(
"开启其它地区番剧搜索",
"bili-search-enableOtherAreaSearchBangumi",
false,
void 0,
"在搜索页面添加其它地区番剧搜索结果,需要解析服务器支持"
)
]
},
{
text: "搜索服务器 ",
type: "forms",
forms: [
UIInput(
"香港",
"bili-search-proxyApiServer-hk",
"",
"用于搜索番剧结果的代理",
void 0,
"bilibili优化.example.com"
),
UIInput(
"台湾",
"bili-search-proxyApiServer-tw",
"",
"用于搜索番剧结果的代理",
void 0,
"bilibili优化.example.com"
),
UIInput(
"泰国/东南亚",
"bili-search-proxyApiServer-tha-or-sea",
"",
"用于搜索番剧结果的代理",
void 0,
"bilibili优化.example.com"
)
]
}
]
},
{
type: "deepMenu",
text: "覆盖点击事件",
forms: [
{
type: "forms",
text: "",
forms: [
UISwitch(
"取消",
"bili-search-cover-cancel",
false,
void 0,
"点击取消按钮回退至上一页"
),
UISwitch(
"搜索结果",
"bili-search-cover-card-result-click-event",
true,
void 0,
"修复点击搜索结果不跳转视频的问题"
)
]
}
]
},
{
text: "变量设置",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"noCallApp",
"bili-search-vue-prop-noCallApp",
true,
void 0,
"noCallApp = true"
),
UISwitch(
"openAppDialog",
"bili-search-vue-prop-openAppDialog",
true,
void 0,
"openAppDialog = false"
)
]
}
]
}
]
}
]
};
const SettingUISpace = {
id: "panel-space",
title: "个人空间",
isDefault() {
return BilibiliRouter.isSpace();
},
forms: [
{
text: "",
type: "forms",
forms: [
{
text: "功能",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"修复正确跳转",
"bili-space-repairRealJump",
true,
void 0,
"修复视频|动态的正确跳转,避免跳转404"
)
]
}
]
},
{
text: "覆盖点击事件",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"动态视频",
"bili-space-coverDynamicStateCardVideo",
true,
void 0,
"点击发布动态的视频可正常跳转至该视频"
)
]
}
]
}
]
}
]
};
const SettingUILive = {
id: "panel-live",
title: "直播",
isDefault() {
return BilibiliRouter.isLive();
},
forms: [
{
text: "",
type: "forms",
forms: [
{
text: "屏蔽",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"【屏蔽】聊天室",
"bili-live-block-chatRoom",
false,
void 0,
"直接不显示底部的聊天室"
),
UISwitch(
"【屏蔽】xxx进入直播间",
"bili-live-block-brush-prompt",
false,
void 0,
"直接不显示底部的xxx进入直播间"
),
UISwitch(
"【屏蔽】控制面板",
"bili-live-block-control-panel",
false,
void 0,
"屏蔽底部的发个弹幕、送礼"
)
]
}
]
},
{
text: "劫持/拦截",
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
"阻止open-app-btn元素点击事件触发",
"bili-live-prevent-openAppBtn",
true,
void 0,
"开启后可不跳转至唤醒App页面"
)
]
}
]
}
]
}
]
};
const SettingUITopicDetail = {
id: "panel-topic-detail",
title: "话题",
isDefault() {
return BilibiliRouter.isTopicDetail();
},
forms: []
};
PanelContent.addContentConfig([
SettingUICommon,
SettingUIHead,
SettingUIVideo,
SettingUIOpus,
SettingUIDynamic,
SettingUIBangumi,
SettingUITopicDetail,
SettingUISearch,
SettingUISpace,
SettingUILive
]);
PanelMenu.addMenuOption([
{
key: "go_to_login",
text: "🛠 前往登录",
autoReload: false,
isStoreValue: false,
showText(text) {
return text;
},
callback() {
BilibiliUtils.goToLogin();
}
},
{
key: "go_to_login_to_parse_access_key",
text: "🛠 扫码并解析access_key",
autoReload: false,
isStoreValue: false,
showText(text) {
return text;
},
callback() {
BilibiliQrCodeLogin.init();
}
}
]);
Panel.init();
Bilibili.init();
__pops.config.cssText.index += /*css*/
`
/* bilibili颜色 #FB7299 */
.pops{
--bili-color: #FB7299;
--bili-color-rgb: 251, 114, 153;
}
` ;
__pops.config.cssText.panelCSS += /*css*/
`
.pops-slider{
--pops-slider-main-bg-color: var(--bili-color);
--pops-slider-color-primary: var(--bili-color);
}
aside.pops-panel-aside .pops-is-visited, aside.pops-panel-aside ul li:hover{
color: rgb(var(--bili-color-rgb));
background: rgba(var(--bili-color-rgb), 0.1);
}
/* switch的 */
.pops-panel-switch.pops-panel-switch-is-checked span.pops-panel-switch__core{
border-color: rgb(var(--bili-color-rgb),var(--pops-bd-opacity));
background-color: rgb(var(--bili-color-rgb),var(--pops-bg-opacity));
}
.pops button[type="primary"],
.pops button[type="primary"]:active ,
.pops button[type="primary"]:hover{
--button-color: #ffffff;
--button-bd-color: var(--bili-color);
--button-bg-color: var(--bili-color);
}
` ;
})(Qmsg, DOMUtils, Utils, pops, MD5, Artplayer, artplayerPluginDanmuku, Viewer, MD5);