// ==UserScript==
// @name Bilibili - 优化未登录情况下的移动网页端
// @namespace https://bilibili.com/
// @version 0.2
// @description 优化未登录情况下的移动网页端的使用体验 | V0.2 修复分P无法点击的问题
// @license GPL-3.0
// @author DD1969
// @match https://m.bilibili.com/*
// @icon https://www.bilibili.com/favicon.ico
// @grant none
// @run-at document-end
// ==/UserScript==
(async function() {
'use strict';
// prevent downloading apk or opening external app
Object.defineProperty(document, 'hidden', { get: () => true });
const emptyFunction = () => {};
setInterval(() => {
if (window.PlayerAgent && window.PlayerAgent.openApp !== emptyFunction) {
window.PlayerAgent.openApp = emptyFunction;
}
}, 500);
// add custom style
const styleElement = document.createElement('style');
styleElement.textContent = `
#app .m-navbar .right .face,
#app .m-navbar .right .m-nav-openapp,
.home-float-openapp,
.caution-dialog,
.launch-app-btn.m-video-main-launchapp,
[class*=float-openapp],
#relateRecomMore,
.card > .open-app,
#app .mplayer-widescreen-callapp,
#app .mplayer-fullscreen-call-app,
.visible-open-app-btn,
.share-video-info .title-wrapper .title a.label,
.share-video-info .title-wrapper .icon-spread,
.up .interact-wrapper,
.bottom-tabs,
.list-view__state,
.mplayer-control-btn-speed .mplayer-control-dot,
.mplayer-control-btn-quality .mplayer-control-dot,
#bilibiliPlayer .mplayer-comment-text,
.v-card-toapp .card .label,
.play-page-gotop,
.natural-main-video .main-cover .play-icon,
.tab-item.more {
display: none !important;
}
.m-navbar .right .search {
width: auto !important;
height: 42px !important;
margin-right: 0 !important;
padding: 12px 0 8px 128px;
}
.m-search-together .list {
overflow-x: hidden;
}
.fixed-module {
position: relative !important;
}
.natural-main-video {
padding-bottom: 24px;
flex-direction: column;
}
.natural-main-video .main-cover {
margin-bottom: 20px;
width: 100% !important;
}
.natural-main-video .main-info {
margin: 0;
width: 100%;
}
.natural-main-video .main-info .btn.light {
margin-top: 20px;
width: 100%;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
}
.m-video-part-new .list {
width: 100% !important;
height: 160px;
display: grid;
grid-template-columns: 48% 48%;
grid-row-gap: 8px;
justify-content: space-around;
overflow-y: auto !important;
}
.m-video-part-new .list .part-item {
margin-right: 0 !important;
}
.m-video-part-new .list .part-item span {
width: 100% !important;
display: flex;
align-items: center;
white-space: nowrap !important;
}
.m-video-part-new .spread {
display: none !important;
}
.natural-module > .m-video-related {
margin-top: 0 !important;
padding-top: 16px;
border-top: 1px dashed #AAAAAA;
}
.bottom-tab > .bottom-tab-header {
padding: 8px 0;
}
.v-dialog.natural-dialog:has(.to-see) {
opacity: 0 !important;
}
.m-video-player {
position: relative !important;
top: initial !important;
z-index: 999 !important;
margin-top: 11.73333vmin;
}
.share-video-info {
margin-top: 16px !important;
}
.share-video-info .title-name {
margin-left: 0 !important;
}
.share-video-info h1.title-text {
font-weight: bold !important;
font-size: 16px !important;
}
.m-footer {
margin-top: 40px;
padding-top: 90px;
border-top: 1px dashed #AAAAAA;
}
.playback-rate-option-container {
width: 240px;
padding: 8px;
display: flex;
flex-direction: column;
align-items: center;
background-color: #FFFFFF;
border-radius: 4px;
user-select: none;
}
.playback-rate-option {
width: 100%;
margin-top: 2px;
padding: 8px 0;
color: #FFFFFF;
background-color: #00AEEC;
border-top: 1px solid #EEEEEE;
border-radius: 4px;
text-align: center;
}
`;
document.head.appendChild(styleElement);
// open home video in new tab
setInterval(() => {
const videoCards = Array.from(document.querySelectorAll('.m-home .card-box a.v-card:not(.modified)'));
for (const card of videoCards) {
card.classList.add('modified');
card.setAttribute('target', '_blank');
}
}, 1000);
// open searched video in new tab
setInterval(() => {
const videoCards = Array.from(document.querySelectorAll('.video-list .card-box .v-card-single:not(.modified)'));
for (const card of videoCards) {
const maskElement = document.createElement('div');
maskElement.style = `
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100%;
background-color: #000000;
opacity: 0;
`;
maskElement.addEventListener('click', (e) => {
e.stopPropagation();
if (parseInt(card.dataset.aid) === 0) return;
window.open(`https://m.bilibili.com/video/${av2bv(card.dataset.aid)}`, '_blank');
});
card.style.position = 'relative';
card.classList.add('modified');
card.appendChild(maskElement);
}
}, 1000);
// click cancel btn which appeared after clicking video card in search page
setInterval(() => {
const openAppDialogCancelBtn = document.querySelector('.v-dialog .open-app-dialog .open-app-dialog-btn.cancel');
if (openAppDialogCancelBtn) openAppDialogCancelBtn.click();
}, 100);
// click the "X" mark in the bottom dialog in video page
const timer4CloseBtn = setInterval(() => {
const openAppDialogCloseBtn = document.querySelector('.openapp-dialog .dialog-close');
if (openAppDialogCloseBtn) {
openAppDialogCloseBtn.click();
clearInterval(timer4CloseBtn);
}
}, 100);
// add publish date
setInterval(() => {
const publishDate = document.querySelector('meta[itemprop=datePublished]');
const authorElement = document.querySelector('.natural-module .main-info .author');
if (publishDate && authorElement && !authorElement.textContent.includes(' @ ')) {
authorElement.textContent += ` @ ${publishDate.getAttribute('content')}`;
}
}, 500);
// click the "play now" button on the top
const timer4PlayBtn = setInterval(() => {
const playNowBtn = document.querySelector('.natural-main-video .main-info .btn.light');
if (playNowBtn) {
playNowBtn.onclick = () => {
const timer4ToSeeBtn = setInterval(() => {
const toSeeBtn = document.querySelector('.to-see');
if (toSeeBtn) {
toSeeBtn.click();
clearInterval(timer4ToSeeBtn);
}
}, 100);
}
clearInterval(timer4PlayBtn);
}
}, 100);
// open space by clicking author avatar or name
setInterval(() => {
const upElement = document.querySelector('.share-video-info .up .face');
const upID = window?.__INITIAL_STATE__?.video?.upInfo?.card?.mid;
if (upElement && upID) {
if (upElement.classList.contains('modified')) return;
upElement.classList.add('modified');
upElement.onclick = () => window.open(`https://m.bilibili.com/space/${upID}`, '_blank');
}
}, 1000);
// open recommand video
setInterval(() => {
const videoCards = Array.from(document.querySelectorAll('.m-video-related .card-box .v-card-toapp:not(.modified)'));
for (const card of videoCards) {
card.classList.add('modified');
card.querySelector('a').onclick = () => window.open(`https://m.bilibili.com/video/${av2bv(card.dataset.aid)}`, '_blank');
}
}, 1000);
// open video in space
setInterval(() => {
const cards = Array.from(document.querySelectorAll('.dynamic-list .list .cover .card-content .main > [regstring][type="8"]:not(.modified)'));
for (const card of cards) {
card.classList.add('modified');
card.onclick = () => window.open(`https://m.bilibili.com/video/${av2bv(Number(card.id))}`, '_blank');
}
}, 1000);
// enable playback rate button
const timer4PlaybackRateBtn = setInterval(() => {
const playbackRateBtn = document.querySelector('.mplayer-control-bar .mplayer-control-btn-speed');
if (playbackRateBtn) {
playbackRateBtn.onclick = () => {
const maskElement = document.createElement('div');
maskElement.style = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999999;
`;
maskElement.innerHTML = `
<div class="playback-rate-option-container">
<span style="margin-bottom: 6px; padding: 6px 0;">播放倍速</span>
<span class="playback-rate-option" data-rate="5">5.0x</span>
<span class="playback-rate-option" data-rate="3">3.0x</span>
<span class="playback-rate-option" data-rate="2">2.0x</span>
<span class="playback-rate-option" data-rate="1.5">1.5x</span>
<span class="playback-rate-option" data-rate="1">1.0x</span>
<span class="playback-rate-option" data-rate="0.5">0.5x</span>
</div>
`;
maskElement
.querySelectorAll('.playback-rate-option')
.forEach(optionElement => optionElement.addEventListener('click', function() {
const videoElement = document.querySelector('#bilibiliPlayer video');
if (videoElement) videoElement.playbackRate = parseFloat(this.dataset.rate);
}));
maskElement.onclick = () => maskElement.remove();
(document.querySelector('#bilibiliPlayer > .mplayer.mplayer-wide') || document.body).appendChild(maskElement);
}
clearInterval(timer4PlaybackRateBtn);
}
}, 100);
// av ID to bv ID
function av2bv(avid) {
const BYTES = ["B", "V", 1, "", "", "", "", "", "", "", "", ""];
const BV_LEN = BYTES.length;
const MAX_AID = 1n << 51n;
const XOR_CODE = 23442827791579n;
const BASE = 58n;
const DIGIT_MAP = [0, 1, 2, 9, 7, 5, 6, 4, 8, 3, 10, 11];
const ALPHABET = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf'.split('');
if(typeof avid !== "bigint") avid = BigInt(avid);
const bytes = Array.from(BYTES);
let bv_idx = BV_LEN - 1;
let tmp = (MAX_AID | avid) ^ XOR_CODE;
while (tmp !== 0n) {
let table_idx = tmp % BASE;
bytes[DIGIT_MAP[Number(bv_idx)]] = ALPHABET[Number(table_idx)];
tmp /= BASE;
bv_idx -= 1;
}
return bytes.join("");
}
})();