Greasy Fork

Greasy Fork is available in English.

YouTube Ad-Bypass

跳过并 column 隐藏 YouTube 广告 (Tiàoguò bìng yǐncáng YouTube guǎnggào)

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name              YouTube Ad-Bypass
// @namespace         YouTube_Ad-Bypass_Fckoff
// @version           1.22
// @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 跳过并 column 隐藏 YouTube 广告 (Tiàoguò bìng yǐncáng YouTube guǎnggào)
// @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';

    /**
     * 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'
        ]
    };

    /**
     * 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;

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

    /**
     * Executes the skipping logic: fast-forwards the video to the end and clicks the skip button.
     */
    const skipAction = () => {
        if (video) {
            if (video.paused) video.play();
            video.muted = true; // Mute to avoid sudden loud ad audio
            video.playbackRate = 16; // Set max speed to fly through the ad
            if (isFinite(video.duration)  && video.duration > 0) {
                // Jump to the very end of the ad segment
                video.currentTime = video.duration - 0.1;
            }
        }
        // Attempt to find and click any available "Skip" button
        const btn = document.querySelector(selectors.skipButtons);
        if (btn) btn.click();
    };

    /**
     * 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;

        // If player has ad-related classes, trigger skip; otherwise, reset playback speed
        if (target.matches(selectors.adsClasses)) {
            skipAction();
        } else {
            // Restore normal speed if the script had previously accelerated it
            if (video.playbackRate > 2) video.playbackRate = 1;
        }
    };

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

    // Event listeners to handle page loads and YouTube's internal navigation (SPA)
    window.addEventListener('load', setupPlayerObserver);
    window.addEventListener('yt-navigate-finish', setupPlayerObserver);

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

    /**
     * Fallback mechanism: attempts to initialize the observer every 250ms 
     * 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++;
    }, 250);

})();