YouTube Channel Blocker v5

Block YouTube channels with optimized performance and fullscreen scroll prevention

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

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

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

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

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

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.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name         YouTube Channel Blocker v5
// @namespace    http://tampermonkey.net/
// @version      5
// @description  Block YouTube channels with optimized performance and fullscreen scroll prevention
// @author       You
// @match        https://www.youtube.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // Configuration
    const CONFIG = {
        PROCESS_INTERVAL: 1500,
        URL_CHECK_INTERVAL: 800,
        INIT_DELAY: 400,
        DEBOUNCE_DELAY: 150,
        SELECTORS: {
            VIDEO: [
                'ytd-video-renderer',
                'ytd-grid-video-renderer',
                'ytd-compact-video-renderer',
                'ytd-rich-item-renderer',
                'ytd-playlist-video-renderer',
                'ytd-reel-item-renderer',
                'ytd-movie-renderer'
            ]
        }
    };

    // State management with performance optimization
    const state = {
        blockedChannels: GM_getValue('blockedChannels', []),
        lastUrl: location.href,
        cache: new Map(),
        processingQueue: new Set(),
        isProcessing: false,
        isFullscreen: false,
        scrollPreventionActive: false
    };

    // Utility functions
    const utils = {
        saveBlocked() {
            GM_setValue('blockedChannels', state.blockedChannels);
            state.cache.clear();
        },

        isBlocked(channelName, channelId) {
            const cacheKey = `${channelName}:${channelId}`;
            if (state.cache.has(cacheKey)) {
                return state.cache.get(cacheKey);
            }

            const blocked = state.blockedChannels.some(b => {
                const lower = b.toLowerCase();
                return channelName.toLowerCase().includes(lower) ||
                       (channelId && channelId.toLowerCase() === lower);
            });

            state.cache.set(cacheKey, blocked);
            return blocked;
        },

        getChannelInfo(video, selector) {
            let channelLink;

            if (selector === 'ytd-rich-item-renderer') {
                const richMedia = video.querySelector('ytd-rich-grid-media');
                channelLink = richMedia?.querySelector('a[href*="/@"], a[href*="/channel/"], ytd-channel-name a');
            } else {
                channelLink = video.querySelector('ytd-channel-name a, #channel-name a, #text a[href*="/@"], a[href*="/channel/"]');
            }

            if (!channelLink) return null;

            const href = channelLink.getAttribute('href') || '';
            const channelId = href.split('/').pop().split('?')[0];

            return {
                name: channelLink.textContent.trim(),
                id: channelId,
                link: channelLink
            };
        },

        getInsertTarget(video, selector) {
            if (selector === 'ytd-rich-item-renderer') {
                const richMedia = video.querySelector('ytd-rich-grid-media');
                return richMedia?.querySelector('#details, #metadata, #meta');
            }
            return video.querySelector('#metadata-line, #meta, #metadata, #details, ytd-channel-name');
        },

        debounce(func, delay) {
            let timeoutId;
            return function(...args) {
                clearTimeout(timeoutId);
                timeoutId = setTimeout(() => func.apply(this, args), delay);
            };
        }
    };

    // Channel management
    const channelManager = {
        block(channelName, channelId) {
            const identifier = channelId || channelName;
            if (state.blockedChannels.includes(identifier)) {
                alert(`Channel "${channelName}" is already blocked.`);
                return false;
            }

            state.blockedChannels.push(identifier);
            utils.saveBlocked();
            alert(`✅ Blocked: ${channelName}\n\nRefresh the page to see changes.`);
            ui.updateToggleButton();
            return true;
        },

        unblock(identifier) {
            const index = state.blockedChannels.indexOf(identifier);
            if (index === -1) return false;

            state.blockedChannels.splice(index, 1);
            utils.saveBlocked();
            ui.updateToggleButton();
            return true;
        },

        exportBlocked() {
            const data = JSON.stringify(state.blockedChannels, null, 2);
            const blob = new Blob([data], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `youtube-blocked-channels-${Date.now()}.json`;
            a.click();
            URL.revokeObjectURL(url);
        },

        importBlocked(jsonData) {
            try {
                const imported = JSON.parse(jsonData);
                if (Array.isArray(imported)) {
                    const newChannels = imported.filter(ch => !state.blockedChannels.includes(ch));
                    state.blockedChannels.push(...newChannels);
                    utils.saveBlocked();
                    alert(`✅ Imported ${newChannels.length} new channels.\nTotal blocked: ${state.blockedChannels.length}`);
                    ui.updateToggleButton();
                    ui.updateBlockedList();
                    return true;
                }
            } catch (e) {
                alert('❌ Invalid JSON file');
            }
            return false;
        }
    };

    // Fullscreen handler - NEW ENHANCED VERSION IN v5
    const fullscreenHandler = {
        preventScroll(e) {
            if (state.isFullscreen) {
                e.preventDefault();
                e.stopPropagation();
                return false;
            }
        },

        hideUIElements() {
            // Hide all YouTube UI overlays during fullscreen
            const elementsToHide = [
                '.ytp-chrome-top',
                '.ytp-chrome-bottom',
                '.ytp-gradient-top',
                '.ytp-gradient-bottom',
                '.ytp-pause-overlay',
                'ytd-engagement-panel-section-list-renderer',
                '.ytp-endscreen-content',
                '.ytp-ce-element',
                '.ytp-cards-teaser',
                '.ytp-suggestion-set'
            ];

            elementsToHide.forEach(selector => {
                document.querySelectorAll(selector).forEach(el => {
                    if (state.isFullscreen) {
                        el.style.setProperty('display', 'none', 'important');
                        el.style.setProperty('opacity', '0', 'important');
                        el.style.setProperty('pointer-events', 'none', 'important');
                    } else {
                        el.style.removeProperty('display');
                        el.style.removeProperty('opacity');
                        el.style.removeProperty('pointer-events');
                    }
                });
            });
        },

        enableScrollPrevention() {
            if (state.scrollPreventionActive) return;

            // Prevent wheel scrolling
            document.addEventListener('wheel', this.preventScroll, { passive: false, capture: true });
            // Prevent touch scrolling
            document.addEventListener('touchmove', this.preventScroll, { passive: false, capture: true });
            // Prevent keyboard scrolling
            document.addEventListener('keydown', (e) => {
                if (state.isFullscreen && ['ArrowDown', 'ArrowUp', 'PageDown', 'PageUp', 'Space', 'Home', 'End'].includes(e.key)) {
                    e.preventDefault();
                    e.stopPropagation();
                }
            }, { capture: true });

            state.scrollPreventionActive = true;
            console.log('🚫 Fullscreen scroll prevention enabled');
        },

        disableScrollPrevention() {
            if (!state.scrollPreventionActive) return;

            document.removeEventListener('wheel', this.preventScroll, { capture: true });
            document.removeEventListener('touchmove', this.preventScroll, { capture: true });

            state.scrollPreventionActive = false;
            console.log('✅ Fullscreen scroll prevention disabled');
        },

        handleFullscreenChange() {
            const isFullscreen = !!(
                document.fullscreenElement ||
                document.webkitFullscreenElement ||
                document.mozFullScreenElement ||
                document.msFullscreenElement ||
                document.querySelector('.html5-video-player.ytp-fullscreen')
            );

            // Update state
            state.isFullscreen = isFullscreen;

            // Hide/show toggle button
            const btn = document.getElementById('blocker-toggle');
            if (btn) {
                btn.style.display = isFullscreen ? 'none' : 'flex';
            }

            // Hide/show management panel
            const panel = document.getElementById('blocker-panel');
            if (panel && isFullscreen) {
                panel.style.display = 'none';
            }

            // Enable/disable scroll prevention
            if (isFullscreen) {
                this.enableScrollPrevention();
                this.hideUIElements();
            } else {
                this.disableScrollPrevention();
                this.hideUIElements(); // Reset UI elements
            }
        }
    };

    // Enhanced style injection with better visual indicators
    const styles = {
        inject() {
            const existingStyle = document.getElementById('channel-blocker-style');
            if (existingStyle) existingStyle.remove();

            const style = document.createElement('style');
            style.id = 'channel-blocker-style';
            style.textContent = `
                ${CONFIG.SELECTORS.VIDEO.map(s => `${s}[data-blocked="true"]`).join(',\n')} {
                    position: relative;
                    border: 3px solid #ffd700 !important;
                    border-radius: 8px !important;
                    padding: 4px !important;
                    background: rgba(255, 215, 0, 0.05) !important;
                }
                ${CONFIG.SELECTORS.VIDEO.map(s => `${s}[data-blocked="true"] ytd-thumbnail`).join(',\n')} {
                    border: 4px solid #ffd700 !important;
                    border-radius: 12px !important;
                    box-sizing: border-box !important;
                    pointer-events: none !important;
                    opacity: 0.6 !important;
                }
                ${CONFIG.SELECTORS.VIDEO.map(s => `${s}[data-blocked="true"] ytd-thumbnail *`).join(',\n')} {
                    pointer-events: none !important;
                }
                ${CONFIG.SELECTORS.VIDEO.map(s => `${s}[data-blocked="true"] ytd-thumbnail video`).join(',\n')} {
                    display: none !important;
                }
                ${CONFIG.SELECTORS.VIDEO.map(s => `${s}[data-blocked="true"] #mouseover-overlay, ${s}[data-blocked="true"] #hover-overlays`).join(',\n')} {
                    display: none !important;
                }
                .channel-block-btn {
                    transition: all 0.2s ease !important;
                }
                .channel-block-btn:hover {
                    transform: scale(1.05) !important;
                    filter: brightness(1.1) !important;
                }
                /* Hide toggle button in fullscreen mode */
                .html5-video-player.ytp-fullscreen ~ #blocker-toggle,
                body.fullscreen #blocker-toggle,
                html[data-fullscreen="true"] #blocker-toggle {
                    display: none !important;
                }
                /* NEW in v5: Prevent all overlays in fullscreen */
                body.ytp-fullscreen .ytp-pause-overlay,
                body.ytp-fullscreen .ytp-chrome-top,
                body.ytp-fullscreen .ytp-chrome-bottom {
                    opacity: 0 !important;
                    pointer-events: none !important;
                }
            `;
            document.head.appendChild(style);
        }
    };

    // Optimized video processing with batching
    const videoProcessor = {
        mark() {
            const batch = [];
            CONFIG.SELECTORS.VIDEO.forEach(selector => {
                document.querySelectorAll(`${selector}:not([data-checked])`).forEach(video => {
                    batch.push({ video, selector });
                });
            });

            // Process in batches to avoid blocking
            batch.forEach(({ video, selector }) => {
                video.dataset.checked = 'true';

                const info = utils.getChannelInfo(video, selector);
                if (!info) return;

                if (utils.isBlocked(info.name, info.id)) {
                    video.dataset.blocked = 'true';
                }
            });
        },

        addBlockButtons() {
            CONFIG.SELECTORS.VIDEO.forEach(selector => {
                document.querySelectorAll(`${selector}:not([data-has-button])`).forEach(video => {
                    if (video.dataset.blocked === 'true') return;

                    video.dataset.hasButton = 'true';

                    const info = utils.getChannelInfo(video, selector);
                    if (!info) return;

                    const insertTarget = utils.getInsertTarget(video, selector);
                    if (!insertTarget) return;

                    const alreadyBlocked = utils.isBlocked(info.name, info.id);
                    const button = this.createButton(info.name, info.id, alreadyBlocked);
                    insertTarget.appendChild(button);
                });
            });
        },

        addBadges() {
            CONFIG.SELECTORS.VIDEO.forEach(selector => {
                document.querySelectorAll(`${selector}[data-blocked="true"]:not([data-has-badge])`).forEach(video => {
                    video.dataset.hasBadge = 'true';

                    const info = utils.getChannelInfo(video, selector);
                    if (!info) return;

                    const insertTarget = utils.getInsertTarget(video, selector);
                    if (!insertTarget) return;

                    const badge = this.createBadge(info.name, info.id);
                    insertTarget.appendChild(badge);
                });
            });
        },

        createButton(channelName, channelId, alreadyBlocked) {
            const button = document.createElement('button');
            button.innerHTML = alreadyBlocked ? '✓ Blocked' : '🚫 Block';
            button.className = 'channel-block-btn';
            button.style.cssText = `
                background: ${alreadyBlocked ? '#666' : '#c00'};
                color: white; border: none; padding: 4px 10px;
                border-radius: 12px; cursor: pointer; font-size: 11px;
                font-weight: 500; margin: 4px 0 4px 8px;
                display: inline-block;
            `;

            button.onclick = (e) => {
                e.preventDefault();
                e.stopPropagation();
                if (alreadyBlocked) {
                    if (confirm(`Unblock "${channelName}"?`)) {
                        channelManager.unblock(channelId || channelName);
                        location.reload();
                    }
                } else {
                    channelManager.block(channelName, channelId);
                }
            };

            return button;
        },

        createBadge(channelName, channelId) {
            const badge = document.createElement('button');
            badge.innerHTML = '✓ Blocked';
            badge.className = 'blocked-channel-badge channel-block-btn';
            badge.style.cssText = `
                background: #ffd700; color: #000; border: none;
                padding: 4px 12px; border-radius: 12px; cursor: pointer;
                font-size: 11px; font-weight: bold; margin: 4px 0 4px 8px;
                display: inline-block;
            `;

            badge.onclick = (e) => {
                e.preventDefault();
                e.stopPropagation();
                if (confirm(`Unblock channel "${channelName}"?\n\nVideos from this channel will no longer be marked.`)) {
                    if (channelManager.unblock(channelId || channelName)) {
                        alert(`✅ Unblocked: ${channelName}`);
                        location.reload();
                    }
                }
            };

            return badge;
        }
    };

    // Enhanced channel page handler
    const channelPage = {
        addButton() {
            if (!location.pathname.match(/\/@|\/channel\//)) return;
            if (document.getElementById('channel-block-btn')) return;

            const nameEl = document.querySelector('yt-dynamic-text-view-model h1 span[role="text"]');
            const handleEl = document.querySelector('yt-content-metadata-view-model span[role="text"]');

            const channelName = nameEl?.textContent.trim().split('\n')[0] || '';
            let channelId = handleEl?.textContent.trim() || '';
            if (!channelId.startsWith('@')) {
                channelId = '@' + (location.pathname.split('/@')[1] || location.pathname.split('/channel/')[1] || '').split('/')[0];
            }

            if (!channelName && !channelId) return;

            const flexActions = document.querySelector('yt-flexible-actions-view-model');
            if (!flexActions) return;

            const blocked = utils.isBlocked(channelName, channelId);
            const wrapper = document.createElement('div');
            wrapper.id = 'channel-block-btn';
            wrapper.className = 'ytFlexibleActionsViewModelAction';

            const button = document.createElement('button');
            button.className = 'yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m channel-block-btn';
            if (blocked) {
                button.style.cssText = `
                    background: #ffd700 !important; color: #000 !important;
                    border: 2px solid #000 !important; padding: 10px 24px !important;
                    font-weight: bold !important;
                `;
            }
            button.innerHTML = `<div class="yt-spec-button-shape-next__button-text-content">${blocked ? '✓ Blocked' : '🚫 Block'}</div>`;
            button.onclick = () => {
                if (blocked) {
                    if (confirm(`Unblock "${channelName}"?`)) {
                        channelManager.unblock(channelId || channelName);
                        location.reload();
                    }
                } else {
                    if (confirm(`Block "${channelName}"?`)) {
                        channelManager.block(channelName, channelId);
                    }
                }
            };

            wrapper.appendChild(button);
            flexActions.appendChild(wrapper);
        }
    };

    // Enhanced UI with import/export functionality
    const ui = {
        createToggleButton() {
            const btn = document.createElement('button');
            btn.id = 'blocker-toggle';
            btn.innerHTML = `🚫 (${state.blockedChannels.length})`;
            btn.title = 'Manage Blocked Channels';
            btn.style.cssText = `
                position: fixed; top: 70px; right: 20px; background: #c00;
                color: white; border: none; padding: 8px 12px; border-radius: 50%;
                cursor: grab; z-index: 9999; font-weight: 600; font-size: 16px;
                box-shadow: 0 2px 10px rgba(204,0,0,0.5); width: 44px; height: 44px;
                display: flex; align-items: center; justify-content: center;
                transition: all 0.2s;
            `;

            btn.onmouseover = () => {
                btn.style.transform = 'scale(1.1)';
                btn.style.boxShadow = '0 4px 15px rgba(204,0,0,0.7)';
            };
            btn.onmouseout = () => {
                btn.style.transform = 'scale(1)';
                btn.style.boxShadow = '0 2px 10px rgba(204,0,0,0.5)';
            };

            btn.onclick = () => {
                const panel = document.getElementById('blocker-panel');
                if (panel.style.display === 'none') {
                    this.updateBlockedList();
                    panel.style.display = 'block';
                } else {
                    panel.style.display = 'none';
                }
            };

            document.body.appendChild(btn);
            this.makeDraggable(btn);
        },

        updateToggleButton() {
            const btn = document.getElementById('blocker-toggle');
            if (btn) btn.innerHTML = `🚫 (${state.blockedChannels.length})`;
        },

        createManagementPanel() {
            const panel = document.createElement('div');
            panel.id = 'blocker-panel';
            panel.style.cssText = `
                position: fixed; top: 150px; right: 20px; background: #282828;
                border: 2px solid #c00; border-radius: 12px; padding: 20px;
                z-index: 10000; max-width: 380px; max-height: 550px;
                overflow-y: auto; display: none; color: white;
                box-shadow: 0 4px 20px rgba(0,0,0,0.5);
            `;

            panel.innerHTML = `
                <h3 style="margin: 0 0 16px 0; font-size: 18px; border-bottom: 2px solid #c00; padding-bottom: 8px;">
                    🚫 Blocked Channels
                </h3>
                <div id="blocked-count" style="margin-bottom: 12px; color: #aaa; font-size: 13px;">
                    Total: ${state.blockedChannels.length}
                </div>
                <div id="blocked-list" style="margin-bottom: 16px;"></div>
                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 16px;">
                    <button id="close-panel" style="background: #065fd4; color: white; border: none; padding: 10px; border-radius: 6px; cursor: pointer;">Close</button>
                    <button id="clear-all" style="background: #c00; color: white; border: none; padding: 10px; border-radius: 6px; cursor: pointer;">Clear All</button>
                    <button id="export-blocked" style="background: #0a8a0a; color: white; border: none; padding: 10px; border-radius: 6px; cursor: pointer;">Export</button>
                    <button id="import-blocked" style="background: #f59e0b; color: white; border: none; padding: 10px; border-radius: 6px; cursor: pointer;">Import</button>
                </div>
                <input type="file" id="import-file" accept=".json" style="display: none;">
            `;

            document.body.appendChild(panel);

            document.getElementById('close-panel').onclick = () => panel.style.display = 'none';
            document.getElementById('clear-all').onclick = () => {
                if (confirm('Unblock all channels?')) {
                    state.blockedChannels = [];
                    utils.saveBlocked();
                    this.updateBlockedList();
                    location.reload();
                }
            };
            document.getElementById('export-blocked').onclick = () => channelManager.exportBlocked();
            document.getElementById('import-blocked').onclick = () => document.getElementById('import-file').click();
            document.getElementById('import-file').onchange = (e) => {
                const file = e.target.files[0];
                if (file) {
                    const reader = new FileReader();
                    reader.onload = (ev) => channelManager.importBlocked(ev.target.result);
                    reader.readAsText(file);
                }
            };
        },

        updateBlockedList() {
            const list = document.getElementById('blocked-list');
            const count = document.getElementById('blocked-count');
            if (!list) return;

            if (count) count.textContent = `Total: ${state.blockedChannels.length}`;
            list.innerHTML = state.blockedChannels.length === 0
                ? '<p style="color: #aaa; text-align: center; padding: 20px;">No blocked channels</p>'
                : state.blockedChannels.map(ch => `
                    <div style="display: flex; justify-content: space-between; align-items: center; padding: 10px; margin-bottom: 6px; background: #3a3a3a; border-radius: 6px;">
                        <span style="flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="${ch}">${ch}</span>
                        <button onclick="window.unblockChannelFromPanel('${ch.replace(/'/g, "\\'")}'))" style="background: #065fd4; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; margin-left: 8px;">Unblock</button>
                    </div>
                `).join('');
        },

        makeDraggable(el) {
            let pos1=0, pos2=0, pos3=0, pos4=0;
            el.onmousedown = (e) => {
                pos3 = e.clientX;
                pos4 = e.clientY;
                el.style.cursor = 'grabbing';
                document.onmouseup = () => {
                    document.onmouseup = null;
                    document.onmousemove = null;
                    el.style.cursor = 'grab';
                };
                document.onmousemove = (e) => {
                    pos1 = pos3 - e.clientX;
                    pos2 = pos4 - e.clientY;
                    pos3 = e.clientX;
                    pos4 = e.clientY;
                    el.style.top = (el.offsetTop - pos2) + "px";
                    el.style.left = (el.offsetLeft - pos1) + "px";
                    el.style.right = 'auto';
                };
            };
        }
    };

    // Global function for panel
    window.unblockChannelFromPanel = (ch) => {
        if (channelManager.unblock(ch)) {
            alert(`✅ Unblocked: ${ch}`);
            ui.updateBlockedList();
            location.reload();
        }
    };

    // Optimized main processing with debouncing
    const debouncedProcess = utils.debounce(() => {
        if (state.isProcessing) return;
        state.isProcessing = true;

        try {
            videoProcessor.mark();
            videoProcessor.addBlockButtons();
            videoProcessor.addBadges();
            channelPage.addButton();
        } finally {
            state.isProcessing = false;
        }
    }, CONFIG.DEBOUNCE_DELAY);

    function processPage() {
        debouncedProcess();
    }

    // URL change detection with optimization
    function detectUrlChange() {
        if (location.href !== state.lastUrl) {
            state.lastUrl = location.href;
            setTimeout(() => {
                styles.inject();
                processPage();
            }, CONFIG.INIT_DELAY);
        }
    }

    // Initialize with performance monitoring
    setTimeout(() => {
        console.log('🚫 YouTube Channel Blocker v5 initialized');
        console.log(`📊 Blocking ${state.blockedChannels.length} channels`);
        console.log('✨ NEW: Enhanced fullscreen mode with scroll prevention');

        ui.createToggleButton();
        ui.createManagementPanel();
        styles.inject();
        processPage();

        // Initialize fullscreen detection
        fullscreenHandler.handleFullscreenChange();
    }, CONFIG.INIT_DELAY);

    // Optimized intervals
    setInterval(processPage, CONFIG.PROCESS_INTERVAL);
    setInterval(detectUrlChange, CONFIG.URL_CHECK_INTERVAL);

    // Listen for fullscreen changes - ENHANCED IN v5
    document.addEventListener('fullscreenchange', () => fullscreenHandler.handleFullscreenChange());
    document.addEventListener('webkitfullscreenchange', () => fullscreenHandler.handleFullscreenChange());
    document.addEventListener('mozfullscreenchange', () => fullscreenHandler.handleFullscreenChange());
    document.addEventListener('MSFullscreenChange', () => fullscreenHandler.handleFullscreenChange());

    // Also check for YouTube's custom fullscreen class changes
    const fullscreenObserver = new MutationObserver(() => fullscreenHandler.handleFullscreenChange());
    setTimeout(() => {
        const videoPlayer = document.querySelector('.html5-video-player');
        if (videoPlayer) {
            fullscreenObserver.observe(videoPlayer, { attributes: true, attributeFilter: ['class'] });
        }
    }, 1000);

    // Performance monitoring
    if (state.blockedChannels.length > 0) {
        console.log('✅ YouTube Channel Blocker v5 active with enhanced fullscreen control');
    }
})();