YouTube 广告屏蔽跳过

阻止YouTube广告和修改请求/响应,以获得更好的体验。

// ==UserScript==
// @name         YouTube 广告屏蔽跳过
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  阻止YouTube广告和修改请求/响应,以获得更好的体验。
// @match        *://*.googlevideo.com/*
// @match        *://www.youtube.com/*
// @match        *://s.youtube.com/*
// @match        *://youtubei.googleapis.com/*
// @namespace    https://greasyfork.org/zh-CN/scripts/499041-youtube-%E5%B9%BF%E5%91%8A%E5%B1%8F%E8%94%BD%E8%B7%B3%E8%BF%87
// @grant        none
// @license      GPL-3.0
// ==/UserScript==

(function() {
    'use strict';

    const blockList = [
        /^https?:\/\/(www|s)\.youtube\.com\/api\/stats\/ads/,
        /^https?:\/\/(www|s)\.youtube\.com\/(pagead|ptracking)/,
        /^https?:\/\/s\.youtube\.com\/api\/stats\/qoe\?adcontext/
    ];

    const redirectList = [
        {
            regex: /^https?:\/\/[\w-]+\.googlevideo\.com\/(?!dclk_video_ads).+?&ctier=L(&.+?),ctier,(.+)/,
            target: (match) => `${match[1]}&ctier=L${match[2]}${match[3]}`
        }
    ];

    const modifyList = [
        {
            regex: /^https?:\/\/youtubei\.googleapis\.com\/youtubei\/v1\/(browse|next|player|reel\/reel_watch_sequence|get_watch)/,
            script: 'https://raw.githubusercontent.com/Maasea/sgmodule/master/Script/Youtube/dist/youtube.request.preview.js',
            type: 'request'
        },
        {
            regex: /^https?:\/\/youtubei\.googleapis\.com\/youtubei\/v1\/(browse|next|player|search|reel\/reel_watch_sequence|guide|account\/get_setting|get_watch)/,
            script: 'https://raw.githubusercontent.com/Maasea/sgmodule/master/Script/Youtube/dist/youtube.response.preview.js',
            type: 'response'
        }
    ];

    // Block unwanted URLs
    blockList.forEach((regex) => {
        const xhrOpen = XMLHttpRequest.prototype.open;
        XMLHttpRequest.prototype.open = function(method, url) {
            if (regex.test(url)) {
                console.log('阻止请求:', url);
                this.abort();
            } else {
                xhrOpen.apply(this, arguments);
            }
        };
    });

    // Redirect specific URLs
    redirectList.forEach((item) => {
        const xhrOpen = XMLHttpRequest.prototype.open;
        XMLHttpRequest.prototype.open = function(method, url) {
            const match = url.match(item.regex);
            if (match) {
                const newUrl = item.target(match);
                console.log('重定向:', url, 'to', newUrl);
                url = newUrl;
            }
            xhrOpen.apply(this, arguments);
        };
    });

    // Modify requests and responses
    modifyList.forEach((item) => {
        const xhrSend = XMLHttpRequest.prototype.send;
        XMLHttpRequest.prototype.send = function(data) {
            if (item.regex.test(this._url)) {
                fetch(item.script)
                    .then(response => response.text())
                    .then(script => {
                    eval(script);
                    xhrSend.apply(this, arguments);
                })
                    .catch(err => {
                    console.error('获取脚本失败:', err);
                    xhrSend.apply(this, arguments);
                });
            } else {
                xhrSend.apply(this, arguments);
            }
        };

        const xhrOpen = XMLHttpRequest.prototype.open;
        XMLHttpRequest.prototype.open = function(method, url) {
            this._url = url;
            xhrOpen.apply(this, arguments);
        };
    });

    // Hide in-page ad elements
    function hideAds() {
        const adSlots = document.querySelectorAll('ytd-ad-slot-renderer');
        adSlots.forEach(adSlot => {
            const richItemRenderer = adSlot.closest('ytd-rich-item-renderer');
            if (richItemRenderer) {
                richItemRenderer.style.display = 'none';
            }
        });
    }

    // Observe for changes in the DOM and hide ad elements if found
    const observer = new MutationObserver(hideAds);
    observer.observe(document.body, { childList: true, subtree: true });

    // Initial check in case ads are already present
    hideAds();

    // Auto click "Skip Ad" button
    function clickSkipButton() {
        const skipButton = document.querySelector('.ytp-skip-ad-button');
        if (skipButton) {
            console.log('自动点击跳过广告按钮');
            skipButton.click();
        }
    }

    // Observe for changes in the DOM and click "Skip Ad" button if found
    const skipButtonObserver = new MutationObserver(clickSkipButton);
    skipButtonObserver.observe(document.body, { childList: true, subtree: true });

    // Initial check in case the skip button is already present
    clickSkipButton();

})();