YouTube Enchantments

Automatically likes videos of channels you're subscribed to, scrolls down on Youtube with a toggle button, and bypasses the AdBlock ban.

// ==UserScript==
// @name           YouTube Enchantments
// @namespace      http://tampermonkey.net/
// @version        0.8.5
// @description    Automatically likes videos of channels you're subscribed to, scrolls down on Youtube with a toggle button, and bypasses the AdBlock ban.
// @author         JJJ
// @match          https://www.youtube.com/*
// @exclude        https://www.youtube.com/*/community
// @icon           https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_registerMenuCommand
// @run-at         document-idle
// @noframes
// @license        MIT
// ==/UserScript==

(() => {
    'use strict';

    // Add logger configuration
    const Logger = {
        enabled: true,
        styles: {
            info: 'color: #2196F3; font-weight: bold',
            warning: 'color: #FFC107; font-weight: bold',
            success: 'color: #4CAF50; font-weight: bold',
            error: 'color: #F44336; font-weight: bold'
        },
        prefix: '[YouTubeEnchantments]',
        getTimestamp() {
            return new Date().toISOString().split('T')[1].slice(0, -1);
        },
        info(msg) {
            if (!this.enabled) return;
            console.log(`%c${this.prefix} ${this.getTimestamp()} - ${msg}`, this.styles.info);
        },
        warning(msg) {
            if (!this.enabled) return;
            console.warn(`%c${this.prefix} ${this.getTimestamp()} - ${msg}`, this.styles.warning);
        },
        success(msg) {
            if (!this.enabled) return;
            console.log(`%c${this.prefix} ${this.getTimestamp()} - ${msg}`, this.styles.success);
        },
        error(msg) {
            if (!this.enabled) return;
            console.error(`%c${this.prefix} ${this.getTimestamp()} - ${msg}`, this.styles.error);
        }
    };

    // Basic browser feature warning (no early return to keep functionality)
    if (!window.URL) {
        Logger.warning('URL API not available; some features may not work as expected.');
    }

    // Inject the YouTube IFrame API script with error handling
    function injectYouTubeAPI() {
        return new Promise((resolve, reject) => {
            try {
                // If API already present, resolve immediately
                if (window.YT && window.YT.Player) return resolve();

                const existing = Array.from(document.scripts).find(s => s.src && s.src.includes('https://www.youtube.com/iframe_api'));
                if (existing) {
                    existing.addEventListener('load', () => resolve());
                    existing.addEventListener('error', reject);
                    return;
                }

                const script = document.createElement('script');
                script.src = 'https://www.youtube.com/iframe_api';
                script.onload = () => resolve();
                script.onerror = (e) => reject(e);
                document.head.appendChild(script);
            } catch (e) {
                reject(e);
            }
        });
    }

    // Constants - Optimized selectors
    const SELECTORS = {
        PLAYER: '#movie_player',
        SUBSCRIBE_BUTTON: '#subscribe-button > ytd-subscribe-button-renderer, ytd-reel-player-overlay-renderer #subscribe-button, tp-yt-paper-button[subscribed]',
        LIKE_BUTTON: [
            'like-button-view-model button',
            'ytd-menu-renderer button[aria-label*="like" i]',
            'button[aria-label*="like" i]',
            'ytd-toggle-button-renderer[aria-pressed] button',
            'ytd-reel-player-overlay-renderer ytd-like-button-view-model button',
            'ytd-reel-video-renderer ytd-like-button-view-model button'
        ].join(','),
        DISLIKE_BUTTON: [
            'dislike-button-view-model button',
            'ytd-menu-renderer button[aria-label*="dislike" i]',
            'button[aria-label*="dislike" i]',
            'ytd-toggle-button-renderer[aria-pressed] button[aria-label*="dislike" i]'
        ].join(','),
        PLAYER_CONTAINER: '#player-container-outer',
        ERROR_SCREEN: '#error-screen',
        PLAYABILITY_ERROR: '.yt-playability-error-supported-renderers',
        LIVE_BADGE: '.ytp-live-badge',
        GAME_SECTION: 'ytd-rich-section-renderer, div#dismissible.style-scope.ytd-rich-shelf-renderer'
    };

    const CONSTANTS = {
        IFRAME_ID: 'adblock-bypass-player',
        STORAGE_KEY: 'youtubeEnchantmentsSettings',
        DELAY: 300,
        MAX_TRIES: 150,
        DUPLICATE_CHECK_INTERVAL: 7000,
        GAME_CHECK_INTERVAL: 2000,
        MIN_CHECK_FREQUENCY: 1000,
        MAX_CHECK_FREQUENCY: 30000
    };

    // Optimized settings with better defaults
    const defaultSettings = {
        autoLikeEnabled: true,
        autoLikeLiveStreams: false,
        likeIfNotSubscribed: false,
        watchThreshold: 0,
        checkFrequency: 3000,
        adBlockBypassEnabled: false,
        scrollSpeed: 50,
        removeGamesEnabled: true,
        loggingEnabled: true
    };

    let settings = loadSettings();
    const autoLikedVideoIds = new Set();
    let isScrolling = false;
    let scrollInterval;
    let currentPageUrl = window.location.href;
    let tries = 0;

    // Scheduler/observer references
    let checkTimer = null;
    let duplicateCleanupInterval = null;
    let gameHideInterval = null;
    let adBlockObserver = null;
    let likeReadyObserver = null;

    // Keep Logger toggle in sync with settings
    Logger.enabled = !!settings.loggingEnabled;

    const urlUtils = {
        extractParams(url) {
            try {
                const params = new URL(url).searchParams;
                return {
                    videoId: params.get('v'),
                    playlistId: params.get('list'),
                    index: params.get('index')
                };
            } catch (e) {
                console.error('Failed to extract URL params:', e);
                return {};
            }
        },

        getTimestampFromUrl(url) {
            try {
                const timestamp = new URL(url).searchParams.get('t');
                if (timestamp) {
                    const timeArray = timestamp.split(/h|m|s/).map(Number);
                    const timeInSeconds = timeArray.reduce((acc, time, index) =>
                        acc + time * Math.pow(60, 2 - index), 0);
                    return `&start=${timeInSeconds}`;
                }
            } catch (e) {
                console.error('Failed to extract timestamp:', e);
            }
            return '';
        }
    };

    let player;

    // Updated PlayerManager
    const playerManager = {
        async initPlayer() {
            try {
                await injectYouTubeAPI();
                const iframe = document.getElementById(CONSTANTS.IFRAME_ID);
                if (iframe) {
                    player = new YT.Player(CONSTANTS.IFRAME_ID, {
                        events: {
                            'onReady': this.onPlayerReady.bind(this),
                            'onStateChange': this.onPlayerStateChange.bind(this),
                            'onError': (event) => {
                                Logger.error(`Player error: ${event.data}`);
                            }
                        }
                    });
                }
            } catch (error) {
                Logger.error(`Failed to initialize player: ${error}`);
            }
        },

        onPlayerReady(event) {
            Logger.info('Player is ready');
        },

        onPlayerStateChange(event) {
            if (event.data === YT.PlayerState.AD_STARTED) {
                Logger.info('Ad is playing, allowing ad to complete.');
            } else if (event.data === YT.PlayerState.ENDED || event.data === YT.PlayerState.PLAYING) {
                Logger.info('Video is playing, ensuring it is tracked in history.');
            }
        },

        createIframe(url) {
            try {
                const { videoId, playlistId, index } = urlUtils.extractParams(url);
                if (!videoId) return null;

                const iframe = document.createElement('iframe');
                const commonArgs = 'autoplay=1&modestbranding=1&enablejsapi=1&origin=' + encodeURIComponent(window.location.origin);
                const embedUrl = playlistId
                    ? `https://www.youtube.com/embed/${videoId}?${commonArgs}&list=${playlistId}&index=${index}`
                    : `https://www.youtube.com/embed/${videoId}?${commonArgs}${urlUtils.getTimestampFromUrl(url)}`;

                this.setIframeAttributes(iframe, embedUrl);
                return iframe;
            } catch (error) {
                Logger.error(`Failed to create iframe: ${error}`);
                return null;
            }
        },

        setIframeAttributes(iframe, url) {
            iframe.id = CONSTANTS.IFRAME_ID;
            iframe.src = url;
            iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share';
            iframe.allowFullscreen = true;
            iframe.style.cssText = 'height:100%; width:calc(100% - 240px); border:none; border-radius:12px; position:relative; left:240px;';
        },

        replacePlayer(url) {
            const playerContainer = document.querySelector(SELECTORS.ERROR_SCREEN);
            if (!playerContainer) return;

            let iframe = document.getElementById(CONSTANTS.IFRAME_ID);
            if (iframe) {
                this.setIframeAttributes(iframe, url);
            } else {
                iframe = this.createIframe(url);
                if (iframe) {
                    playerContainer.appendChild(iframe);
                    this.initPlayer();
                }
            }
            // Ensure the iframe is on top of the player container
            this.bringToFront(CONSTANTS.IFRAME_ID);
            this.addScrollListener();
        },

        bringToFront(elementId) {
            const element = document.getElementById(elementId);
            if (element) {
                const maxZIndex = Math.max(
                    ...Array.from(document.querySelectorAll('*'))
                        .map(e => parseInt(window.getComputedStyle(e).zIndex) || 0)
                );
                element.style.zIndex = maxZIndex + 1;
            }
        },

        removeDuplicates() {
            const iframes = document.querySelectorAll(`#${CONSTANTS.IFRAME_ID}`);
            if (iframes.length > 1) {
                Array.from(iframes).slice(1).forEach(iframe => iframe.remove());
            }
        },

        addScrollListener() {
            window.addEventListener('scroll', this.handleScroll);
        },

        handleScroll() {
            const iframe = document.getElementById(CONSTANTS.IFRAME_ID);
            if (!iframe) return;

            const playerContainer = document.querySelector(SELECTORS.ERROR_SCREEN);
            if (!playerContainer) return;

            const rect = playerContainer.getBoundingClientRect();
            if (rect.top < 0) {
                iframe.style.position = 'fixed';
                iframe.style.top = '0';
                iframe.style.left = '240px';
                iframe.style.width = 'calc(100% - 240px)';
                iframe.style.height = 'calc(100vh - 56px)'; // Adjust height as needed
            } else {
                iframe.style.position = 'relative';
                iframe.style.top = '0';
                iframe.style.left = '240px';
                iframe.style.width = 'calc(100% - 240px)';
                iframe.style.height = '100%';
            }
        }
    };

    // Throttle helper to avoid rapid actions
    function throttle(fn, wait) {
        let last = 0;
        let timeout = null;
        return function (...args) {
            const now = Date.now();
            const remaining = wait - (now - last);
            const context = this;
            if (remaining <= 0) {
                if (timeout) { clearTimeout(timeout); timeout = null; }
                last = now;
                fn.apply(context, args);
            } else if (!timeout) {
                timeout = setTimeout(() => {
                    last = Date.now();
                    timeout = null;
                    fn.apply(context, args);
                }, remaining);
            }
        };
    }

    // Optimized settings management
    function loadSettings() {
        const saved = GM_getValue(CONSTANTS.STORAGE_KEY, {});
        return { ...defaultSettings, ...saved };
    }

    const saveSettings = () => GM_setValue(CONSTANTS.STORAGE_KEY, settings);

    function createSettingsMenu() {
        GM_registerMenuCommand('YouTube Enchantments Settings', showSettingsDialog);
    }

    function showSettingsDialog() {
        let dialog = document.getElementById('youtube-enchantments-settings');
        if (!dialog) {
            dialog = createSettingsDialog();
            document.body.appendChild(dialog);
        }
        dialog.style.display = 'block';
    }

    function createSettingsDialog() {
        const wrapper = document.createElement('div');
        wrapper.id = 'youtube-enchantments-settings';

        // Styles (Trusted Types safe)
        const styleEl = document.createElement('style');
        styleEl.textContent = `
            .dpe-dialog {
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                background: #030d22;
                border: 1px solid #2a2945;
                border-radius: 12px;
                padding: 24px;
                box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
                z-index: 9999;
                color: #ffffff;
                width: 320px;
                font-family: 'Roboto', Arial, sans-serif;
            }
            .dpe-dialog h3 {
                margin-top: 0;
                font-size: 1.8em;
                text-align: center;
                margin-bottom: 24px;
                color: #ffffff;
                font-weight: 700;
            }
            .dpe-toggle-container {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 16px;
                padding: 8px;
                border-radius: 8px;
                background: #15132a;
                transition: background-color 0.2s;
            }
            .dpe-toggle-container:hover { background: #1a1832; }
            .dpe-toggle-label {
                flex-grow: 1;
                color: #ffffff;
                font-size: 1.1em;
                font-weight: 600;
                margin-left: 12px;
            }
            .dpe-toggle { position: relative; display: inline-block; width: 46px; height: 24px; }
            .dpe-toggle input {
                position: absolute; width: 100%; height: 100%; opacity: 0; cursor: pointer; margin: 0;
            }
            .dpe-toggle-slider {
                position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0;
                background-color: #2a2945; transition: .3s; border-radius: 24px;
            }
            .dpe-toggle-slider:before {
                position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px;
                background-color: #ffffff; transition: .3s; border-radius: 50%;
            }
            .dpe-toggle input:checked + .dpe-toggle-slider { background-color: #cc0000; }
            .dpe-toggle input:checked + .dpe-toggle-slider:before { transform: translateX(22px); }
            .dpe-slider-container { margin: 24px 0; padding: 12px; background: #15132a; border-radius: 8px; }
            .dpe-slider-container label { display: block; margin-bottom: 8px; color: #ffffff; font-size: 1.1em; font-weight: 600; }
            .dpe-slider-container input[type="range"] {
                width: 100%; margin: 8px 0; height: 4px; background: #2a2945; border-radius: 2px; -webkit-appearance: none;
            }
            .dpe-slider-container input[type="range"]::-webkit-slider-thumb {
                -webkit-appearance: none; width: 16px; height: 16px; background: #cc0000; border-radius: 50%; cursor: pointer; transition: background-color 0.2s;
            }
            .dpe-slider-container input[type="range"]::-webkit-slider-thumb:hover { background: #990000; }
            .dpe-button-container { display: flex; justify-content: space-between; margin-top: 24px; gap: 12px; }
            .dpe-button { padding: 10px 20px; border: none; border-radius: 6px; cursor: pointer; font-size: 1.1em; font-weight: 600; transition: all 0.2s; flex: 1; }
            .dpe-button-save { background-color: #cc0000; color: white; }
            .dpe-button-save:hover { background-color: #990000; transform: translateY(-1px); }
            .dpe-button-cancel { background-color: #15132a; color: white; border: 1px solid #2a2945; }
            .dpe-button-cancel:hover { background-color: #1a1832; transform: translateY(-1px); }
        `;

        const container = document.createElement('div');
        container.className = 'dpe-dialog';

        const title = document.createElement('h3');
        title.textContent = 'YouTube Enchantments';
        container.appendChild(title);

        // Toggles
        container.appendChild(createToggle('autoLikeEnabled', 'Auto Like', 'Automatically like videos of subscribed channels'));
        container.appendChild(createToggle('autoLikeLiveStreams', 'Like Live Streams', 'Include live streams in auto-like feature'));
        container.appendChild(createToggle('likeIfNotSubscribed', 'Like All Videos', 'Like videos even if not subscribed'));
        container.appendChild(createToggle('adBlockBypassEnabled', 'AdBlock Bypass', 'Bypass AdBlock detection'));
        container.appendChild(createToggle('removeGamesEnabled', 'Remove Games', 'Hide game sections from YouTube homepage'));
        container.appendChild(createToggle('loggingEnabled', 'Logging', 'Enable or disable console logging'));

        // Watch Threshold slider
        const wt = document.createElement('div');
        wt.className = 'dpe-slider-container';
        wt.title = 'Percentage of video to watch before liking';
        const wtLabel = document.createElement('label');
        wtLabel.setAttribute('for', 'watchThreshold');
        wtLabel.textContent = 'Watch Threshold';
        const wtInput = document.createElement('input');
        wtInput.type = 'range'; wtInput.id = 'watchThreshold'; wtInput.min = '0'; wtInput.max = '100'; wtInput.step = '10';
        wtInput.setAttribute('data-setting', 'watchThreshold');
        wtInput.value = String(settings.watchThreshold);
        const wtValue = document.createElement('span');
        wtValue.id = 'watchThresholdValue';
        wtValue.textContent = `${settings.watchThreshold}%`;
        wt.appendChild(wtLabel); wt.appendChild(wtInput); wt.appendChild(wtValue);
        container.appendChild(wt);

        // Scroll Speed slider
        const ss = document.createElement('div');
        ss.className = 'dpe-slider-container';
        ss.title = 'Speed of auto-scroll (pixels per interval)';
        const ssLabel = document.createElement('label');
        ssLabel.setAttribute('for', 'scrollSpeed');
        ssLabel.textContent = 'Scroll Speed';
        const ssInput = document.createElement('input');
        ssInput.type = 'range'; ssInput.id = 'scrollSpeed'; ssInput.min = '10'; ssInput.max = '100'; ssInput.step = '5';
        ssInput.setAttribute('data-setting', 'scrollSpeed');
        ssInput.value = String(settings.scrollSpeed);
        const ssValue = document.createElement('span');
        ssValue.id = 'scrollSpeedValue';
        ssValue.textContent = `${settings.scrollSpeed}px`;
        ss.appendChild(ssLabel); ss.appendChild(ssInput); ss.appendChild(ssValue);
        container.appendChild(ss);

        // Check Frequency slider
        const cf = document.createElement('div');
        cf.className = 'dpe-slider-container';
        cf.title = 'Interval for auto-like checks (milliseconds)';
        const cfLabel = document.createElement('label');
        cfLabel.setAttribute('for', 'checkFrequency');
        cfLabel.textContent = 'Check Frequency (ms)';
        const cfInput = document.createElement('input');
        cfInput.type = 'range'; cfInput.id = 'checkFrequency'; cfInput.min = String(CONSTANTS.MIN_CHECK_FREQUENCY); cfInput.max = String(CONSTANTS.MAX_CHECK_FREQUENCY); cfInput.step = '500';
        cfInput.setAttribute('data-setting', 'checkFrequency');
        cfInput.value = String(settings.checkFrequency);
        const cfValue = document.createElement('span');
        cfValue.id = 'checkFrequencyValue';
        cfValue.textContent = `${settings.checkFrequency}ms`;
        cf.appendChild(cfLabel); cf.appendChild(cfInput); cf.appendChild(cfValue);
        container.appendChild(cf);

        // Buttons
        const btns = document.createElement('div');
        btns.className = 'dpe-button-container';
        const saveBtn = document.createElement('button');
        saveBtn.id = 'saveSettingsButton'; saveBtn.className = 'dpe-button dpe-button-save';
        saveBtn.textContent = 'Save';
        const cancelBtn = document.createElement('button');
        cancelBtn.id = 'closeSettingsButton'; cancelBtn.className = 'dpe-button dpe-button-cancel';
        cancelBtn.textContent = 'Cancel';
        btns.appendChild(saveBtn); btns.appendChild(cancelBtn);
        container.appendChild(btns);

        // Compose
        wrapper.appendChild(styleEl);
        wrapper.appendChild(container);

        // Listeners
        saveBtn.addEventListener('click', () => { saveSettings(); hideSettingsDialog(); });
        cancelBtn.addEventListener('click', hideSettingsDialog);
        wrapper.querySelectorAll('.dpe-toggle input').forEach(toggle => {
            toggle.addEventListener('change', handleSettingChange);
        });
        wtInput.addEventListener('input', handleSliderInput);
        ssInput.addEventListener('input', handleSliderInput);
        cfInput.addEventListener('input', handleSliderInput);

        return wrapper;
    }

    function createToggle(id, label, title) {
        const container = document.createElement('div');
        container.className = 'dpe-toggle-container';
        container.title = title;

        const toggleLabel = document.createElement('label');
        toggleLabel.className = 'dpe-toggle';
        const input = document.createElement('input');
        input.type = 'checkbox';
        input.setAttribute('data-setting', id);
        if (settings[id]) input.checked = true;
        const slider = document.createElement('span');
        slider.className = 'dpe-toggle-slider';
        toggleLabel.appendChild(input);
        toggleLabel.appendChild(slider);

        const textLabel = document.createElement('label');
        textLabel.className = 'dpe-toggle-label';
        textLabel.textContent = label;

        container.appendChild(toggleLabel);
        container.appendChild(textLabel);
        return container;
    }

    function hideSettingsDialog() {
        const dialog = document.getElementById('youtube-enchantments-settings');
        if (dialog) {
            dialog.style.display = 'none';
        }
    }

    function formatSettingName(setting) {
        return setting.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase());
    }

    function handleSettingChange(e) {
        if (e.target.dataset.setting) {
            if (e.target.type === 'checkbox') {
                toggleSetting(e.target.dataset.setting);
            } else if (e.target.type === 'range') {
                updateNumericSetting(e.target.dataset.setting, e.target.value);
            }

            // Log the status of adBlockBypassEnabled if it is changed
            if (e.target.dataset.setting === 'adBlockBypassEnabled') {
                Logger.info(`AdBlock Ban Bypass is ${e.target.checked ? 'enabled' : 'disabled'}`);
            }
            if (e.target.dataset.setting === 'loggingEnabled') {
                Logger.enabled = !!settings.loggingEnabled;
                Logger.info(`Logging ${Logger.enabled ? 'enabled' : 'disabled'}`);
            }
            if (e.target.dataset.setting === 'checkFrequency') {
                // Apply new frequency immediately
                restartBackgroundCheck();
            }
        }
    }


    function handleSliderInput(e) {
        if (e.target.type === 'range') {
            const value = e.target.value;
            if (e.target.id === 'watchThreshold') {
                document.getElementById('watchThresholdValue').textContent = `${value}%`;
                updateNumericSetting('watchThreshold', value);
            } else if (e.target.id === 'scrollSpeed') {
                document.getElementById('scrollSpeedValue').textContent = `${value}px`;
                updateNumericSetting('scrollSpeed', value);
            } else if (e.target.id === 'checkFrequency') {
                document.getElementById('checkFrequencyValue').textContent = `${value}ms`;
                updateNumericSetting('checkFrequency', value);
                restartBackgroundCheck();
            }
        }
    }

    function toggleSetting(settingName) {
        settings[settingName] = !settings[settingName];
        saveSettings();
        if (settingName === 'loggingEnabled') {
            Logger.enabled = !!settings.loggingEnabled;
        }
    }

    function updateNumericSetting(settingName, value) {
        let v = parseInt(value, 10);
        if (settingName === 'checkFrequency') {
            if (!Number.isFinite(v)) v = defaultSettings.checkFrequency;
            v = Math.min(CONSTANTS.MAX_CHECK_FREQUENCY, Math.max(CONSTANTS.MIN_CHECK_FREQUENCY, v));
        }
        settings[settingName] = v;
        saveSettings();
    }

    function startBackgroundCheck() {
        restartBackgroundCheck();
    }

    function restartBackgroundCheck() {
        if (checkTimer) clearInterval(checkTimer);
        const freq = Math.min(CONSTANTS.MAX_CHECK_FREQUENCY, Math.max(CONSTANTS.MIN_CHECK_FREQUENCY, settings.checkFrequency || defaultSettings.checkFrequency));
        const throttled = throttle(checkAndLikeVideo, Math.max(750, Math.floor(freq / 2)));
        checkTimer = setInterval(throttled, freq);
        Logger.info(`Background check started (every ${freq}ms)`);
    }

    let isChecking = false;
    function checkAndLikeVideo() {
        if (isChecking) return; // prevent reentrancy
        isChecking = true;
        Logger.info('Checking if video should be liked...');
        if (watchThresholdReached()) {
            Logger.info('Watch threshold reached.');
            if (settings.autoLikeEnabled) {
                Logger.info('Auto-like is enabled.');
                if (settings.likeIfNotSubscribed || isSubscribed()) {
                    Logger.info('User is subscribed or likeIfNotSubscribed is enabled.');
                    if (settings.autoLikeLiveStreams || !isLiveStream()) {
                        Logger.info('Video is not a live stream or auto-like for live streams is enabled.');
                        likeVideo();
                    } else {
                        Logger.info('Video is a live stream and auto-like for live streams is disabled.');
                    }
                } else {
                    Logger.info('User is not subscribed and likeIfNotSubscribed is disabled.');
                }
            } else {
                Logger.info('Auto-like is disabled.');
            }
        } else {
            Logger.info('Watch threshold not reached.');
        }
        isChecking = false;
    }

    function watchThresholdReached() {
        const player = document.querySelector(SELECTORS.PLAYER);
        if (player && typeof player.getCurrentTime === 'function' && typeof player.getDuration === 'function') {
            const currentTime = player.getCurrentTime();
            const duration = player.getDuration();

            if (duration > 0) {
                const watched = currentTime / duration;
                const watchedTarget = settings.watchThreshold / 100;
                if (watched < watchedTarget) {
                    Logger.info(`Waiting until watch threshold reached (${(watched * 100).toFixed(1)}%/${settings.watchThreshold}%)...`);
                    return false;
                }
            }
        }
        return true;
    }

    // Optimized subscription detection
    function isSubscribed() {
        const subscribeButton = document.querySelector(SELECTORS.SUBSCRIBE_BUTTON);
        if (!subscribeButton) {
            Logger.info('Subscribe button not found');
            return false;
        }

        const isSubbed = subscribeButton.hasAttribute('subscribe-button-invisible') ||
            subscribeButton.hasAttribute('subscribed') ||
            /subscrib/i.test(subscribeButton.textContent);

        Logger.info(`Subscribe button found: true, Is subscribed: ${isSubbed}`);
        return isSubbed;
    }

    function isLiveStream() {
        try {
            const liveBadge = document.querySelector(SELECTORS.LIVE_BADGE);
            if (liveBadge && window.getComputedStyle(liveBadge).display !== 'none') return true;
            // Shorts live is rare; fallback false
            return false;
        } catch (_) { return false; }
    }

    function likeVideo() {
        Logger.info('Attempting to like the video...');
        const likeButton = document.querySelector(SELECTORS.LIKE_BUTTON);
        const dislikeButton = document.querySelector(SELECTORS.DISLIKE_BUTTON);
        const videoId = getVideoId();

        Logger.info(`Like button found: ${!!likeButton}`);
        Logger.info(`Dislike button found: ${!!dislikeButton}`);
        Logger.info(`Video ID: ${videoId}`);

        if (!likeButton || !dislikeButton || !videoId) {
            Logger.info('Like button, dislike button, or video ID not found.');
            return;
        }

        const likePressed = isButtonPressed(likeButton);
        const dislikePressed = isButtonPressed(dislikeButton);
        const alreadyAutoLiked = autoLikedVideoIds.has(videoId);

        Logger.info(`Like button pressed: ${likePressed}`);
        Logger.info(`Dislike button pressed: ${dislikePressed}`);
        Logger.info(`Already auto-liked: ${alreadyAutoLiked}`);

        if (!likePressed && !dislikePressed && !alreadyAutoLiked) {
            Logger.info('Liking the video...');
            likeButton.click();

            // Check again after a short delay
            setTimeout(() => {
                if (isButtonPressed(likeButton)) {
                    Logger.success('Video liked successfully.');
                    autoLikedVideoIds.add(videoId);
                } else {
                    Logger.warning('Failed to like the video.');
                }
            }, 500);
        } else {
            Logger.info('Video already liked or disliked, or already auto-liked.');
        }
    }

    function isButtonPressed(button) {
        if (!button) return false;
        const pressed = button.classList.contains('style-default-active') || button.getAttribute('aria-pressed') === 'true';
        const toggled = button.closest('ytd-toggle-button-renderer')?.getAttribute('aria-pressed') === 'true';
        return pressed || toggled;
    }

    function getVideoId() {
        // Watch page
        const watchFlexyElem = document.querySelector('#page-manager > ytd-watch-flexy');
        if (watchFlexyElem && watchFlexyElem.hasAttribute('video-id')) {
            return watchFlexyElem.getAttribute('video-id');
        }
        // Shorts URL pattern
        const path = window.location.pathname;
        const shortsMatch = path.match(/^\/shorts\/([\w-]{5,})/);
        if (shortsMatch) return shortsMatch[1];
        // Fallback to query param
        const urlParams = new URLSearchParams(window.location.search);
        return urlParams.get('v');
    }

    function handleAdBlockError() {
        if (!settings.adBlockBypassEnabled) {
            Logger.info('AdBlock bypass disabled');
            return;
        }

        const playabilityError = document.querySelector(SELECTORS.PLAYABILITY_ERROR);
        if (playabilityError) {
            playabilityError.remove();
            playerManager.replacePlayer(window.location.href);
        } else if (tries < CONSTANTS.MAX_TRIES) {
            tries++;
            setTimeout(handleAdBlockError, CONSTANTS.DELAY);
        }
    }

    function redirectToVideosPage() {
        const currentUrl = window.location.href;

        // Handle new format @username channels
        if (currentUrl.includes('/@')) {
            if (currentUrl.endsWith('/featured') || currentUrl.includes('/featured?')) {
                const videosUrl = currentUrl.replace(/\/featured(\?.*)?$/, '/videos');
                Logger.info(`Redirecting to videos page: ${videosUrl}`);
                window.location.replace(videosUrl);
                return true;
            } else if (currentUrl.match(/\/@[^\/]+\/?(\?.*)?$/)) {
                const videosUrl = currentUrl.replace(/\/?(\?.*)?$/, '/videos');
                Logger.info(`Redirecting to videos page: ${videosUrl}`);
                window.location.replace(videosUrl);
                return true;
            }
        }

        // Handle legacy channel URLs
        if (currentUrl.includes('/channel/')) {
            if (currentUrl.endsWith('/featured') || currentUrl.includes('/featured?')) {
                const videosUrl = currentUrl.replace(/\/featured(\?.*)?$/, '/videos');
                Logger.info(`Redirecting to videos page: ${videosUrl}`);
                window.location.replace(videosUrl);
                return true;
            } else if (currentUrl.match(/\/channel\/[^\/]+\/?(\?.*)?$/)) {
                const videosUrl = currentUrl.replace(/\/?(\?.*)?$/, '/videos');
                Logger.info(`Redirecting to videos page: ${videosUrl}`);
                window.location.replace(videosUrl);
                return true;
            }
        }

        return false;
    }

    // Remove redundant functions
    const toggleSettingsDialog = () => {
        const dialog = document.getElementById('youtube-enchantments-settings');
        if (dialog && dialog.style.display === 'block') {
            hideSettingsDialog();
        } else {
            showSettingsDialog();
        }
    };

    const toggleScrolling = () => {
        if (isScrolling) {
            clearInterval(scrollInterval);
            isScrolling = false;
        } else {
            isScrolling = true;
            scrollInterval = setInterval(() => window.scrollBy(0, settings.scrollSpeed), 20);
        }
    };

    const handlePageUp = () => {
        if (isScrolling) {
            clearInterval(scrollInterval);
            isScrolling = false;
        } else {
            window.scrollTo(0, 0);
        }
    };

    // Observe like button readiness to reduce polling pressure
    function observeLikeButtonReady() {
        if (likeReadyObserver) likeReadyObserver.disconnect();
        likeReadyObserver = new MutationObserver(() => {
            const btn = document.querySelector(SELECTORS.LIKE_BUTTON);
            if (btn) {
                Logger.info('Like button detected by observer');
                // Trigger a check once when like button shows up
                checkAndLikeVideo();
                if (likeReadyObserver) likeReadyObserver.disconnect();
            }
        });
        likeReadyObserver.observe(document.body, { childList: true, subtree: true });
    }

    // Optimized event handling
    function setupEventListeners() {
        // Page navigation
        window.addEventListener('beforeunload', () => {
            currentPageUrl = window.location.href;
            cleanup();
        });

        document.addEventListener('yt-navigate-finish', () => {
            Logger.info('Page navigation detected');
            const newUrl = window.location.href;
            if (newUrl !== currentPageUrl) {
                Logger.info(`URL changed: ${newUrl}`);

                if (redirectToVideosPage()) return;

                if (newUrl.endsWith('.com/')) {
                    const iframe = document.getElementById(CONSTANTS.IFRAME_ID);
                    if (iframe) {
                        Logger.info('Removing iframe');
                        iframe.remove();
                    }
                } else {
                    Logger.info('Handling potential ad block error');
                    handleAdBlockError();
                }
                currentPageUrl = newUrl;
                // Stop auto-scroll on navigation
                if (isScrolling && scrollInterval) { clearInterval(scrollInterval); isScrolling = false; }
                // Restart background checks and observers on navigation
                restartBackgroundCheck();
                observeLikeButtonReady();
            }
        });

        window.addEventListener('popstate', () => {
            Logger.info('popstate detected');
            const newUrl = window.location.href;
            if (newUrl !== currentPageUrl) {
                currentPageUrl = newUrl;
                restartBackgroundCheck();
                observeLikeButtonReady();
            }
        });

        // Keyboard shortcuts (capture early to avoid site handlers swallowing keys)
        document.addEventListener('keydown', (event) => {
            const tag = (event.target && event.target.tagName) ? event.target.tagName.toLowerCase() : '';
            const isEditable = tag === 'input' || tag === 'textarea' || (event.target && event.target.isContentEditable);

            switch (event.key) {
                case 'F2':
                    // Avoid triggering while typing inside inputs/textareas
                    if (!isEditable) {
                        event.preventDefault();
                        event.stopPropagation();
                        Logger.info('F2 pressed - toggling settings dialog');
                        toggleSettingsDialog();
                    }
                    break;
                case 'PageDown':
                    if (!isEditable) {
                        event.preventDefault();
                        toggleScrolling();
                    }
                    break;
                case 'PageUp':
                    if (!isEditable) {
                        event.preventDefault();
                        handlePageUp();
                    }
                    break;
            }
        }, true);

        // DOM observer for ad block errors
        adBlockObserver = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                if (mutation.type === 'childList') {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE &&
                            node.matches(SELECTORS.PLAYABILITY_ERROR)) {
                            Logger.info('Playability error detected');
                            handleAdBlockError();
                            return;
                        }
                    }
                }
            }
        });
        adBlockObserver.observe(document.body, { childList: true, subtree: true });

        // Periodic tasks
        duplicateCleanupInterval = setInterval(() => playerManager.removeDuplicates(), CONSTANTS.DUPLICATE_CHECK_INTERVAL);
        gameHideInterval = setInterval(hideGameSections, CONSTANTS.GAME_CHECK_INTERVAL);

        // Initial like button observation
        observeLikeButtonReady();
    }

    function hideGameSections() {
        if (!settings.removeGamesEnabled) return;

        const allSections = document.querySelectorAll(SELECTORS.GAME_SECTION);
        if (allSections.length > 0) {
            allSections.forEach(section => {
                // Check if this is a game section using DOM traversal
                if (isGameSection(section)) {
                    section.style.display = 'none';
                    Logger.success('Game section hidden');
                }
            });
        }
    }

    // Optimized game section detection
    function isGameSection(section) {
        // Quick checks first
        if (section.querySelectorAll('div#dismissible.style-scope.ytd-rich-shelf-renderer').length > 0) return true;
        if (section.querySelectorAll('ytd-mini-game-card-view-model').length > 0) return true;
        if (section.querySelectorAll('a[href*="/playables"], a[href*="gaming"]').length > 0) return true;

        // Text-based checks
        const titleElement = section.querySelector('#title-text span');
        if (titleElement && /game|jugable/i.test(titleElement.textContent)) return true;

        // Aria-label checks
        const richShelfElements = section.querySelectorAll('ytd-rich-shelf-renderer');
        for (const element of richShelfElements) {
            const ariaLabel = element.getAttribute('aria-label');
            if (ariaLabel && /game|juego/i.test(ariaLabel)) return true;
        }

        // Genre checks
        const gameGenres = ['Arcade', 'Racing', 'Sports', 'Action', 'Puzzles', 'Music', 'Carreras', 'Deportes', 'Acción', 'Puzles', 'Música'];
        const genreSpans = section.querySelectorAll('.yt-mini-game-card-view-model__genre');
        return Array.from(genreSpans).some(span =>
            gameGenres.some(genre => span.textContent.includes(genre))
        );
    }

    // Optimized initialization
    async function initScript() {
        try {
            Logger.info('Initializing YouTube Enchantments');

            // Setup core functionality
            createSettingsMenu();
            setupEventListeners();
            redirectToVideosPage();

            // Initialize auto-like system (interval-based)
            startBackgroundCheck();

            // Initialize game section removal
            hideGameSections();

            Logger.info('Script initialization complete');
        } catch (error) {
            Logger.error(`Initialization failed: ${error}`);
        }
    }

    function cleanup() {
        try {
            if (checkTimer) { clearInterval(checkTimer); checkTimer = null; }
            if (duplicateCleanupInterval) { clearInterval(duplicateCleanupInterval); duplicateCleanupInterval = null; }
            if (gameHideInterval) { clearInterval(gameHideInterval); gameHideInterval = null; }
            if (adBlockObserver) { adBlockObserver.disconnect(); adBlockObserver = null; }
            if (likeReadyObserver) { likeReadyObserver.disconnect(); likeReadyObserver = null; }
            if (isScrolling && scrollInterval) { clearInterval(scrollInterval); isScrolling = false; }
        } catch (e) { /* no-op */ }
    }

    initScript();
})();