YouTube广告拦截器

动态监测+请求拦截双重方案,支持最新YouTube广告结构

// ==UserScript==
// @name         YouTube广告拦截器
// @namespace    safe-adblock-pro
// @version      5.0
// @match        *://*.youtube.com/*
// @grant        none
// @run-at       document-start
// @description  动态监测+请求拦截双重方案,支持最新YouTube广告结构
// ==/UserScript==

(function () {
    'use strict';

    // 最新广告特征选择器 (更新于2024)
    const AD_SELECTORS = [
        'ytd-ad-slot-renderer',
        'ytd-rich-item-renderer.ytd-rich-grid-row[is-ad]', // 新版信息流广告
        'div.ytd-promoted-sparkles-web-renderer',
        'div#player-ads:not(#movie_player)',
        'ytd-action-companion-ad-renderer',
        'div.ad-showing', // 全屏广告状态
        'div.ad-container', // 新版广告容器
        'ytm-promoted-sparkles-web-renderer', // 移动端适配
        'div.ytp-ad-module',
        'div.video-ads.ytp-ad-module' // 播放器内广告
    ];

    // 广告URL特征黑名单
    const AD_URL_PATTERNS = [
        /\/pagead\/|\/ad_|\/get_midroll_/,
        /doubleclick\.net/,
        /google\.com\/pagead/,
        /\/ads\//,
        /\/adformat\//,
        /\/log_event\?action_type=ad/,
        /\/generate_204\?ad/
    ];

    // 动态广告扫描器
    const adCleaner = {
        observer: null,
        scanInterval: null,

        init() {
            // 立即执行首次扫描
            this.cleanAds();
            
            // 配置MutationObserver
            this.observer = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    if (mutation.addedNodes.length) {
                        this.cleanAds();
                    }
                });
            });

            // 开始监听DOM变化
            this.observer.observe(document, {
                childList: true,
                subtree: true
            });

            // 定时扫描作为备用方案
            this.scanInterval = setInterval(() => this.cleanAds(), 1000);
        },

        cleanAds() {
            AD_SELECTORS.forEach(selector => {
                document.querySelectorAll(selector).forEach(element => {
                    this.safeRemove(element, '选择器移除');
                });
            });

            // 处理动态插入的视频广告
            this.handleVideoAds();
        },

        safeRemove(element, reason) {
            try {
                if (element) {
                    element.style.display = 'none';
                    element.remove();
                    console.log(`[拦截日志] ${reason}: ${element.tagName} 已移除`);
                }
            } catch (e) {
                console.warn('移除元素时出错:', e);
            }
        },

        handleVideoAds() {
            // 跳过广告按钮处理
            const skipButton = document.querySelector('.ytp-ad-skip-button-modern, .ytp-ad-skip-button');
            if (skipButton) {
                skipButton.click();
                console.log('[广告拦截] 已跳过视频广告');
            }

            // 处理全屏广告覆盖
            const playerOverlay = document.querySelector('.ytp-ad-player-overlay');
            if (playerOverlay) {
                playerOverlay.style.display = 'none';
            }

            // 强制恢复视频播放
            const video = document.querySelector('video');
            if (video && video.played && video.currentTime < 1) {
                video.currentTime = 1e4;
                video.play().catch(() => {});
            }
        }
    };

    // 网络请求拦截器
    const requestBlocker = {
        init() {
            // 拦截XHR请求
            const originalXHR = window.XMLHttpRequest;
            window.XMLHttpRequest = class extends originalXHR {
                open(method, url) {
                    if (AD_URL_PATTERNS.some(pattern => pattern.test(url))) {
                        console.log(`[请求拦截] 已阻止广告请求: ${url}`);
                        return;
                    }
                    super.open(method, url);
                }
            };

            // 拦截Fetch请求
            const originalFetch = window.fetch;
            window.fetch = async (...args) => {
                const [resource] = args;
                const url = typeof resource === 'string' ? resource : resource.url;
                
                if (AD_URL_PATTERNS.some(pattern => pattern.test(url))) {
                    console.log(`[请求拦截] 已阻止广告请求: ${url}`);
                    return new Response(null, { status: 403 });
                }
                return originalFetch(...args);
            };
        }
    };

    // 播放器保护模块
    const playerProtector = {
        init() {
            const observer = new MutationObserver(() => {
                const player = document.getElementById('movie_player');
                if (player) {
                    // 禁用广告自动播放
                    player.addEventListener('onAdStart', e => e.stopImmediatePropagation());
                    player.classList.remove('ad-showing');
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }
    };

    // 初始化所有模块
    const init = () => {
        requestBlocker.init();
        playerProtector.init();
        adCleaner.init();
    };

    // 启动脚本
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    // 反检测保护
    Object.defineProperty(document, 'hidden', { value: false, configurable: true });
    Object.defineProperty(document, 'visibilityState', { value: 'visible', configurable: true });
})();