Hide Members Only Filter and whitelist for youtube

Auto-hide "Members only" videos from untrusted channels on YouTube with a toggle to re-show/hide them. Optionally leave layout gap with config flag (handles sidebar safely too). Includes fallback logic for non-existent parent elements.

// ==UserScript==
// @name         Hide Members Only Filter and whitelist for youtube
// @namespace    http://tampermonkey.net/
// @version      2.6
// @description  Auto-hide "Members only" videos from untrusted channels on YouTube with a toggle to re-show/hide them. Optionally leave layout gap with config flag (handles sidebar safely too). Includes fallback logic for non-existent parent elements.
// @author       Aonnymous
// @match        https://www.youtube.com/*
// @grant        GM_registerMenuCommand
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // === Config ===

    const WHITELIST = [
        'channel name 1',
        'Some Channel',
        'Trusted Creator'
    ];

    /**
     * LEAVE_BLANK_SPACE_WHEN_HIDDEN:
     * For channel page video/live tab
     * - true  => hides only video element (layout gap)
     * - false => tries to hide parent container (no layout gap),
     *            but still hides video if parent is unavailable
     */
    const LEAVE_BLANK_SPACE_WHEN_HIDDEN = true;

    const SCAN_INTERVAL_FAST = 2000;              // Every 2s for first minute
    const FAST_SCAN_DURATION = 60000;             // Fast mode for 1 minute
    const MAX_RUNS_PER_MINUTE = 4;                // Slow mode limit
    const DEBUG_BORDER_STYLE = '2px solid red';   // Red border on hidden
    const AUTO_ENABLE_DELAY_MS = 4500;            // Auto-start 4.5s in

    // === State ===
    let scanInterval = null;
    let filteringEnabled = false;
    let scanStartTime = null;
    let lastRunTimestamp = 0;
    let runsThisMinute = 0;

    // === Utilities ===

    const CLEANED_WHITELIST = WHITELIST.map(name => name.trim().toLowerCase());

    function log(...args) {
        console.log('[YT-MembersFilter]', ...args);
    }

    function isWhitelisted(name) {
        const lc = name.trim().toLowerCase();
        return CLEANED_WHITELIST.some(w => lc.indexOf(w) !== -1);
    }

    function getTopLevelVideoElements() {
        const sidebarVideos = Array.from(document.querySelectorAll('ytd-compact-video-renderer'))
            .filter(el => el.closest('ytd-compact-video-renderer') === el);

        const richItems = Array.from(document.querySelectorAll('div#content.style-scope.ytd-rich-item-renderer'));

        return [...sidebarVideos, ...richItems];
    }

    function scanAndHide() {
        const now = Date.now();
        const inFastMode = now - scanStartTime < FAST_SCAN_DURATION;

        if (!inFastMode) {
            if (now - lastRunTimestamp > 60000) {
                runsThisMinute = 0;
                lastRunTimestamp = now;
            }

            if (runsThisMinute >= MAX_RUNS_PER_MINUTE) {
                log('Throttled: max scans this minute reached.');
                return;
            }

            runsThisMinute++;
        }

        const videos = getTopLevelVideoElements();

        if (!videos.length) {
            log('No video elements found.');
            return;
        }

        let hiddenCount = 0;

        videos.forEach(vid => {
            if (vid.dataset._ytMembersFiltered === 'true') return;

            const text = vid.textContent || '';
            if (!text.includes('Members only')) return;

            const channelElem = vid.querySelector('.ytd-channel-name');
            const channelName = channelElem?.textContent?.trim() || '';

            if (!isWhitelisted(channelName)) {
                // Always hide the video element
                vid.style.display = 'none';
                vid.style.border = DEBUG_BORDER_STYLE;
                vid.dataset._ytMembersFiltered = 'true';

                if (!LEAVE_BLANK_SPACE_WHEN_HIDDEN) {
                    // Try to hide the parent (to leave blank space), but catch any errors
                    try {
                        const parentRenderer = vid.closest('ytd-rich-item-renderer');
                        if (parentRenderer) {
                            parentRenderer.hidden = true;
                            parentRenderer.style.border = DEBUG_BORDER_STYLE;
                            parentRenderer.dataset._ytMembersFiltered = 'true';
                        }
                    } catch (err) {
                        log('Could not hide parent element (possibly sidebar):', err);
                    }
                }

                hiddenCount++;
                log(`Hid video from: "${channelName}"`);
            } else {
                log(`Whitelisted video from: "${channelName}"`);
            }
        });

        if (hiddenCount) {
            log(`Hidden videos this scan: ${hiddenCount}`);
        }
    }

    function startScanning() {
        if (scanInterval) clearInterval(scanInterval);
        scanStartTime = Date.now();
        runsThisMinute = 0;
        lastRunTimestamp = 0;

        scanInterval = setInterval(scanAndHide, SCAN_INTERVAL_FAST);
        log('Started scanning every 2s for 1 minute...');

        setTimeout(() => {
            if (scanInterval) clearInterval(scanInterval);
            log('Fast scan finished. Throttled scanning active.');
            scanInterval = setInterval(scanAndHide, 15000);
        }, FAST_SCAN_DURATION);
    }

    function stopScanningAndShowAll() {
        if (scanInterval) clearInterval(scanInterval);
        scanInterval = null;

        const hiddenEls = document.querySelectorAll('[data-_yt-members-filtered="true"]');
        hiddenEls.forEach(el => {
            el.style.display = '';
            el.style.border = '';
            el.hidden = false;
            delete el.dataset._ytMembersFiltered;
        });

        log('Stopped scanning and revealed previously hidden videos.');
    }

    function toggleFiltering() {
        filteringEnabled = !filteringEnabled;

        if (filteringEnabled) {
            log('Filtering ENABLED.');
            startScanning();
        } else {
            log('Filtering DISABLED.');
            stopScanningAndShowAll();
        }
    }

    // === Tampermonkey Menu ===
    GM_registerMenuCommand('Toggle Member Filter On/Off', toggleFiltering);

    // === Auto Start After Delay ===
    setTimeout(() => {
        if (!filteringEnabled) {
            filteringEnabled = true;
            log('Auto-starting filtering after delay...');
            startScanning();
        }
    }, AUTO_ENABLE_DELAY_MS);

    log('YouTube Member Filter script loaded. Will auto-start in ~4.5s. Toggle anytime via Tampermonkey menu.');
})();