Bilibili净化

屏蔽B站的广告和推广视频,优化页面布局样式。

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Bilibili净化
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  屏蔽B站的广告和推广视频,优化页面布局样式。
// @author       wang1122
// @match        *://www.bilibili.com/*
// @match        *://t.bilibili.com/*
// @match        *://search.bilibili.com/*
// @icon         https://www.bilibili.com/favicon.ico
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // 添加布局相关样式
    GM_addStyle(`
        /* 布局相关样式 */
        .bili-header__channel {
            height: 20px !important;
            min-height: 20px !important;
        }

        .bili-header__bar {
            display: flex !important;
            justify-content: flex-end !important;
            width: 100% !important;
        }

        /* 导航栏样式 */
        .right-entry {
            margin-left: auto !important;
            display: flex !important;
            align-items: center !important;
        }

        .inner-logo .logo-img {
            display: flex !important;
            align-items: center !important;
            height: 36px !important;
            width: auto !important;
        }

    `);

    const AD_SELECTORS = {
        common: [
            '.adcard-content',                 // 主页右侧广告
            '.recommended-swipe',              // 主页轮播广告
            '.left-entry',                     // 主页左侧导航栏
            '.trending',                       // 搜索栏热搜
            '.channel-icons',                  // 导航栏左侧图标
            '.right-channel-container',        // 导航栏右侧容器
            '.floor-single-card',              // 视频卡片
            '.header-channel',                 // 顶部导航栏
            '.flexible-roll-btn',              // 右下角刷新内容按钮
            '.desktop-download-tip',           // 下载桌面端弹窗
            '.new-charge-btn',                 // 充电按钮
            'slide_ad',                        // 视频页右侧广告
            '.video-card-ad-small',            // 视频页右侧广告
            '.video-page-special-card-small',  // 视频页右侧广告
            '.ad-report',                      // 视频推荐上下的广告
            '.pop-live-small-mode',            // 推荐视频下方直播
            '.activity-m-v1',                  // 视频页标签下方广告
            '.video-ai-assistant',             // AI助手
            '#biliMainFooter',                 // 搜索页底部广告
        ],
        dynamicPage: [
            '.bili-dyn-ads',                   // 动态页右侧广告
            '.sticky',                         // 动态页右侧话题
        ],
        shadowDOM: {
            commentAd: {
                path: ['#commentapp > bili-comments', '#header > bili-comments-header-renderer', '#notice'],
                description: '评论区顶部广告',
            },
        },
    };

    const utils = {
        hideElement(element) {
            if (element) {
                element.style.display = 'none';
            }
        },

        querySelectorAll(selector) {
            return Array.from(document.querySelectorAll(selector));
        },

        processShadowDOM(pathSelectors) {
            let currentElement = document;
            for (const selector of pathSelectors) {
                if (!currentElement) break;
                currentElement = currentElement instanceof Document
                    ? currentElement.querySelector(selector)
                    : currentElement.shadowRoot?.querySelector(selector);
            }
            return currentElement;
        },
    };

    const adHandler = {
        hideCommonAds() {
            AD_SELECTORS.common.forEach(selector => {
                const elements = selector.startsWith('.')
                    ? document.getElementsByClassName(selector.substring(1))
                    : [document.getElementById(selector.replace('#', ''))];

                Array.from(elements).forEach(utils.hideElement);
            });
        },

        hideDynamicPageAds() {
            if (window.location.hostname === 't.bilibili.com') {
                AD_SELECTORS.dynamicPage.forEach(selector => {
                    const elements = document.querySelectorAll(selector);
                    Array.from(elements).forEach(utils.hideElement);
                });
            }
        },

        hideShadowDOMAds() {
            Object.values(AD_SELECTORS.shadowDOM).forEach(({ path }) => {
                const element = utils.processShadowDOM(path);
                utils.hideElement(element);
            });
        },

        hideAdVideoCard() {
            const hostname = window.location.hostname;

            if (hostname === 'www.bilibili.com') {
                utils.querySelectorAll('.feed-card').forEach(card => {
                    if (!card.querySelector('.enable-no-interest')) {
                        utils.hideElement(card);
                    }
                });

                Array.from(document.getElementsByClassName('bili-video-card')).forEach(card => {
                    if (!card.classList.contains('enable-no-interest')) {
                        utils.hideElement(card);
                    }
                });
            } else if (hostname === 'search.bilibili.com') {
                utils.querySelectorAll('.col_3.col_xs_1_5.col_md_2.col_xl_1_7.mb_x40').forEach(card => {
                    if (card.querySelector('a[href^="//cm.bilibili.com"]')) {
                        utils.hideElement(card);
                    }
                });
            }
        },
    };

    const logoHandler = {
        move() {
            const logo = document.querySelector('.bili-header__banner .inner-logo');
            const searchContainer = document.querySelector('.bili-header__bar .center-search-container');

            if (logo && searchContainer) {
                const logoContainer = document.createElement('div');
                logoContainer.style.marginRight = '20px';
                logoContainer.appendChild(logo);
                searchContainer.parentNode.insertBefore(logoContainer, searchContainer);
            }
        },
    };

    function main() {
        adHandler.hideCommonAds();
        adHandler.hideDynamicPageAds();
        adHandler.hideShadowDOMAds();
        adHandler.hideAdVideoCard();
    }

    document.addEventListener('DOMContentLoaded', main);

    const mainObserver = new MutationObserver(main);
    mainObserver.observe(document.documentElement, {
        childList: true,
        subtree: true,
    });

    const logoObserver = new MutationObserver(logoHandler.move);
    logoObserver.observe(document.documentElement, {
        childList: true,
        subtree: true,
    });
})();