YouTube Ad-Master

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

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

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

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

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

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

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

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.

(I already have a user style manager, let me install it!)

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