Greasy Fork

Bilibili净化

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

// ==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,
    });
})();