YouTube Ad-Master

解决Trusted Types与Class构造报错,优化广告跳过逻辑

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         YouTube Ad-Master
// @namespace    https://github.com/tientq64/userscripts
// @version      9.9.8.3
// @description  解决Trusted Types与Class构造报错,优化广告跳过逻辑
// @author       tientq64 + Gemini + Copilot
// @match        https://www.youtube.com/*
// @match        https://m.youtube.com/*
// @match        https://music.youtube.com/*
// @exclude      https://studio.youtube.com/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // --- 1. 解决 Trusted Types 策略错误 (修复图2/图3报错) ---
    if (window.trustedTypes && window.trustedTypes.createPolicy) {
        if (!window.trustedTypes.defaultPolicy) {
            window.trustedTypes.createPolicy('default', {
                createHTML: (string) => string,
                createScriptURL: (string) => string,
                createScript: (string) => string,
            });
        }
    }

    let state = {
        savedVolume: 1,
        savedMuted: false,
        isAdActive: false,
        volumeLocked: false
    };

    // --- 2. 安全点击与错误捕获 (修复图4 Class构造报错) ---
    const safeClick = (el) => {
        if (!el) return;
        try {
            // 优先尝试标准点击
            el.click();
            // 辅助模拟指针事件
            ['pointerdown', 'mousedown', 'mouseup'].forEach(name => {
                el.dispatchEvent(new MouseEvent(name, {
                    bubbles: true,
                    cancelable: true,
                    view: window,
                    buttons: 1
                }));
            });
        } catch (e) {
            // 捕获并忽略来自受保护组件(如 sl-popup)的构造错误
            console.debug('Skip error during click:', e.message);
        }
    };

    // --- 3. 深度 Shadow DOM 探测器 ---
    const findButtonsRecursive = (root) => {
        const selectors = [
            'button[id^="skip-button"]',
            '.ytp-ad-skip-button',
            '.ytp-ad-skip-button-modern',
            '[aria-label*="跳过"]',
            '[aria-label*="Skip"]',
            ".ytp-ad-overlay-close-button",
        ];
        let found = [];
        try {
            selectors.forEach(s => root.querySelectorAll(s).forEach(el => found.push(el)));
            root.querySelectorAll('*').forEach(el => {
                if (el.shadowRoot) found = found.concat(findButtonsRecursive(el.shadowRoot));
            });
        } catch (e) {}
        return found;
    };

    // --- 4. 动态透明度控制 ---
    const toggleAdVisibility = (isAd) => {
        const player = document.querySelector('#movie_player');
        if (!player) return;
        if (isAd) {
            player.style.setProperty('opacity', '0', 'important');
            player.style.setProperty('filter', 'brightness(0)', 'important');
            player.style.setProperty('pointer-events', 'none', 'important');
        } else {
            player.style.removeProperty('opacity');
            player.style.removeProperty('filter');
            player.style.removeProperty('pointer-events');
        }
    };

    // --- 5. 核心跳过引擎 ---
    const runSkipEngine = () => {
        const video = document.querySelector('video.html5-main-video');
        const moviePlayer = document.querySelector('#movie_player');

        const adShowing = moviePlayer && (
            moviePlayer.classList.contains('ad-showing') ||
            moviePlayer.classList.contains('ad-interrupting') ||
            document.querySelector('.ytp-ad-player-overlay')
        );

        if (adShowing && video) {
            if (!state.isAdActive) {
                state.savedVolume = video.volume;
                state.savedMuted = video.muted;
                state.isAdActive = true;
                state.volumeLocked = true;
                toggleAdVisibility(true);
            }
            video.muted = true;
            video.playbackRate = 16.0;

            // 自动点击所有发现的跳过按钮
            findButtonsRecursive(document).forEach(btn => safeClick(btn));

            // 调用内部 API
            if (moviePlayer && typeof moviePlayer.skipAd === 'function') {
                try { moviePlayer.skipAd(); } catch(e) {}
            }
        } else if (video && state.isAdActive) {
            state.isAdActive = false;
            video.playbackRate = 1.0;
            toggleAdVisibility(false);
            setTimeout(() => {
                if (state.volumeLocked && video) {
                    video.volume = state.savedVolume;
                    video.muted = state.savedMuted;
                }
            }, 200);
            state.volumeLocked = false;
        }
    };

    // --- 6. 稳健的 UI 净化 ---
    const injectStyles = () => {
        const styleId = 'yt-master-transparent-css';
        if (document.getElementById(styleId)) return;

        const style = document.createElement('style');
        style.id = styleId;
        style.textContent = `
            .ytp-ad-player-overlay, .ytp-ad-module, ytd-ad-slot-renderer,
            #masthead-ad, ytd-banner-promo-renderer {
                opacity: 0 !important;
                pointer-events: none !important;
            }
            yt-upsell-dialog-renderer, #pigeon-messaging-container { display: none !important; }
        `;

        // 确保在适当的时机插入,不干扰初始化
        const target = document.head || document.documentElement;
        if (target) {
            target.appendChild(style);
        }
    };

    // --- 7. 循环与监听 ---
    const tick = () => {
        runSkipEngine();
        requestAnimationFrame(tick);
    };

    // 初始执行
    injectStyles();
    requestAnimationFrame(tick);

    // 辅助功能:每秒清理弹窗和错误层
    setInterval(() => {
        // 自动关闭“不用了”等弹窗
        const dismiss = document.querySelectorAll('#dismiss-button, [aria-label*="thanks"], [aria-label*="不用了"]');
        dismiss.forEach(btn => safeClick(btn));

        // 刷新播放错误层
        if (document.querySelector('.yt-playability-error-supported-renderers')) {
            location.reload();
        }
    }, 1500);

    window.addEventListener('yt-navigate-finish', () => {
        state.isAdActive = false;
        state.volumeLocked = false;
        toggleAdVisibility(false);
    });
})();