// ==UserScript== // @name 【移动端】bilibili优化 // @namespace https://github.com/WhiteSevs/TamperMonkeyScript // @version 2025.7.12 // @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 = `${key}`; 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 += `
${replyData.member.uname} LV${replyData.member.level_info.current_level} ${createrID === replyData.mid ? `` : ""}
${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 = `
共${pageAmount}页 ${currentPageNumber !== 1 ? '上一页' : ""} ${(() => { const left = [ currentPageNumber - 4, currentPageNumber - 3, currentPageNumber - 2, currentPageNumber - 1 ].filter((num) => num >= 1); const right = [ currentPageNumber + 1, currentPageNumber + 2, currentPageNumber + 3, currentPageNumber + 4 ].filter((num) => num <= pageAmount); const merge = [].concat(left, currentPageNumber, right); let chosen; if (currentPageNumber <= 3) chosen = merge.slice(0, 5); else if (currentPageNumber >= pageAmount - 3) chosen = merge.reverse().slice(0, 5).reverse(); else chosen = merge.slice( merge.indexOf(currentPageNumber) - 2, merge.indexOf(currentPageNumber) + 3 ); let final = JSON.parse(JSON.stringify(chosen)); if (!final.includes(1)) { let front = [1]; if (final.at(0) !== 2) front = [1, "..."]; final = [].concat(front, final); } if (!final.includes(pageAmount)) { let back = [pageAmount]; if (final.at(-1) !== pageAmount - 1) back = ["...", pageAmount]; final = [].concat(final, back); } return final.reduce((acc, cur) => { if (cur === "...") return acc + '...'; if (cur === currentPageNumber) return acc + `${cur}`; return acc + `${cur}`; }, ""); })()} ${currentPageNumber !== pageAmount ? '下一页' : ""}
`; 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(() => { document.querySelectorAll( ".video-list .card-box > div:not([data-gm-inject-no-call-app])" ).forEach(($div) => { let vueIns = VueUtils.getVue($div); if (!vueIns) { return; } if (typeof vueIns.noCallApp === "boolean") { Object.defineProperty(vueIns, "noCallApp", { value: true, writable: false, enumerable: true, configurable: true }); $div.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(() => { document.querySelectorAll( ".video-list .card-box > div:not([data-gm-inject-openAppDialog])" ).forEach(($div) => { let vueIns = VueUtils.getVue($div); if (!vueIns) { return; } if (typeof vueIns.openAppDialog === "boolean") { Object.defineProperty(vueIns, "openAppDialog", { value: false, writable: false, enumerable: true, configurable: true }); $div.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(); }); 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(); }); } }; 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}
${likeCount}

${title}

图文

${upName}
` }, { "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*/ `
${title}
${playCount} ${commentCount} ${videoTime}

${title}

${upName}
` ) }, { "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*/ `
${upName}
`; $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, "点击取消按钮回退至上一页" ) ] } ] }, { 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);