// ==UserScript==
// @name 【移动端】bilibili优化
// @namespace https://github.com/WhiteSevs/TamperMonkeyScript
// @version 2024.6.15.13
// @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://update.greasyfork.icu/scripts/497907/1394170/QRCodeJS.js
// @require https://fastly.jsdelivr.net/npm/qmsg@1.1.2/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/@whitesev/utils@1.4.3/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/@whitesev/domutils@1.1.1/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/md5@2.3.0/dist/md5.min.js
// @connect *
// @connect m.bilibili.com
// @connect www.bilibili.com
// @connect api.bilibili.com
// @connect app.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,.openapp-dialog,.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 .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,#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, md5) {
'use strict';
var _a, _b;
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 QRCodeJS = _monkeyWindow.QRCode || _unsafeWindow.QRCode;
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 GMCookie = new utils.GM_Cookie();
const DEBUG = false;
log.config({
debug: DEBUG,
logMaxCount: 1e3,
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(
"修复点击UP主正确进入空间",
"bili-repairEnterUserHome",
true,
void 0,
"可以修复点击UP主进入个人空间但是跳转404的问题"
),
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,
"自动执行初始化播放器"
),
UISwitch(
"强制本页刷新跳转",
"bili-video-forceThisPageToRefreshAndRedirect",
false,
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,
"点击下面的相关视频可正确跳转至该视频"
),
UISwitch(
"选集",
"bili-video-cover-seasonNew",
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: []
};
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-automaticallyExpandToReadFullText",
true,
void 0,
"屏蔽【展开阅读全文】按钮并自动处理全文高度"
)
]
},
{
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: [
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 TVKeyInfo = {
appkey: "4409e2ce8ffd12b8",
appsec: "59b43e04ad6965f34319062b478f83dd"
};
function appSign(params, appkey, appsec) {
params.appkey = appkey;
const searchParams = new URLSearchParams(params);
searchParams.sort();
return md5(searchParams.toString() + appsec);
}
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
* @param [useRouter=false] 是否强制使用Router
*/
goToUrl(path, useRouter = false) {
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 (useRouter) {
isGoToUrlBlank = false;
}
if (isGoToUrlBlank) {
window.open(path, "_blank");
} else {
if (path.startsWith("http") || path.startsWith("//")) {
if (path.startsWith("//")) {
path = window.location.protocol + path;
}
let urlObj = new URL(path);
if (urlObj.origin === window.location.origin) {
path = urlObj.pathname + urlObj.search + urlObj.hash;
} else {
log.info("不同域名,直接本页打开,不用Router:" + path);
window.location.href = path;
return;
}
}
log.info("$router push跳转Url:" + path);
$router.push(path);
}
},
/**
* 前往登录
*/
goToLogin(fromUrl = "") {
window.open(
`https://passport.bilibili.com/h5-app/passport/login?gourl=${encodeURIComponent(
fromUrl
)}`
);
},
/**
* 转换时长为显示的时长
*
* + 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
};
},
/**
* 加载