YouTube Enchantments

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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();
})();