// ==UserScript== // @name Bilibili - 优化未登录情况下的移动网页端 // @namespace https://bilibili.com/ // @version 1.1 // @description 优化未登录情况下的移动网页端的使用体验 | V1.1 优化处理视频第30秒处弹窗的方式 // @license GPL-3.0 // @author DD1969 // @match https://m.bilibili.com/* // @icon https://www.bilibili.com/favicon.ico // @require https://update.greasyfork.icu/scripts/475332/1250588/spark-md5.js // @require https://update.greasyfork.icu/scripts/510239/1454424/viewer.js // @require https://update.greasyfork.icu/scripts/524844/1702513/bilibili-mobile-comment-module.js // @require https://update.greasyfork.icu/scripts/512576/1464552/inject-viewerjs-style.js // @require https://update.greasyfork.icu/scripts/512574/1464548/inject-bilibili-comment-style.js // @grant none // @run-at document-end // @downloadURL https://update.greasyfork.icu/scripts/497732/Bilibili%20-%20%E4%BC%98%E5%8C%96%E6%9C%AA%E7%99%BB%E5%BD%95%E6%83%85%E5%86%B5%E4%B8%8B%E7%9A%84%E7%A7%BB%E5%8A%A8%E7%BD%91%E9%A1%B5%E7%AB%AF.user.js // @updateURL https://update.greasyfork.icu/scripts/497732/Bilibili%20-%20%E4%BC%98%E5%8C%96%E6%9C%AA%E7%99%BB%E5%BD%95%E6%83%85%E5%86%B5%E4%B8%8B%E7%9A%84%E7%A7%BB%E5%8A%A8%E7%BD%91%E9%A1%B5%E7%AB%AF.meta.js // ==/UserScript== (async function() { 'use strict'; // no need to continue this script if user already logged in if (document.cookie.includes('DedeUserID')) return; const blacklist = []; // regular expressions const re = { home: /m\.bilibili\.com\/$|m\.bilibili\.com\/channel\/v\/.*/, video: /m\.bilibili\.com\/video\/.*/, search: /m\.bilibili\.com\/search.*/, space: /m\.bilibili\.com\/space\/.*/, dynamic: /m\.bilibili\.com\/dynamic\/.*/, opus: /m\.bilibili\.com\/opus\/.*/, topicDetail: /m\.bilibili\.com\/topic-detail.*/, } // make sure the document is ready await new Promise(resolve => { const timer = setInterval(() => { if (document.head && document.body) { clearInterval(timer); resolve(); } }, 50); }); // search and remove elements constantly setupElementCleaner(); // add style patch addStyle(); // nav bar modifyNavBar(); // home page if (re.home.test(window.location.href)) modifyHomePage(); // video page if (re.video.test(window.location.href)) modifyVideoPage(); // search page if (re.search.test(window.location.href)) modifySearchPage(); // space page if (re.space.test(window.location.href)) modifySpacePage(); // dynamic page if (re.dynamic.test(window.location.href)) modifyDynamicPage(); // opus page if (re.opus.test(window.location.href)) modifyOpusPage(); // topic detail page if (re.topicDetail.test(window.location.href)) modifyTopicDetailPage(); // ------------ functions below ------------ function setupElementCleaner() { const selectors = []; // home page if (re.home.test(window.location.href)) { selectors.push(...[ '.m-nav-openapp', // 右上角的"下载App"按钮 '.m-navbar .face', // 右上角的用户头像 '.fixed-openapp', // 首页底部的打开App横条 '.v-card__stats', // 视频卡片的数据信息 '.reserve-float-btn', // 首页底部的浮动弹窗 ]); } // video page if (re.video.test(window.location.href)) { selectors.push(...[ '.m-nav-openapp', // 右上角的"下载App"按钮 '.m-navbar .face', // 右上角的用户头像 '.video-natural-search .fixed-wrapper', // 顶部遮挡video元素的整个区域 '.m-video-related .list-custom-slot .m-open-app', // 相关视频底部的打开App横条 '.openapp-dialog', // 底部弹出的"浏览方式" '.caution-dialog', // 底部弹出的"友情提示" '.play-page-gotop', // 回到顶部按钮 '.reserve-float-btn', // 底部的浮动弹窗 '.fixed-openapp', // URL带'unique_k'参数时右下角才会出现的打开App按钮 '.gsl-callapp-dom', // 视频模块中阻碍点击并唤起app的元素 'm-open-app.m-video-main-launchapp', // 从b23.tv打开时出现的打开App横条 '.m-video-info', // 从b23.tv打开时出现的视频标题、作者和简介 '.bottom-tab-header', // 从b23.tv打开时出现的下方相关视频header '.m-video-part', // 从b23.tv打开时出现的视频分P模块#1 '.m-video-part-panel', // 从b23.tv打开时出现的视频分P模块#2 ]); } // space page if (re.space.test(window.location.href)) { selectors.push(...[ '.fixed-openapp', // 底部的打开App按钮 '.bili-dyn-item-header__following', // 动态右上方的关注按钮 '.dyn-orig-author__following', // 转发的动态的作者的关注按钮 ]); } // dynamic & opus page if (re.dynamic.test(window.location.href) || re.opus.test(window.location.href)) { selectors.push(...[ '.fixed-openapp', // 底部的打开App横条 '.dyn-header__following', // 动态右上方的关注按钮 '.dyn-share', // 若干分享按钮 '.openapp-dialog', // 底部弹出的"浏览方式" '.reserve-float-btn', // 底部的浮动弹窗 '.opus-module-author__action', // opus页右上角的关注按钮 '.stat-openApp', // opus页(原专栏页)底部的stat模块 ]); } // topic detail page if (re.topicDetail.test(window.location.href)) { selectors.push(...[ '.m-topic-float-openapp', // 底部的打开App按钮 ]); } // start cleaning setInterval(() => { for (const selector of selectors) { document.querySelectorAll(selector).forEach(element => element.remove()); } }, 100); } function addStyle() { // nav bar const navBarCSS = document.createElement('style'); navBarCSS.textContent = ` .m-navbar { position: relative !important; display: flex !important; justify-content: space-between; align-items: center; background: none !important; background-color: #FFFFFF !important; border-bottom: 1px solid #EEEEEE; } .nav-logo { display: flex; align-items: center; height: 100%; } .nav-logo img { height: 60%; } .nav-search { display: flex; flex-direction: column; justify-content: center; width: 200px; height: 28px; border: 1px solid #EEEEEE; border-radius: 16px; } .nav-search > svg { align-self: flex-end; margin-right: 8px; } `; document.head.appendChild(navBarCSS); // video page const videoPageCSS = document.createElement('style'); videoPageCSS.textContent = ` .video-share-loading-mask { position: absolute; top: 0; left: 0; display: none; justify-content: center; align-items: center; width: 100%; height: 100%; z-index: 1999; background-color: #000000; } .video-share-loading-mask-circle { width: 30px; height: 30px; border: 2px solid #000000; border-top-color: #ffffff; border-right-color: #ffffff; border-bottom-color: #ffffff; border-radius: 100%; animation: circle infinite 0.75s linear; } @keyframes circle { 0% { transform: rotate(0); } 100% { transform: rotate(360deg); } } .m-video-player { position: relative !important; top: initial !important; } .video-info { display: flex; flex-direction: column; padding: 20px 12px; } .video-info-title { font-size: 1.2rem; word-break: break-all; } .video-info-author { display: flex; align-items: center; margin-top: 16px; } .video-info-author-avatar { width: 32px; height: 32px; margin-right: 8px; border-radius: 100%; } .video-info-desc { color: #666666; font-size: 0.8rem; word-break: break-all; } .m-video-related { margin-top: 0 !important; padding: 24px 0; border-top: 1px dashed #aaa; border-bottom: 1px dashed #aaa; } .card-box { justify-content: space-between; } .card-box > .card { width: 49%; } .card-box .card .label, .card-box .card .open-app.weakened, .card-box .card .video-card .count { display: none !important; } .card-box .card .title { padding-top: 8px; padding-bottom: 16px; font-size: 0.8rem !important; word-break: break-all; } .gsl-top-return { transform: scale(0.5); } .gsl-buffer-app { display: none !important; } .gsl-control-btn.gsl-control-btn-quality { display: none !important; } .gsl-control-btn.gsl-control-btn-speed { display: flex !important; } .gsl-control-btn.gsl-control-btn-speed .gsl-control-dot { display: none !important; } .playback-rate-setting-panel { 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; } .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; } .episode-container { display: flex; flex-direction: column; background-color: #f1f2f3; } .episode-container-header { display: flex; align-items: center; padding: 12px; padding-bottom: 10px; } .episode-container-header-count { margin-left: 4px; font-size: 0.8rem; font-family: monospace; color: #9499A0; } .episode-list { display: flex; flex-direction: column; padding: 12px; padding-top: 0; } .episode-list-item { display: flex; align-items: center; height: 36px; margin: 2px 0; padding: 2px 6px; } .episode-list-item.is-current-episode { background-color: #ffffff; color: #00AEEC; outline: 1px solid #7bdbfd; border-radius: 4px; } .episode-playing-gif { display: inline-block; width: 12px; height: 12px; margin-right: 5px; background-image: url('https://i0.hdslb.com/bfs/static/jinkela/playlist-video/asserts/playing.gif'); background-repeat: no-repeat; background-size: 12px 12px; background-position: center; } .episode-list-item-title { max-width: 250px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; line-height: 36px; font-size: 0.8rem; } .episode-list-item-duration { flex-grow: 1; text-align: right; font-size: 0.8rem; font-family: monospace; color: #9499A0; } #video-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: #00AEEC; } .open-comment-module-btn { display: flex; justify-content: center; align-items: center; margin: 0 12px 20px 12px; height: 40px; color: #fff; border-radius: 4px; background-color: #00AEEC; } `; document.head.appendChild(videoPageCSS); // space page const spacePageCSS = document.createElement('style'); spacePageCSS.textContent = ` .m-space-info { margin-top: 0 !important; } .bili-dyn-item { border-bottom: 1px solid #e7e7e7; } .video-item-space { box-sizing: border-box; margin-right: 3.2vmin; padding: 2.4vmin 0; height: 24.26667vmin; position: relative; display: block; border-bottom: 1px solid #ddd; } .video-item-space .cover { float: left; width: 31.2vmin; height: 19.46667vmin; position: relative; border-radius: 1.06667vmin; overflow: hidden; } .video-item-space .cover .duration { padding: 0 0.53333vmin; position: absolute; right: 1.06667vmin; bottom: 1.06667vmin; border-radius: 0.53333vmin; background: rgba(0,0,0,.5); font-size: 3.2vmin; color: #fff; } .video-item-space .info { margin-left: 34.4vmin; height: 19.46667vmin; position: relative; } .video-item-space .info .title { max-height: 9.06667vmin; font-size: 3.73333vmin; color: #212121; line-height: 4.53333vmin; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .video-item-space .info .state { display: flex; align-items: center; position: absolute; bottom: 0; left: 0; right: 0; font-size: 2.66667vmin; color: #999; line-height: 4.53333vmin; height: 4.53333vmin; } .video-item-space .info .state .view, .video-item-space .info .state .danmaku { display: flex; align-items: center; } .video-item-space .info .state .danmaku { margin-left: 7.73333vmin; } .video-item-space .info .state .icon { margin-right: 1.06667vmin; } `; document.head.appendChild(spacePageCSS); // opus page const opusPageCSS = document.createElement('style'); opusPageCSS.textContent = ` .show-read-text { max-height: initial !important; } `; document.head.appendChild(opusPageCSS); // topic detail page const topicDetailPageCSS = document.createElement('style'); topicDetailPageCSS.textContent = ` .topic-detail-container { top: 0 !important; } `; document.head.appendChild(topicDetailPageCSS); } async function modifyNavBar() { const timer = setInterval(() => { const navBarElement = document.querySelector('.m-navbar'); if (navBarElement) { const parentElement = navBarElement.parentElement; if (parentElement.tagName === 'M-OPEN-APP') { parentElement.insertAdjacentElement('beforebegin', navBarElement); parentElement.remove(); } navBarElement.__vue__.$destroy(); navBarElement.innerHTML = ` `; clearInterval(timer); } }, 100); } async function modifyHomePage() { // modify video card setInterval(() => { document.querySelectorAll('.card-box .v-card:not(.modified)').forEach(card => { // blacklist check const cardTitle = card.querySelector('.v-card__title'); if (blacklist.some(keyword => cardTitle.textContent.includes(keyword))) { card.remove(); return; } // rewrite behavior when video card being clicked if (card.__vue__?.bvid && card.__vue__?.$destroy) { const bvid = card.__vue__.bvid; card.onclick = () => window.location.href = `https://m.bilibili.com/video/${bvid}`; card.__vue__.$destroy(); card.querySelector('.sleepy')?.classList.remove('sleepy'); card.classList.add('modified'); } }); }, 100); } async function modifyVideoPage() { // show hidden video player const videoShare = await new Promise(resolve => { const timer = setInterval(() => { const videoShare = document.querySelector('.video-share'); if (videoShare) { videoShare.style.display = 'block'; videoShare.style.position = 'relative'; clearInterval(timer); resolve(videoShare); } }, 100); }); // add loading mask const loadingMask = document.createElement('div'); loadingMask.classList.add('video-share-loading-mask'); loadingMask.innerHTML = ''; videoShare.appendChild(loadingMask); // show loading mask when video share being clicked, but only once const videoShareOnClickHandler = () => { videoShare.removeEventListener('click', videoShareOnClickHandler); loadingMask.style.display = 'flex'; // hide the loading mask after the video start playing const timer = setInterval(() => { const progressBar = videoShare.querySelector('.gsl-ui-progress-bar'); if (progressBar && progressBar.style.width && progressBar.style.width !== '0%') { clearInterval(timer); loadingMask.style.display = 'none'; document.querySelector('.gsl-poster')?.remove(); } }, 50); } videoShare.addEventListener('click', videoShareOnClickHandler); // get video info const videoID = window.location.pathname.replace('/video/', ''); let param; if (videoID.startsWith('av')) param = `aid=${videoID.replace('av', '')}`; if (videoID.startsWith('BV')) param = `bvid=${videoID}`; const videoInfo = await fetch(`https://api.bilibili.com/x/web-interface/view?${param}`).then(res => res.json()).then(json => json.data); // add info const infoContainer = document.createElement('div'); infoContainer.classList.add('video-info'); infoContainer.innerHTML = `
${videoInfo.title}
${videoInfo.owner.name}
${videoInfo.desc.replaceAll('\n', '
').replaceAll(/BV[1-9A-HJ-NP-Za-km-z]{10}|av\d+/g, (match) => `${match}`)}
`; document.querySelector('.video-share').insertAdjacentElement('afterend', infoContainer); // add comment module wrapper const commentModuleWrapper = document.createElement('div'); commentModuleWrapper.id = 'video-comment-module-wrapper'; document.body.appendChild(commentModuleWrapper); MobileCommentModule.init(commentModuleWrapper); // add button to close comment module const closeCommentModuleBtn = document.createElement('span'); closeCommentModuleBtn.classList.add('close-comment-module-btn'); closeCommentModuleBtn.textContent = '×'; closeCommentModuleBtn.onclick = function () { // trigger 'popstate' event window.history.back(); } document.body.appendChild(closeCommentModuleBtn); // add button to open comment module const openCommentModuleBtn = document.createElement('div'); openCommentModuleBtn.classList.add('open-comment-module-btn'); openCommentModuleBtn.textContent = '查看评论'; openCommentModuleBtn.onclick = function () { commentModuleWrapper.style.display = 'block'; closeCommentModuleBtn.style.display = 'flex'; document.body.style.overflow = 'hidden'; // push state in history window.history.pushState({}, ''); // close comment module when 'popstate' event triggered const popStateHandler = () => { commentModuleWrapper.style.display = 'none'; closeCommentModuleBtn.style.display = 'none'; document.body.style.overflow = 'initial'; document.querySelectorAll('.reply-item .preview-image-container').forEach(item => item.viewer?.hide(true)); window.removeEventListener('popstate', popStateHandler); } window.addEventListener('popstate', popStateHandler); } infoContainer.insertAdjacentElement('afterend', openCommentModuleBtn); // add episodes const partNum = parseInt((new URLSearchParams(window.location.search)).get('p') || '1'); const episodeContainer = document.createElement('div'); episodeContainer.classList.add('episode-container'); if (videoInfo.pages.length > 1) { episodeContainer.innerHTML = `
视频选集 (${partNum}/${videoInfo.pages.length})
${ videoInfo.pages.map((page, index) => { const isCurrentEpisode = partNum === index + 1; const second = page.duration % 60; const minute = (page.duration - second) / 60 % 60; const hour = Math.floor(page.duration / 3600); const formattedDuration = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}:${String(second).padStart(2, '0')}`; return ` ${isCurrentEpisode ? '' : ''} ${page.part} ${formattedDuration} `; }).join('') }
`; } openCommentModuleBtn.insertAdjacentElement('afterend', episodeContainer); // move .bottom-tab to where it should be, usually happens when page open by b23.tv short url const bottomTab = document.querySelector('.video-share > .bottom-tab'); if (bottomTab) episodeContainer.insertAdjacentElement('afterend', bottomTab); // modify video card setInterval(() => { // move the video card out of document.querySelectorAll('.card-box > m-open-app').forEach(item => { const href = item.getAttribute('universallink').replace('.html?', ''); item.outerHTML = item.querySelector('.card').outerHTML.replace('class="card"', `class="card" data-href="${href}"`).replaceAll('sleepy', ''); }); // setup click event document.querySelectorAll('.card-box > .card:not(.modified)').forEach(item => { // blacklist check const cardTitle = item.querySelector('.title'); if (blacklist.some(keyword => cardTitle.textContent.includes(keyword))) { item.remove(); return; } item.classList.add('modified'); item.dataset.href = item.dataset.href.split('trackid=').shift(); item.onclick = () => window.location.href = item.dataset.href; }); }, 100); // modify playback rate button const timer4PlaybackRateBtn = setInterval(() => { const playbackRateBtn = document.querySelector('.gsl-control .gsl-control-btn-speed'); if (playbackRateBtn) { playbackRateBtn.onclick = () => { const maskElement = document.createElement('div'); maskElement.classList.add('playback-rate-setting-panel'); maskElement.innerHTML = `
播放倍速 5.0x 3.0x 2.0x 1.5x 1.0x 0.5x
`; 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 .gsl-area.gsl-wide') || document.body).appendChild(maskElement); } clearInterval(timer4PlaybackRateBtn); } }, 100); // enable fullscreen button const timer4FullscreenBtn = setInterval(() => { const info = window?.player?.developer?.store?.state?.setting?.mStationSetting?.fullBtnCallAppInfo; if (info) { info.enable = false; clearInterval(timer4FullscreenBtn); } }, 100); // modify ending panel of video const timer4EndingPanel = setInterval(async () => { const endingPanel = document.querySelector('.gsl-end.gsl-show .gsl-ending-panel-video'); if (endingPanel) { clearInterval(timer4EndingPanel); // clean click events endingPanel.innerHTML = `
点击打开
`; // get related video data const relatedVideoData = await fetch(`https://api.bilibili.com/x/web-interface/archive/related?bvid=${videoInfo.bvid}`).then(res => res.json()).then(json => json.data); // start showing let currentIndex; const currentVideoCover = endingPanel.querySelector('.gsl-ending-panel-pic img'); const currentVideoTitle = endingPanel.querySelector('.gsl-ending-panel-title'); const showRelatedVideo = () => { currentIndex = Math.floor(Math.random() * relatedVideoData.length); currentVideoCover.src = relatedVideoData[currentIndex].pic + '@460w_280h.webp'; currentVideoTitle.textContent = relatedVideoData[currentIndex].title; } showRelatedVideo(); setInterval(showRelatedVideo, 5 * 1000); // setup click event endingPanel.onclick = () => window.location.href = `https://m.bilibili.com/video/${relatedVideoData[currentIndex].bvid}`; } }, 100); // modify charge video page const timer4ChargeMask = setInterval(() => { const mask = document.querySelector('.m-video-player m-open-app.charge-mask'); if (mask) { mask.outerHTML = mask.outerHTML.replace('', ''); } }, 100); setTimeout(() => clearInterval(timer4ChargeMask), 3 * 1000); // handle the popup dialog at 00:30 const timer4PlayerStoreEmit = setInterval(() => { const originEmit = window?.player?.videoEvents?.store?.emit; if (originEmit) { window.player.videoEvents.store.emit = function () { if (arguments[0] === 'video_progress_update') arguments[1] = { currentTime: 1.234 }; return originEmit.apply(this, arguments); } clearInterval(timer4PlayerStoreEmit); } }, 100); } async function modifySearchPage() { // modify cancel button const timer4CancelBtn = setInterval(() => { const cancelBtn = document.querySelector('.m-search-search-bar .cancel'); if (cancelBtn) { cancelBtn.outerHTML = cancelBtn.outerHTML.replace('class="cancel"', 'class="cancel" href="/"'); clearInterval(timer4CancelBtn); } }, 100); // modify video card setInterval(() => { document.querySelectorAll('.card-box .v-card-single:not(.vue-destroyed)').forEach(card => { // blacklist check const cardTitle = card.querySelector('.info .title'); const cardAuthor = card.querySelector('.info .author'); if (blacklist.some(keyword => cardTitle.textContent.includes(keyword) || cardAuthor.textContent.includes(keyword))) { card.remove(); return; } const aid = card.dataset.aid; // remove card which leading to live streaming if (aid === '0') { card.remove(); return; } // cancel default actions card.classList.add('vue-destroyed'); card.__vue__.$destroy(); // setup click event card.onclick = () => window.location.href = `https://m.bilibili.com/video/av${aid}`; // show covers card.querySelector('.sleepy')?.classList.remove('sleepy'); }); }, 100); // modify user card setInterval(() => { document.querySelectorAll('.card-box a.m-search-user-item:not(.vue-destroyed)').forEach(card => { card.classList.add('vue-destroyed'); card.__vue__.$destroy(); card.href = card.href.replace('?from=search', ''); card.querySelector('.sleepy')?.classList.remove('sleepy'); }); }, 100); } async function modifySpacePage() { // setup click event on user avatar if live streaming const timer4LiveStreaming = setInterval(() => { const face = document.querySelector('.m-space-info .info-main .face:not(.modified):has(.living-wrapper)'); const liveRoomID = window.__INITIAL_STATE__?.space?.info?.live_room?.roomid; if (face && liveRoomID) { face.classList.add('modified'); face.onclick = () => window.location.href = `https://live.bilibili.com/${liveRoomID}`; } }, 100); setTimeout(() => clearInterval(timer4LiveStreaming), 3 * 1000); // deactivate follow button const timer4FollowBtn = setInterval(() => { const followBtn = document.querySelector('.m-space-info .follow-btn'); if (followBtn) { followBtn.__vue__.$destroy(); clearInterval(timer4FollowBtn); } }, 100); // modify dynamic items setInterval(() => { document.querySelectorAll('.dynamic-list .list-scroll-content-wrap > m-open-app').forEach(item => { const dynItem = item.querySelector('.bili-dyn-item'); dynItem.onclick = () => window.location.href = item.getAttribute('universallink'); item.insertAdjacentElement('beforebegin', dynItem); item.remove(); }); }, 100); // modify archive list const timer4ArchiveList = setInterval(async () => { const archiveList = document.querySelector('.archive-list'); if (archiveList) { clearInterval(timer4ArchiveList); // get user id const mid = window.location.pathname.replace('/space/', ''); const getPaginationData = async (pn) => { const params = { mid, pn, ps: 50, order: 'senddate', wts: Math.floor(Date.now() / 1000) }; return fetch(`https://api.bilibili.com/x/space/wbi/arc/search?${await getWbiQueryString(params)}`, { credentials: 'include' }).then(res => res.json()).then(json => json.data.list?.vlist || []); }; const getVideoItem = (videoData) => { return `
${videoData.title}
${videoData.length}

${videoData.title}

${videoData.play < 10000 ? videoData.play : (parseInt(videoData.play) / 10000).toFixed(1) + '万'} ${videoData.video_review}
`; }; const addAnchor = (container) => { const anchorElement = document.createElement('div'); anchorElement.classList.add('anchor-for-loading'); anchorElement.style = `margin-right: 3.2vmin; padding: 10vmin 0; text-align: center; font-size: 0.8rem; color: #61666d;`; anchorElement.textContent = '正在加载...'; container.appendChild(anchorElement); let paginationCounter = 1; const ob = new IntersectionObserver(async (entries) => { if (!entries[0].isIntersecting) return; const newPaginationData = await getPaginationData(++paginationCounter); if (newPaginationData instanceof Array && newPaginationData.length === 0) { anchorElement.textContent = '所有投稿已加载完毕'; ob.disconnect(); return; } for (const videoData of newPaginationData) { anchorElement.insertAdjacentHTML('beforebegin', getVideoItem(videoData)); } }); ob.observe(anchorElement); }; // get first pagination data const firstPaginationData = await getPaginationData(1); // return if there are no video archives if (firstPaginationData instanceof Array && firstPaginationData.length === 0) return; // clear archive list, then add video item archiveList.innerHTML = ''; for (const videoData of firstPaginationData) { archiveList.insertAdjacentHTML('beforeend', getVideoItem(videoData)); } // add anchor to the bottom addAnchor(archiveList); } }, 100); } async function modifyDynamicPage() { // if still exists 1 second after opening the page, reload the page const noMoreOpenAppPromise = new Promise(resolve => { setTimeout(() => { if (document.querySelector('m-open-app')) window.location.reload(); else resolve(); }, 1000); }); // get cleaned dynamic card const dynamicCard = await new Promise(resolve => { const timer = setInterval(() => { const dynamicCardWrapper = document.querySelector('m-open-app.card-wrap'); if (dynamicCardWrapper) { clearInterval(timer); const dynamicCard = dynamicCardWrapper.querySelector('.dyn-card'); dynamicCardWrapper.insertAdjacentElement('beforebegin', dynamicCard); dynamicCardWrapper.remove(); resolve(dynamicCard); } }, 100); }); // setup click event on author avatar and name const spaceURL = dynamicCard.querySelector('.dyn-header').dataset.url; dynamicCard.querySelector('.dyn-header .dyn-header__author__avatar').onclick = () => window.location.href = spaceURL; dynamicCard.querySelector('.dyn-header .dyn-header__author__name').onclick = () => window.location.href = spaceURL; // setup click event on topic(original or referenced) dynamicCard.querySelectorAll('.dyn-content .bili-dyn-topic').forEach(item => { item.onclick = () => window.location.href = item.dataset.url; }); // setup click event on archive(original or referenced) dynamicCard.querySelectorAll('.dyn-content .dyn-archive').forEach(item => { item.onclick = () => window.location.href = `https://m.bilibili.com/video/${item.dataset.oid}`; }); // setup click event on rich-text-topic dynamicCard.querySelectorAll('.dyn-content .bili-richtext .bili-rich-text-topic').forEach(item => { item.onclick = () => window.location.href = item.dataset.url; }); // setup click event on rich-text-at dynamicCard.querySelectorAll('.dyn-content .bili-richtext .bili-rich-text-module.at').forEach(item => { item.onclick = () => window.location.href = `https://m.bilibili.com/space/${item.dataset.oid}`; }); // setup click event on rich-text-link dynamicCard.querySelectorAll('.dyn-content .bili-richtext .bili-rich-text-link:not(.viewpic)').forEach(item => { item.onclick = () => window.location.href = item.dataset.url; }); // setup click event on rich-text-link for viewing image const viewPicElements = dynamicCard.querySelectorAll('.dyn-content .bili-richtext .bili-rich-text-link.viewpic'); if (viewPicElements.length > 0) { let code, dynamicDetail; const dynamicID = window.location.pathname.replace('/dynamic/', ''); const parseResult = JSON.parse(window.localStorage.getItem('dynamicDetail')); if (parseResult && parseResult.data.item.id_str === dynamicID) { code = parseResult.code; dynamicDetail = parseResult.data; } else { const fetchResult = await fetch(`https://api.bilibili.com/x/polymer/web-dynamic/v1/detail?id=${dynamicID}`).then(res => res.json()); code = fetchResult.code; dynamicDetail = fetchResult.data; if (code === 0) window.localStorage.setItem('dynamicDetail', JSON.stringify(fetchResult)); } if (code === 0) { const viewPicNodes = [].concat( dynamicDetail.item.modules?.module_dynamic?.desc?.rich_text_nodes?.filter(node => node.type === 'RICH_TEXT_NODE_TYPE_VIEW_PICTURE') || [], dynamicDetail.item.orig?.modules?.module_dynamic?.desc?.rich_text_nodes?.filter(node => node.type === 'RICH_TEXT_NODE_TYPE_VIEW_PICTURE') || [], ); viewPicElements.forEach((viewPicElement, index) => { viewPicElement.insertAdjacentHTML('beforeend', `
${viewPicNodes[index].pics.map(pic => ``).join('')}
`); const viewerInstance = new Viewer(viewPicElement, { title: false, toolbar: false, tooltip: false, keyboard: false }); viewPicElement.onclick = () => viewerInstance.show(); }); } } // setup image viewer on pics-block dynamicCard.querySelectorAll('.dyn-content .bm-pics-block').forEach(item => { new Viewer(item, { title: false, toolbar: false, tooltip: false, keyboard: false, url: (image) => image.src.slice(0, image.src.lastIndexOf('@')) }); }); // setup click event on goods card dynamicCard.querySelectorAll('.dyn-content .dyn-goods .dyn-goods__card > [data-url]').forEach(item => { item.onclick = () => window.location.href = item.dataset.url; }); // setup click event on goods list item dynamicCard.querySelectorAll('.dyn-content .dyn-goods .dyn-goods__card .dyn-goods__list__item > img').forEach(item => { item.onclick = () => window.location.href = item.dataset.url; }); // setup click event on live card dynamicCard.querySelectorAll('.dyn-content .dyn-live__card').forEach(item => { item.onclick = () => window.location.href = item.dataset.url; }); // setup click event on additional common card dynamicCard.querySelectorAll('.dyn-content .dyn-add-common').forEach(item => { item.onclick = () => window.location.href = item.dataset.url; }); // setup click event on reference author const referenceAuthor = dynamicCard.querySelector('.dyn-content .reference .dyn-orig-author'); if (referenceAuthor) { referenceAuthor.querySelector('.dyn-orig-author__following').remove(); referenceAuthor.onclick = () => window.location.href = referenceAuthor.dataset.url; } // setup click event on reference article const referenceArticle = dynamicCard.querySelector('.dyn-content .reference .dyn-article'); if (referenceArticle) { referenceArticle.onclick = async () => { let code, dynamicDetail; const dynamicID = window.location.pathname.replace('/dynamic/', ''); const parseResult = JSON.parse(window.localStorage.getItem('dynamicDetail')); if (parseResult && parseResult.data.item.id_str === dynamicID) { code = parseResult.code; dynamicDetail = parseResult.data; } else { const fetchResult = await fetch(`https://api.bilibili.com/x/polymer/web-dynamic/v1/detail?id=${dynamicID}`).then(res => res.json()); code = fetchResult.code; dynamicDetail = fetchResult.data; if (code === 0) window.localStorage.setItem('dynamicDetail', JSON.stringify(fetchResult)); } if (code === 0) { const jump_url = dynamicDetail.item.orig?.modules?.module_dynamic?.major?.article?.jump_url; if (jump_url) window.location.href = jump_url; } } } // setup comment module const timer4CommentModule = setInterval(() => { const wrapper = document.querySelector('.m-dynamic > .v-switcher'); if (wrapper) { clearInterval(timer4CommentModule); wrapper.className = 'comment-module-wrapper'; wrapper.style = `background-color: #fff; overflow-x: hidden;`; wrapper.innerHTML = ''; noMoreOpenAppPromise.then(_ => MobileCommentModule.init(wrapper)); } }, 100); } async function modifyOpusPage() { // if still exists 1 second after opening the page, reload the page const noMoreOpenAppPromise = new Promise(resolve => { setTimeout(() => { if (document.querySelector('m-open-app')) window.location.reload(); else resolve(); }, 1000); }); // get data of opus modules const opusModules = await new Promise(resolve => { const timer = setInterval(() => { const opusModules = window.__INITIAL_STATE__?.opus?.detail?.modules; if (opusModules instanceof Array && opusModules.length !== 0) { clearInterval(timer); resolve(opusModules); } }, 100); }); // remove opus content read more limit const timer4OpusReadMoreLimit = setInterval(() => { document.querySelectorAll('.opus-module-content.limit').forEach(item => item.classList.remove('limit')); document.querySelectorAll('.opus-read-more').forEach(item => item.remove()); }, 100); setTimeout(() => clearInterval(timer4OpusReadMoreLimit), 3 * 1000); // setup image viewer on top album document.querySelectorAll('.opus-module-top .opus-module-top__album').forEach(item => { let previousImageAmount = item.querySelectorAll('.v-swipe__item img').length; const viewerInstance = new Viewer(item, { title: false, toolbar: false, tooltip: false, keyboard: false, url: (image) => image.src.slice(0, image.src.lastIndexOf('@')) }); setInterval(() => { const currentImageAmount = item.querySelectorAll('.v-swipe__item img').length; if (currentImageAmount > previousImageAmount) { previousImageAmount = currentImageAmount; viewerInstance.update(); } }, 500); }); // get cleaned author avatar and name const wrappedAuthorAvatar = document.querySelector('m-open-app.opus-module-author__avatar'); const wrappedAuthorName = document.querySelector('m-open-app.opus-module-author__name'); wrappedAuthorAvatar.outerHTML = wrappedAuthorAvatar.outerHTML.replace(' module.module_type === 'MODULE_TYPE_AUTHOR')?.module_author; authorAvatar.onclick = () => window.location.href = authorData.jump_url; authorName.onclick = () => window.location.href = authorData.jump_url; // clean and setup click event on topic const timer4Topic = setInterval(() => { const wrapper = document.querySelector('m-open-app.opus-module-topic'); if (wrapper) { wrapper.outerHTML = wrapper.outerHTML.replace(' module.module_type === 'MODULE_TYPE_TOPIC')?.module_topic?.jump_url; if (jump_url) document.querySelector('.opus-module-topic').onclick = () => window.location.href = jump_url; } }, 100); setTimeout(() => clearInterval(timer4Topic), 3 * 1000); // clean and setup click event on rich-text-at document.querySelectorAll('m-open-app > .opus-text-rich-hl.at').forEach(item => { const wrapper = item.parentElement; wrapper.insertAdjacentElement('beforebegin', item); wrapper.remove(); const rid = opusModules.find(module => module.module_type === 'MODULE_TYPE_CONTENT')?.module_content?.paragraphs.find(paragraph => paragraph.para_type === 1)?.text?.nodes.find(node => node.type === 'TEXT_NODE_TYPE_RICH' && node?.rich?.type === 'RICH_TEXT_NODE_TYPE_AT' && node?.rich?.text === item.textContent)?.rich?.rid; if (rid) item.onclick = () => window.location.href = `https://m.bilibili.com/space/${rid}`; }); // clean and setup click event on rich-text-topic document.querySelectorAll('m-open-app > .opus-text-rich-hl.topic').forEach((item, index) => { const wrapper = item.parentElement; wrapper.insertAdjacentElement('beforebegin', item); wrapper.remove(); const jump_url = opusModules.find(module => module.module_type === 'MODULE_TYPE_CONTENT')?.module_content?.paragraphs.find(paragraph => paragraph.para_type === 1)?.text?.nodes.filter(node => node.type === 'TEXT_NODE_TYPE_RICH' && node?.rich?.type === 'RICH_TEXT_NODE_TYPE_TOPIC').at(index)?.rich?.jump_url; if (jump_url) item.onclick = () => window.location.href = jump_url; }); // clean and setup click event on rich-text-link document.querySelectorAll('m-open-app > .opus-text-rich-hl.link').forEach((item, index) => { const wrapper = item.parentElement; wrapper.insertAdjacentElement('beforebegin', item); wrapper.remove(); const jump_url = opusModules.find(module => module.module_type === 'MODULE_TYPE_CONTENT')?.module_content?.paragraphs.find(paragraph => paragraph.para_type === 1)?.text?.nodes.filter(node => node.type === 'TEXT_NODE_TYPE_RICH' && node?.rich?.type === 'RICH_TEXT_NODE_TYPE_WEB').at(index)?.rich?.jump_url; if (jump_url) item.onclick = () => window.location.href = jump_url; }); // clean and setup click event on rich-text-goods document.querySelectorAll('m-open-app > .opus-text-rich-hl.goods').forEach((item, index) => { const wrapper = item.parentElement; wrapper.insertAdjacentElement('beforebegin', item); wrapper.remove(); const jump_url = opusModules.find(module => module.module_type === 'MODULE_TYPE_CONTENT')?.module_content?.paragraphs.find(paragraph => paragraph.para_type === 1)?.text?.nodes.filter(node => node.type === 'TEXT_NODE_TYPE_RICH' && node?.rich?.type === 'RICH_TEXT_NODE_TYPE_GOODS').at(index)?.rich?.jump_url; if (jump_url) item.onclick = () => window.location.href = jump_url; }); // clean rich-text-lottery document.querySelectorAll('m-open-app > .opus-text-rich-hl.lottery').forEach((item, index) => { const wrapper = item.parentElement; wrapper.insertAdjacentElement('beforebegin', item); wrapper.remove(); }); // setup image viewer on pics-block document.querySelectorAll('.opus-module-content .bm-pics-block').forEach(item => { const wrapper = item.parentElement; if (wrapper.tagName === 'M-OPEN-APP') { wrapper.insertAdjacentElement('beforebegin', item); wrapper.remove(); } new Viewer(item, { title: false, toolbar: false, tooltip: false, keyboard: false, url: (image) => image.src.slice(0, image.src.lastIndexOf('@')) }); }); // clean reserve card and setup click event const timer4ReserveCard = setInterval(() => { const reserveCard = document.querySelector('m-open-app > .bm-link-card-reserve'); if (reserveCard) { const wrapper = reserveCard.parentElement; wrapper.insertAdjacentElement('beforebegin', reserveCard); wrapper.remove(); const jump_url = opusModules.find(module => module.module_type === 'MODULE_TYPE_CONTENT')?.module_content?.paragraphs.find(paragraph => paragraph.para_type === 6)?.link_card?.card?.reserve?.jump_url; if (jump_url) reserveCard.onclick = () => window.location.href = jump_url; } }, 100); setTimeout(() => clearInterval(timer4ReserveCard), 3 * 1000); // clean goods card and setup click event const timer4GoodsCard = setInterval(() => { const goodsCard = document.querySelector('m-open-app > .bm-link-card-goods'); if (goodsCard) { const wrapper = goodsCard.parentElement; wrapper.insertAdjacentElement('beforebegin', goodsCard); wrapper.remove(); goodsCard.querySelectorAll('.bm-link-card-goods__one[data-url]').forEach(item => { item.onclick = () => window.location.href = item.dataset.url; }); goodsCard.querySelectorAll('.bm-link-card-goods__list__item img').forEach(item => { item.onclick = () => window.location.href = item.dataset.url; }); } }, 100); setTimeout(() => clearInterval(timer4GoodsCard), 3 * 1000); // clean and setup click event on ugc card const timer4UgcCard = setInterval(() => { const ugcCard = document.querySelector('m-open-app > .bm-link-card-ugc'); if (ugcCard) { const wrapper = ugcCard.parentElement; wrapper.insertAdjacentElement('beforebegin', ugcCard); wrapper.remove(); const jump_url = opusModules.find(module => module.module_type === 'MODULE_TYPE_CONTENT')?.module_content?.paragraphs.find(paragraph => paragraph.para_type === 6)?.link_card?.card?.ugc?.jump_url; if (jump_url) ugcCard.onclick = () => window.location.href = jump_url; } }, 100); setTimeout(() => clearInterval(timer4UgcCard), 3 * 1000); // clean and setup click event on common card const timer4CommonCard = setInterval(() => { const commonCard = document.querySelector('m-open-app > .bm-link-card-common'); if (commonCard) { const wrapper = commonCard.parentElement; wrapper.insertAdjacentElement('beforebegin', commonCard); wrapper.remove(); commonCard.onclick = () => window.location.href = commonCard.dataset.url; } }, 100); setTimeout(() => clearInterval(timer4CommonCard), 3 * 1000); // setup comment module const timer4CommentModule = setInterval(() => { const wrapper = document.querySelector('.m-opus .v-switcher'); if (wrapper) { clearInterval(timer4CommentModule); wrapper.className = 'comment-module-wrapper'; wrapper.style = `background-color: #fff; overflow-x: hidden;`; wrapper.innerHTML = ''; noMoreOpenAppPromise.then(_ => MobileCommentModule.init(wrapper)); } }, 100); } async function modifyTopicDetailPage() { const timer4OpenApp = setInterval(() => { document.querySelectorAll('m-open-app').forEach(item => { item.outerHTML = item.outerHTML.replace(' clearInterval(timer4OpenApp), 3 * 1000); } async function getWbiQueryString(params) { // get origin key const { img_url, sub_url } = await fetch('https://api.bilibili.com/x/web-interface/nav').then(res => res.json()).then(json => json.data.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; // get mixin key 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); // generate basic query string const query = Object .keys(params) .sort() // sort properties by key .map(key => { const value = params[key].toString().replace(/[!'()*]/g, ''); // remove characters !'()* in value return `${encodeURIComponent(key)}=${encodeURIComponent(value)}` }) .join('&'); // calculate wbi sign const wbiSign = SparkMD5.hash(query + mixinKey); return query + '&w_rid=' + wbiSign; } })();