YouTube Ad-Bypass

Salta y oculta anuncios de YouTube

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name              YouTube Ad-Bypass
// @namespace         YouTube_Ad-Bypass_Fckoff
// @version           1.26.3
// @description:en    Skips and hides YouTube and YouTube Music ads
// @description:es    Salta y oculta anuncios de YouTube y YouTube Music
// @description:pt    Pula e oculta anuncios do YouTube & YouTube Music
// @description:fr    Passe et masque las publicités YouTube & YouTube Music
// @description:it    Salta e nasconde gli annunci di YouTube & YouTube Music
// @description:de    Überspringe und verberge YouTube-Werbung
// @description:hi    यूट्यूब विज्ञापनों को छोड़ें और छुपाएं (YouTube vigyapanon ko chhodein aur chhupayein)
// @description:zh-CN 跳过并隐藏 YouTube 和 YouTube Music 广告
// @description:ja    YouTube 広告をスキップして非表示にします (YouTube kōkoku o sukippu shite hihyōji ni shimasu)
// @description:ru    Пропускает и скрывает рекламу на YouTube.
// @author            WakeUpNeo
// @match             *://www.youtube.com/*
// @match             *://music.youtube.com/*
// @run-at            document-start
// @grant             none
// @license           MIT
// @description Salta y oculta anuncios de YouTube
// ==/UserScript==

(function() {
    'use strict';

    let DEBUG = true;

    const log = (...a) => DEBUG && console.log('[YouTube Ad-Bypass]', ...a);

    /**
     * Configuration object containing CSS selectors for different types of ad elements.
     */
    const SELECTORS = {
        // Elements that should be visually hidden from the UI
        toHide: [
            '.ytp-ad-message-container',
            'ytd-player-legacy-desktop-watch-ads-renderer',
            'ytd-ad-slot-renderer',
            '#masthead-ad',
            'tp-yt-paper-dialog:has(#feedback.ytd-enforcement-message-view-model)',
            '.yt-mealbar-promo-renderer',
            '.video-ads',
            '.ytp-ad-module',
            '.ytp-ad-player-overlay',
            '.ad-showing > video',
            '.ad-interrupting > video',
            'div:has(> div#banner)',
            'ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]',
            'ytd-rich-item-renderer:has(ytd-ad-slot-renderer)',
            'ytmusic-mealbar-promo-renderer',
            'ytd-in-feed-ad-layout-renderer',
            '#player-ads',
            '.ytd-video-masthead-ad-v3-renderer',
            'ytd-ad-selection-preview-renderer',
            '.ytp-ad-image-overlay',
            '#root.yt-chips-search-renderer-header-v2'
        ],
        // Selectors for the main YouTube video player container
        player: [
            '#movie_player',
            '.html5-video-player'
        ],
        // Classes added by YouTube when an ad is active
        adsClasses: [
            '.ad-showing',
            '.ad-interrupting',
            '.ytp-ad-player-overlay'
        ],
        // Selectors for the various "Skip Ad" buttons
        skipButtons: [
            '.ytp-ad-skip-button-modern',
            '.ytp-skip-ad-button',
            '.ytp-ad-skip-button',
            '.ytp-ad-skip-button-slot',
            '.ytp-ad-skip-button-container'
        ]
    };

     const isAdActive = (player) => {
        return (
            player?.classList.contains('ad-showing') ||
            player?.classList.contains('ad-interrupting') ||
            player?.classList.contains('ytp-ad-player-overlay')
        );
    }

    /**
     * Convert arrays of selectors into single comma-separated strings for querySelectorAll/matches usage.
     */
    const selectors = Object.fromEntries(
       Object.entries(SELECTORS).map(([key, value]) => [key, value.join(', ')])
    );

    let playerObserver = null;
    let video = null;
    let spinner = null;
    let cued_thumb = null;
    let lastSkipAttempt = 0;

    /**
     * Injects a global <style> tag to hide ad-related elements using CSS.
     * Uses visibility:hidden and 1px size to avoid breaking layout while making ads invisible.
     */
    const injectStyles = () => {
        const style = document.createElement('style');
        style.textContent = `
            ${selectors.toHide} {
                display: flex !important;
                visibility: hidden !important;
                opacity: 0 !important;
                pointer-events: none !important;
                height: 1px !important;
                width: 1px !important;
                overflow: hidden !important;
            }
        `;
        (document.head || document.documentElement).appendChild(style);
        log('Styles injected');
    };



    /**
     * Executes the skipping logic: fast-forwards the video to the end and clicks the skip button.
     */
    const skipAction = () => {
        if (spinner){
            spinner.style.display = '';
            cued_thumb.style.display = 'none';
        }
        log('Ad paused');
        video.pause();
        video.paused = true;
        if (!video.muted) { video.muted = true; video.volume = 0; log('Ad muted'); }
        if (video.playbackRate !== 2.0) { video.playbackRate = 2.0; log('Ad accelerated'); }
        if (isFinite(video.duration) && video.duration > 0) {
            video.currentTime = video.duration - 0.1;
            log('Ad seekToEnd');
        }
        if (video.paused) { video.play(); log('Ad play');}
    };

    /**
     * Checks if the player is currently showing an ad.
     * @param {HTMLElement} target - The player element to check for ad-related classes.
     */
    const checkAndSkip = (target) => {
       // Re-fetch the video element if it's missing or disconnected from DOM
       if (!video || !video.isConnected) {
            video = document.querySelector('video');
        }
        if (!video) return;

        const now = Date.now();
        // Throttle skip attempts to avoid rapid loops
        if (now - lastSkipAttempt < 500) return;
        lastSkipAttempt = now;

        // If player has ad-related classes, trigger skip; otherwise, reset playback speed
        if (isAdActive(target)) {
            log('SkipAd start');
            skipAction();
            log('SkipAd end');
        } else {
            if (spinner){
                spinner.style.display = 'none';
                cued_thumb.style.display = 'none';
            }
            if (video.muted) video.muted = false;
            // Restore normal speed if the script had previously accelerated it
            if (video.playbackRate > 1) video.playbackRate = 1;
            log('Restore Play');
        }
    };

    /**
     * Initializes a MutationObserver to watch for changes in the player's class attribute.
     * This allows the script to react instantly when an ad starts.
     */
    const setupPlayerObserver = () => {
        const player = document.querySelector(selectors.player);

        if (player && !playerObserver) {
            playerObserver = new MutationObserver(() => checkAndSkip(player));
            // Monitor class changes which indicate ad transitions
            playerObserver.observe(player, { attributes: true, attributeFilter: ['class'] });

            // Run initial check
            checkAndSkip(player);
        }
    };

    function onNavigate() {
        log('Navigation — reinitializing');
        // Disconnect old observer (player element may be replaced)
        playerObserver?.disconnect();
        playerObserver = null;
        video = null;

        setTimeout(setupPlayerObserver, 400);
    }

    // Event listeners to handle page loads and YouTube's internal navigation (SPA)
    window.addEventListener('yt-navigate-finish', setupPlayerObserver);
    window.addEventListener('yt-page-data-updated', setupPlayerObserver);
    window.addEventListener('load', (event) => {
        setupPlayerObserver();
        spinner = document.querySelector('.ytp-spinner');
        cued_thumb = document.querySelector('.ytp-cued-thumbnail-overlay');
    });

    // Inject CSS as soon as the DOM structure is available
    window.addEventListener('DOMContentLoaded', (event) => {
        injectStyles();
    });

    /**
     * Fallback mechanism: attempts to initialize the observer every 1000ms
     * in case 'load' events fire before the player is ready.
     */
    let retry = 0;
    const fallback = setInterval(() => {
        setupPlayerObserver();
        // Stop retrying if observer is active or after 10 failed attempts
        if (playerObserver || retry > 10) clearInterval(fallback);
        retry++;
    }, 1000);

    log(' loaded!');
})();