YouTube Keyword Blocker

Hide YouTube homepage videos by blacklisted words in title or channel name

当前为 2025-05-07 提交的版本,查看 最新版本

// ==UserScript==
// @name         YouTube Keyword Blocker
// @description  Hide YouTube homepage videos by blacklisted words in title or channel name
// @match        https://www.youtube.com/*
// @run-at       document-end
// @version 0.0.1.20250507085225
// @namespace https://greasyfork.org/users/1435046
// ==/UserScript==

(function() {
    'use strict';

    // Hard-coded blacklist (case-insensitive substrings or regexes)
    const blacklist = [

        'trump',
        // add more words or patterns here
    ];

    // CSS selectors for video items on the homepage
    const ITEM_SELECTOR = 'ytd-rich-grid-media, ytd-video-renderer';
    const TITLE_SELECTOR = '#video-title';
    const CHANNEL_SELECTOR = 'ytd-channel-name a';

    // Check an element and hide if matches blacklist
    function filterItem(item) {
        try {
            const titleEl = item.querySelector(TITLE_SELECTOR);
            const channelEl = item.querySelector(CHANNEL_SELECTOR);
            const title = titleEl ? titleEl.textContent.trim().toLowerCase() : '';
            const channel = channelEl ? channelEl.textContent.trim().toLowerCase() : '';

            for (const term of blacklist) {
                const pattern = term.toLowerCase();
                if ((title && title.includes(pattern)) || (channel && channel.includes(pattern))) {
                    item.style.display = 'none';
                    return;
                }
            }
        } catch (e) {
            console.error('Keyword Blocker error', e);
        }
    }

    // Initial scan
    function scanPage() {
        document.querySelectorAll(ITEM_SELECTOR).forEach(filterItem);
    }

    // Observe for dynamically loaded content
    const observer = new MutationObserver(mutations => {
        for (const m of mutations) {
            for (const node of m.addedNodes) {
                if (!(node instanceof HTMLElement)) continue;
                if (node.matches(ITEM_SELECTOR)) {
                    filterItem(node);
                } else {
                    node.querySelectorAll && node.querySelectorAll(ITEM_SELECTOR).forEach(filterItem);
                }
            }
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });

    // Run initial scan after short delay to catch initial feed
    window.addEventListener('yt-navigate-finish', scanPage);
    setTimeout(scanPage, 2000);
})();