// ==UserScript== // @name 【移动端】bilibili优化 // @namespace https://github.com/WhiteSevs/TamperMonkeyScript // @version 2024.6.5 // @author WhiteSevs // @description bilibili(哔哩哔哩)优化,免登录等 // @license GPL-3.0-only // @icon https://i0.hdslb.com/bfs/static/jinkela/long/images/512.png // @supportURL https://github.com/WhiteSevs/TamperMonkeyScript/issues // @match *://m.bilibili.com/* // @match *://live.bilibili.com/* // @require https://update.greasyfork.icu/scripts/494167/1376186/CoverUMD.js // @require https://update.greasyfork.icu/scripts/456485/1384984/pops.js // @require https://cdn.jsdelivr.net/npm/qmsg@1.1.0/dist/index.umd.js // @require https://cdn.jsdelivr.net/npm/@whitesev/utils@1.3.6/dist/index.umd.js // @require https://cdn.jsdelivr.net/npm/@whitesev/domutils@1.1.1/dist/index.umd.js // @connect * // @connect m.bilibili.com // @connect www.bilibili.com // @connect api.bilibili.com // @grant GM_addStyle // @grant GM_deleteValue // @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)})(" .m-video2-awaken-btn,.m-head .launch-app-btn.m-nav-openapp,.m-head .launch-app-btn.home-float-openapp,.m-home .launch-app-btn.home-float-openapp,.m-space .launch-app-btn.m-space-float-openapp,.m-space .launch-app-btn.m-nav-openapp{display:none!important}#app .video .openapp-dialog,#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{display:none!important}#app.LIVE .open-app-btn.bili-btn-warp,#app .m-dynamic .launch-app-btn.m-nav-openapp,#app .m-dynamic .dynamic-float-openapp.dynamic-float-btn,#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 .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} "); (function (Qmsg, Utils, DOMUtils) { 'use strict'; var _a; 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 _SCRIPT_NAME_ = "【移动端】bilibili优化"; const utils = Utils.noConflict(); const domutils = DOMUtils.noConflict(); const pops = _monkeyWindow.pops || _unsafeWindow.pops; const log = new utils.Log( _GM_info, _unsafeWindow.console || _monkeyWindow.console ); const SCRIPT_NAME = ((_a = _GM_info == null ? void 0 : _GM_info.script) == null ? void 0 : _a.name) || _SCRIPT_NAME_; const DEBUG = false; log.config({ debug: DEBUG, logMaxCount: 2e4, autoClearConsole: true, tag: true }); Qmsg.config({ position: "bottom", html: true, maxNums: 5, autoClose: true, showClose: false, showReverse: true }); const GM_Menu = new utils.GM_Menu({ GM_getValue: _GM_getValue, GM_setValue: _GM_setValue, GM_registerMenuCommand: _GM_registerMenuCommand, GM_unregisterMenuCommand: _GM_unregisterMenuCommand }); const httpx = new utils.Httpx(_GM_xmlhttpRequest); httpx.config({ logDetails: DEBUG, onabort() { Qmsg.warning("请求取消"); }, ontimeout() { Qmsg.error("请求超时"); }, onerror(response) { Qmsg.error("请求异常"); log.error(["httpx-onerror 请求异常", response]); } }); const OriginPrototype = { Object: { defineProperty: _unsafeWindow.Object.defineProperty }, Function: { apply: _unsafeWindow.Function.prototype.apply, call: _unsafeWindow.Function.prototype.call }, Element: { appendChild: _unsafeWindow.Element.prototype.appendChild }, setTimeout: _unsafeWindow.setTimeout }; const addStyle = utils.addStyle; const KEY = "GM_Panel"; const ATTRIBUTE_KEY = "data-key"; const ATTRIBUTE_DEFAULT_VALUE = "data-default-value"; const UISwitch = function(text, key, defaultValue, clickCallBack, description) { let result = { text, type: "switch", description, attributes: {}, getValue() { return Boolean(PopsPanel.getValue(key, defaultValue)); }, callback(event, value) { log.success(`${value ? "开启" : "关闭"} ${text}`); PopsPanel.setValue(key, Boolean(value)); }, afterAddToUListCallBack: void 0 }; if (result.attributes) { result.attributes[ATTRIBUTE_KEY] = key; result.attributes[ATTRIBUTE_DEFAULT_VALUE] = Boolean(defaultValue); } return result; }; const SettingUICommon = { id: "panel-common", title: "通用", forms: [ { text: "功能", type: "forms", forms: [ UISwitch( "监听路由改变", "bili-listenRouterChange", true, void 0, "用于处理页面跳转时功能不生效问题" ), UISwitch( "新标签页打开", "bili-go-to-url-blank", false, void 0, "通过开启【覆盖点击事件】相关的设置,通过新标签页打开链接" ) ] }, { text: "变量设置", type: "forms", forms: [ UISwitch( "isLogin", "bili-setLogin", true, void 0, "$store.state.common.noCallApp=true
$store.state.common.userInfo.isLogin=true
$store.state.loginInfo.isLogin=true" ), 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" ), UISwitch( "tinyApp", "bili-setTinyApp", true, void 0, "$store.state.common.tinyApp=true" ) ] }, { text: "劫持/拦截", type: "forms", forms: [ UISwitch( "覆盖.launch-app-btn openApp", "bili-overrideLaunchAppBtn_Vue_openApp", true, void 0, "覆盖.launch-app-btn元素上的openApp函数,可阻止点击唤醒/下载App" ), UISwitch( "劫持setTimeout-autoOpenApp", "bili-hookSetTimeout_autoOpenApp", true, void 0, "阻止自动调用App" ) ] } ] }; const BilibiliRouter = { /** * 判断当前路径 * + /video/ */ isVideo() { return window.location.pathname.startsWith("/video/"); }, /** * 判断当前路径 * + /banggumi/ */ isBangumi() { return window.location.pathname.startsWith("/bangumi/"); }, /** * 判断当前路径 * + /search */ isSearch() { return window.location.pathname.startsWith("/search"); }, /** * 判断当前路径 * + 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"); } }; const SettingUIVideo = { id: "panel-video", title: "视频", isDefault() { return BilibiliRouter.isVideo(); }, forms: [ { text: "功能", type: "forms", forms: [ UISwitch( "修复视频底部区域高度", "bili-video-repairVideoBottomAreaHeight", true, void 0, "添加margin-top" ), UISwitch( "自动点击【继续在网页观看】", "bili-video-autoClickContinueToWatchOnTheWebpage", true, void 0, "可避免弹窗出现且自动点击后播放视频" ), UISwitch( "美化显示", "bili-video-beautify", true, void 0, "调整底部推荐视频卡片样式类似哔哩哔哩App" ), UISwitch( "手势返回关闭评论区", "bili-video-gestureReturnToCloseCommentArea", true, void 0, "当浏览器手势触发浏览器回退页面时,关闭评论区" ), UISwitch( "initPlayer", "bili-video-initPlayer", true, void 0, "自动执行初始化播放器" ) ] }, { text: "变量设置", type: "forms", forms: [ UISwitch( "playBtnNoOpenApp", "bili-video-setVideoPlayer", true, void 0, "playBtnNoOpenApp=true
playBtnOpenApp=false
coverOpenApp=false" ), UISwitch( "解锁充电限制", "bili-video-unlockUpower", false, void 0, "is_upower_exclusive=true
is_upower_play=false
is_upower_preview=false" ) ] }, { text: "覆盖点击事件", type: "forms", forms: [ UISwitch( "相关视频", "bili-video-cover-bottomRecommendVideo", true, void 0, "点击下面的相关视频可正确跳转至该视频" ) ] }, { text: "劫持/拦截", type: "forms", forms: [ UISwitch( "阻止调用App", "bili-video-hook-callApp", true, void 0, "处理函数: PlayerAgent" ) ] } ] }; const SettingUIBangumi = { id: "panel-bangumi", title: "番剧", isDefault() { return BilibiliRouter.isBangumi(); }, forms: [ { text: "变量设置", type: "forms", forms: [ UISwitch( "pay", "bili-bangumi-setPay", true, void 0, "$store.state.userStat.pay=1
$store.state.mediaInfo.user_status.pay=1" ) ] }, { 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: "forms", forms: [ UISwitch("阻止调用App", "bili-bangumi-hook-callApp", true, void 0, "") ] } ] }; const SettingUISearch = { id: "panel-search", title: "搜索", isDefault() { return BilibiliRouter.isSearch(); }, forms: [ { text: "功能", type: "forms", forms: [ UISwitch( "修复点击UP主正确进入空间", "bili-search-repair-enter-user-home", true, void 0, "可以修复点击UP主进入个人空间但是是404问题" ) ] } ] }; const SettingUILive = { id: "panel-live", title: "直播", isDefault() { return BilibiliRouter.isLive(); }, 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: "forms", forms: [ UISwitch( "阻止open-app-btn元素点击事件触发", "bili-live-prevent-openAppBtn", true, void 0, "开启后可不跳转至唤醒App页面" ) ] } ] }; const SettingUIOpus = { id: "panel-opus", title: "专栏", isDefault() { return BilibiliRouter.isOpus(); }, forms: [ { text: "覆盖点击事件", type: "forms", forms: [ UISwitch( "话题", "bili-opus-cover-topicJump", true, void 0, "点击话题正确跳转" ) ] } ] }; const SettingUIDynamic = { id: "panel-dynamic", title: "动态", isDefault() { return BilibiliRouter.isDynamic(); }, 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 SettingUITopicDetail = { id: "panel-topic-detail", title: "话题", isDefault() { return BilibiliRouter.isTopicDetail(); }, forms: [] }; const SettingUIHead = { id: "panel-head", title: "首页", forms: [ { text: "功能", type: "forms", forms: [ UISwitch( "美化显示", "bili-head-beautify", true, void 0, "调整瀑布流视频卡片样式类似哔哩哔哩App" ), UISwitch( "补充推荐视频信息", "bili-head-supplementaryVideoStreamingInformation", true, void 0, "给视频添加UP主名,当前视频总时长信息" ) ] } ] }; const PopsPanel = { /** 数据 */ $data: { /** * 菜单项的默认值 */ data: new utils.Dictionary(), /** * 成功只执行了一次的项 */ oneSuccessExecMenu: new utils.Dictionary(), /** * 成功只执行了一次的项 */ onceExec: new utils.Dictionary(), /** 脚本名,一般用在设置的标题上 */ scriptName: SCRIPT_NAME, /** 菜单项的总值在本地数据配置的键名 */ key: KEY, /** 菜单项在attributes上配置的菜单键 */ attributeKeyName: ATTRIBUTE_KEY, /** 菜单项在attributes上配置的菜单默认值 */ attributeDefaultValueName: ATTRIBUTE_DEFAULT_VALUE }, /** 监听器 */ $listener: { /** * 值改变的监听器 */ listenData: new utils.Dictionary() }, init() { this.initPanelDefaultValue(); this.initExtensionsMenu(); }, initExtensionsMenu() { if (_unsafeWindow.top !== _unsafeWindow.self) { return; } GM_Menu.add([ { key: "show_pops_panel_setting", text: "⚙ 设置", autoReload: false, isStoreValue: false, showText(text) { return text; }, callback: () => { this.showPanel(); } } ]); }, /** 初始化本地设置默认的值 */ initPanelDefaultValue() { let that = this; function initDefaultValue(config) { if (!config["attributes"]) { return; } let key = config.attributes[ATTRIBUTE_KEY]; let defaultValue = config["attributes"][ATTRIBUTE_DEFAULT_VALUE]; if (key == null) { log.warn(["请先配置键", config]); return; } if (that.$data.data.has(key)) { log.warn("请检查该key(已存在): " + key); } that.$data.data.set(key, defaultValue); } let contentConfigList = this.getPanelContentConfig(); for (let index = 0; index < contentConfigList.length; index++) { let leftContentConfigItem = contentConfigList[index]; if (!leftContentConfigItem.forms) { continue; } let rightContentConfigList = leftContentConfigItem.forms; for (let formItemIndex = 0; formItemIndex < rightContentConfigList.length; formItemIndex++) { let rightContentConfigItem = rightContentConfigList[formItemIndex]; if (rightContentConfigItem.forms) { let childFormConfigList = rightContentConfigItem.forms; for (let formChildConfigIndex = 0; formChildConfigIndex < childFormConfigList.length; formChildConfigIndex++) { initDefaultValue(childFormConfigList[formChildConfigIndex]); } } else { initDefaultValue(rightContentConfigItem); } } } }, /** * 设置值 * @param key 键 * @param value 值 */ setValue(key, value) { let locaData = _GM_getValue(KEY, {}); let oldValue = locaData[key]; locaData[key] = value; _GM_setValue(KEY, locaData); if (this.$listener.listenData.has(key)) { this.$listener.listenData.get(key).callback(key, oldValue, value); } }, /** * 获取值 * @param key 键 * @param defaultValue 默认值 */ getValue(key, defaultValue) { let locaData = _GM_getValue(KEY, {}); let localValue = locaData[key]; if (localValue == null) { if (this.$data.data.has(key)) { return this.$data.data.get(key); } return defaultValue; } return localValue; }, /** * 删除值 * @param key 键 */ deleteValue(key) { let locaData = _GM_getValue(KEY, {}); let oldValue = locaData[key]; Reflect.deleteProperty(locaData, key); _GM_setValue(KEY, locaData); if (this.$listener.listenData.has(key)) { this.$listener.listenData.get(key).callback(key, oldValue, void 0); } }, /** * 监听调用setValue、deleteValue * @param key 需要监听的键 * @param callback */ addValueChangeListener(key, callback) { let listenerId = Math.random(); this.$listener.listenData.set(key, { id: listenerId, key, callback }); return listenerId; }, /** * 移除监听 * @param listenerId 监听的id */ removeValueChangeListener(listenerId) { let deleteKey = null; for (const [key, value] of this.$listener.listenData.entries()) { if (value.id === listenerId) { deleteKey = key; break; } } this.$listener.listenData.delete(deleteKey); }, /** * 判断该键是否存在 * @param key 键 */ hasKey(key) { let locaData = _GM_getValue(KEY, {}); return key in locaData; }, /** * 自动判断菜单是否启用,然后执行回调 * @param key * @param callback 回调 */ execMenu(key, callback) { if (typeof key !== "string") { throw new TypeError("key 必须是字符串"); } if (!this.$data.data.has(key)) { log.warn(`${key} 键不存在`); return; } let value = PopsPanel.getValue(key); if (value) { callback(value); } }, /** * 自动判断菜单是否启用,然后执行回调,只会执行一次 * @param key * @param callback 回调 */ execMenuOnce(key, callback) { if (typeof key !== "string") { throw new TypeError("key 必须是字符串"); } if (!this.$data.data.has(key)) { log.warn(`${key} 键不存在`); return; } let value = PopsPanel.getValue(key); if (value) { if (this.$data.oneSuccessExecMenu.has(key)) { return; } callback(value); this.$data.oneSuccessExecMenu.set(key, 1); } }, /** * 根据key执行一次 * @param key */ onceExec(key, callback) { if (typeof key !== "string") { throw new TypeError("key 必须是字符串"); } if (this.$data.onceExec.has(key)) { return; } callback(); this.$data.onceExec.set(key, 1); }, /** * 显示设置面板 */ showPanel() { pops.panel({ title: { text: `${SCRIPT_NAME}-设置`, position: "center", html: false, style: "" }, content: this.getPanelContentConfig(), mask: { enable: true, clickEvent: { toClose: true, toHide: false } }, isMobile: this.isMobile(), width: this.getWidth(), height: this.getHeight(), drag: true, only: true }); }, isMobile() { return window.outerWidth < 550; }, /** * 获取设置面板的宽度 */ getWidth() { if (window.outerWidth < 550) { return "92dvw"; } else { return "550px"; } }, /** * 获取设置面板的高度 */ getHeight() { if (window.outerHeight > 450) { return "80dvh"; } else { return "450px"; } }, /** * 获取配置内容 */ getPanelContentConfig() { let configList = [ SettingUICommon, SettingUIHead, SettingUIVideo, SettingUIOpus, SettingUIDynamic, SettingUIBangumi, SettingUITopicDetail, SettingUISearch, SettingUILive ]; return configList; } }; const BilibiliBeautifyCSS = "/* 主页 */\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\r\n background-color: var(--bg-color);\r\n .m-home {\r\n background-color: var(--bg-color);\r\n }\r\n /* 美化视频卡片 */\r\n .video-list .card-box {\r\n .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 .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 .count {\r\n display: flex;\r\n justify-content: safe flex-start;\r\n padding-right: 0;\r\n\r\n .iconfont {\r\n font-size: var(--icon-text-font-size);\r\n }\r\n\r\n > 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 }\r\n /* 视频标题区域 */\r\n .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 /* 两列 => 左边的 */\r\n .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 .v-card:nth-child(2n) {\r\n /*background-color: rebeccapurple;*/\r\n margin-left: calc(var(--pd-width) / 2);\r\n }\r\n }\r\n}\r\n"; const BilibiliUtils = { /** * 获取元素上的__vue__属性 * @param element * @returns */ getVue(element) { return element == null ? void 0 : element.__vue__; }, /** * 等待vue属性并进行设置 */ waitVuePropToSet($target, needSetList) { function getTarget() { let __target__ = null; if (typeof $target === "string") { __target__ = document.querySelector($target); } else if (typeof $target === "function") { __target__ = $target(); } else if ($target instanceof HTMLElement) { __target__ = $target; } return __target__; } needSetList.forEach((needSetOption) => { if (typeof needSetOption.msg === "string") { log.info(needSetOption.msg); } function checkVue() { let target = getTarget(); if (target == null) { return false; } let vueObj = BilibiliUtils.getVue(target); if (vueObj == null) { return false; } let needOwnCheck = needSetOption.check(vueObj); return Boolean(needOwnCheck); } utils.waitVueByInterval( () => { return getTarget(); }, checkVue, 250, 1e4 ).then((result) => { if (!result) { return; } let target = getTarget(); let vueObj = BilibiliUtils.getVue(target); if (vueObj == null) { return; } needSetOption.set(vueObj); }); }); }, /** * 前往网址 * @param path */ goToUrl(path) { let $app = document.querySelector("#app"); if ($app == null) { Qmsg.error("跳转Url: 获取根元素#app失败"); log.error("跳转Url: 获取根元素#app失败:" + path); return; } let vueObj = BilibiliUtils.getVue($app); if (vueObj == null) { log.error("获取#app的vue属性失败"); Qmsg.error("获取#app的vue属性失败"); return; } let $router = vueObj.$router; let isGoToUrlBlank = PopsPanel.getValue("bili-go-to-url-blank"); log.info("即将跳转URL:" + path); if (isGoToUrlBlank) { window.open(path, "_blank"); } else { if (path.startsWith("http") || path.startsWith("//")) { window.location.href = path; } else { $router.push(path); } } }, /** * 转换时长为显示的时长 * * + 30 => 0:30 * + 120 => 2:00 * + 14400 => 4:00:00 * @param duration 秒 */ parseDuration(duration) { if (typeof duration !== "number") { duration = parseInt(duration); } if (isNaN(duration)) { return duration.toString(); } function zeroPadding(num) { if (num < 10) { return `0${num}`; } else { return num; } } if (duration < 60) { return `0:${zeroPadding(duration)}`; } else if (duration >= 60 && duration < 3600) { return `${Math.floor(duration / 60)}:${zeroPadding(duration % 60)}`; } else { return `${Math.floor(duration / 3600)}:${zeroPadding( Math.floor(duration / 60) % 60 )}:${zeroPadding(duration % 60)}`; } }, /** * 手势返回 */ hookGestureReturnByVueRouter(option) { function popstateEvent() { log.success("触发popstate事件"); resumeBack(true); } function banBack() { log.success("监听地址改变"); option.vueObj.$router.history.push(option.hash); domutils.on(window, "popstate", popstateEvent); } async function resumeBack(isFromPopState = false) { domutils.off(window, "popstate", popstateEvent); let callbackResult = option.callback(isFromPopState); if (callbackResult) { return; } while (1) { if (option.vueObj.$router.history.current.hash === option.hash) { log.info("后退!"); option.vueObj.$router.back(); await utils.sleep(250); } else { return; } } } banBack(); return { resumeBack }; }, /** * 加载