Filter YT home feed by video titles (updated selectors)

Filter YouTube home feed by video titles. Use multiple words with AND/OR logic switch.

// ==UserScript==
// @name         Filter YT home feed by video titles (updated selectors)
// @version      0.2
// @description  Filter YouTube home feed by video titles. Use multiple words with AND/OR logic switch.
// @match        https://www.youtube.com/*
// @grant        none
// @license MIT
// @namespace https://greasyfork.org/users/1449358
// ==/UserScript==

(function() {
    'use strict';

    // Define the words you want to filter for (case-insensitive)
    const requiredWords = ['vlog', 'daily'];
    // Set the filter logic: true for AND, false for OR
    const useAndLogic = false; // Change this to true for AND logic

    function escapeRegExp(string) {
        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }

    function makeRegexp(word) {
        const escaped = escapeRegExp(word);
        // Create regex with word boundaries if word is only word chars
        if (/^\w+$/.test(word)) {
            return new RegExp(`\\b${escaped}\\b`, 'i');
        }
        // Otherwise a simple case-insensitive regex
        return new RegExp(escaped, 'i');
    }

    // Precompile regexps for performance
    const wordRegexps = requiredWords.map(makeRegexp);

    function filterVideos() {
        const videos = document.querySelectorAll('ytd-rich-item-renderer');

        videos.forEach(video => {
            // Find title anchor element by class pattern containing "title"
            const titleElement = video.querySelector('a[class*="title"]');
            if (!titleElement) {
                video.style.display = 'none';
                return;
            }
            const titleText = titleElement.textContent || '';
            const title = titleText.trim();

            // Detect playlist by href containing '/playlist?'
            const href = titleElement.getAttribute('href') || '';
            const isPlaylist = href.includes('/playlist?');

            if (isPlaylist || !title) {
                video.style.display = 'none';
                return;
            }

            // Check match based on AND/OR logic with wordRegexps
            let shouldDisplay;
            if (useAndLogic) {
                shouldDisplay = wordRegexps.every(re => re.test(title));
            } else {
                shouldDisplay = wordRegexps.some(re => re.test(title));
            }

            video.style.display = shouldDisplay ? '' : 'none';
        });
    }

    // Set up MutationObserver on the home page feed container for dynamic updates
    function setupObserver() {
        // The container that holds the video items
        const container = document.querySelector('ytd-rich-grid-renderer #contents');
        if (!container) {
            // Retry if container not found yet (page may not be loaded)
            setTimeout(setupObserver, 500);
            return;
        }
        // Observe changes so we re-filter on new videos added
        const observer = new MutationObserver(filterVideos);
        observer.observe(container, { childList: true, subtree: true });

        // Initial run
        filterVideos();
    }

    setupObserver();

})();