Bilibili Live Enhancer

Stream filtering + Gift bar removal + Live counter for Bilibili

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Bilibili Live Enhancer
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Stream filtering + Gift bar removal + Live counter for Bilibili
// @author       Gavin Hon
// @match        https://live.bilibili.com/*
// @grant        none
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Configuration - Stream Filtering
    const LIVE_FILTER_SELECTOR = '.face.living';
    const TARGET_CONTAINER_SELECTOR = '.single';
    const NAME_SELECTOR = '.name';
    const BLOCKED_NAME = '花芽荔枝';
    const COUNTER_SELECTOR = '.mount';
    const COUNTER_PREFIX = '(Live Streaming: ';

    // Configuration - Gift Bar Removal
    const GIFT_BAR_ID = 'gift-control-vm';

    let liveCount = 0;
    let counterElement = null;
    let timer = null;
    const THROTTLE_TIME = 0;

    // Core Functions
    function updateCounter() {
        if (!counterElement) {
            counterElement = document.querySelector(COUNTER_SELECTOR);
            if (!counterElement) return;
        }
        counterElement.textContent = `${COUNTER_PREFIX}${liveCount})`;
    }

    function filterStreamers() {
        liveCount = 0;
        document.querySelectorAll(TARGET_CONTAINER_SELECTOR).forEach(container => {
            // Name filter
            const nameElem = container.querySelector(NAME_SELECTOR);
            if (nameElem?.textContent.includes(BLOCKED_NAME)) {
                container.remove();
                return;
            }

            // Live status check
            const isLive = container.querySelector(LIVE_FILTER_SELECTOR);
            isLive ? liveCount++ : container.remove();
        });

        updateCounter();
    }

    function removeGiftBar() {
        const giftBar = document.getElementById(GIFT_BAR_ID);
        if (giftBar) giftBar.remove();
    }

    // Throttle function
    function throttle(func) {
        if (!timer) {
            func();
            timer = setTimeout(() => {
                timer = null;
            }, THROTTLE_TIME);
        }
    }

    // Unified observer for all DOM changes
    const mainObserver = new MutationObserver((mutations) => {
        throttle(() => {
            filterStreamers();
            removeGiftBar();
        });
    });

    // Initial setup
    function initialize() {
        // Run features immediately
        removeGiftBar();
        filterStreamers();

        // Start observing
        mainObserver.observe(document.body, {
            subtree: true,
            childList: true,
            attributes: false,
            characterData: false
        });

        // Handle SPA navigation
        window.addEventListener('popstate', initialize);
        window.addEventListener('pushstate', initialize);
    }

    // Start the script
    initialize();
})();