您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Cleanup moot UI widgets from bilibili.
// ==UserScript== // @name Clean Bilibili // @namespace https://ntnyq.com // @version 0.0.8 // @description Cleanup moot UI widgets from bilibili. // @author ntnyq (https://ntnyq.com) // @license MIT // @homepageURL https://github.com/ntnyq/clean-bilibili // @supportURL https://github.com/ntnyq/clean-bilibili // @match http*://*.bilibili.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=live.bilibili.com // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @run-at document-body // ==/UserScript== // @ts-check /** * @typedef Logger * @property {(...args: any[]) => void} log log * @property {(...args: any[]) => void} warn warn * @property {(...args: any[]) => void} error error * @property {(...args: any[]) => void} info info */ /** * @typedef {() => boolean} Condition */ ;(function () { 'use strict' const SCRIPT_NAME = 'clean_bilibili' /** * Create logger * @returns {Logger} logger instance */ function createLogger() { const prefix = `[${SCRIPT_NAME}]` return { log: (...args) => console.log(prefix, ...args), warn: (...args) => console.warn(prefix, ...args), error: (...args) => console.error(prefix, ...args), info: (...args) => console.info(prefix, ...args), } } const logger = createLogger() /** * Wait for elements to be ready * @param {string} selector - selector to watch * @returns {Promise<Element[]>} elements */ function waitForElements(selector) { return new Promise(resolve => { if (document.querySelectorAll(selector).length > 0) { resolve(Array.from(document.querySelectorAll(selector))) } const observer = new MutationObserver(() => { if (document.querySelectorAll(selector).length > 0) { resolve(Array.from(document.querySelectorAll(selector))) observer.disconnect() } }) try { observer.observe(document.body, { childList: true, subtree: true, }) } catch { // If failed, try again in 100ms setTimeout(() => { resolve(waitForElements(selector)) }, 100) } }) } /** * Wait until conditions are met * * @param {Condition | Condition[]} conditions - conditions * @param {() => void} callback - callback */ function waitUntil(conditions, callback) { const observer = new MutationObserver(() => { const matchCondition = Array.isArray(conditions) ? conditions.every(condition => condition()) : conditions() if (!matchCondition) return callback() observer.disconnect() }) observer.observe(document.body, { childList: true, subtree: true, }) } /** * Create UI for the options * @param {string} key - option key * @param {string} title - option title * @param {boolean} defaultValue - option default-value * @returns {{ value: boolean }} value ref */ function useOption(key, title, defaultValue) { if (typeof GM_getValue === 'undefined') { return { value: defaultValue, } } let value = GM_getValue(key, defaultValue) const ref = { get value() { return value }, set value(v) { value = v GM_setValue(key, v) location.reload() }, } GM_registerMenuCommand(`${title}: ${value ? '✅' : '❌'}`, () => { ref.value = !value }) return ref } const doc = document // Up 主投稿 创作中心 const HIDE_UP_ENTRY = useOption('bilibili_hide_up_entry', 'Hide UP Entry', true) // 话题 - 动态页面 const HIDE_TOPIC_PANEL = useOption('bilibili_hide_topic_panel', 'Hide Topic Panel', true) // 显示关注时间 const ENABLE_FOLLOW_TIME = useOption('bilibili_enable_follow_time', 'Enable Follow Time', true) /** @type {string[]} */ const HIDE_SELECTORS = [ /** * ================================ * 全局 * ================================ */ // 游戏中心 '.bili-header .left-entry .default-entry[href*="game.bilibili.com"]', // 会员购 '.bili-header .left-entry .default-entry[href*="show.bilibili.com"]', // 漫画 '.bili-header .left-entry .default-entry[href*="manga.bilibili.com"]', // 赛事 '.bili-header .left-entry .default-entry[href*="www.bilibili.com/match"]', // 年度报告 '.bili-header .left-entry .loc-entry.loc-moveclip', // 番剧 '.bili-header .left-entry .default-entry[href*="www.bilibili.com/bangumi/play"]', '.bili-header .left-entry .left-loc-entry a[href*="www.bilibili.com/bangumi/play"]', // 下载客户端 '.bili-header .left-entry .download-entry[href*="app.bilibili.com"]', // 桌面端提示 '.desktop-download-tip', /** * ================================ * 个人动态 * ================================ */ // 轮播广告 '.bili-dyn-ads', /** * ================================ * 视频页面 * ================================ */ // 右侧轮播 '.slide-ad-exp', // also #slide_ad // 游戏推荐 '.video-page-game-card-small', // 评论区顶部推荐 右侧推荐区底部 '.ad-report.ad-floor-exp', /** * ================================ * 用户空间 * ================================ */ // 最近玩儿过的游戏 '.s-space .col-2 .section.game', /** * ================================ * 直播页面 * ================================ */ '#head-info-vm .activity-gather-entry', ...(HIDE_UP_ENTRY.value ? ['.bili-header .right-entry a[href*="member.bilibili.com"]'] : []), ...(HIDE_TOPIC_PANEL.value ? ['.sticky .bili-dyn-topic-box'] : []), ].filter(Boolean) /** @type {string[]} */ const INJECTED_STYLE = [ // 隐藏元素 `${HIDE_SELECTORS.join(',')} { display: none !important;}`, ] async function onUserSpace() { if (!ENABLE_FOLLOW_TIME.value) { return logger.info('Follow time disabled') } if (/^https?:\/\/space\.bilibili\.com\/\d{3,}/.test(location.href)) { const getSpaceMid = () => location.pathname.split('/').at(1) const mid = getSpaceMid() if (!mid) return /** @type any */ let res = await unsafeWindow.fetch( `https://api.bilibili.com/x/space/acc/relation?mid=${mid}`, { credentials: 'include', }, ) res = await res.json() const mtime = /** @type {number?} */ (res?.data?.relation?.mtime) if (!mtime) return logger.error('Failed to get follow time') const result = new Date(mtime * 1000).toLocaleString() const panel = doc.querySelector('.s-space .col-2') if (!panel) return const section = doc.createElement('div') section.classList.add('section') section.innerHTML = ` <span>关注时间:</span> <strong>${result}</strong> ` panel.prepend(section) } } async function hideShadowDOM() { const styleSheet = new CSSStyleSheet() styleSheet.replaceSync('#notice { display: none !important; }') document .querySelector('#commentapp bili-comments') ?.shadowRoot?.querySelector('bili-comments-header-renderer') ?.shadowRoot?.adoptedStyleSheets.push(styleSheet) } async function registerSevices() { waitForElements('#page-dynamic').then(onUserSpace) waitUntil( () => !!document.querySelector('#commentapp bili-comments'), () => { hideShadowDOM() }, ) } /** * Inject style */ function injectStyle() { const el = doc.createElement('style') el.id = `${SCRIPT_NAME}_style` el.innerHTML = INJECTED_STYLE.join('') doc.head.append(el) logger.info('Style injected') } if (doc.readyState === 'loading') { doc.addEventListener('DOMContentLoaded', () => { injectStyle() registerSevices() }) } else { injectStyle() registerSevices() } })()