Greasy Fork

Greasy Fork is available in English.

硬核YouTube广告拦截器

无API依赖的极简高效广告拦截方案

目前为 2025-03-29 提交的版本。查看 最新版本

// ==UserScript==
// @name        硬核YouTube广告拦截器
// @version     3.1.5
// @description 无API依赖的极简高效广告拦截方案
// @icon        https://www.youtube.com/favicon.ico
// @author      little fool
// @match       *://*.youtube.com/*
// @grant       none
// @run-at      document-start
// @namespace   https://direct.blocker
// ==/UserScript==

(function() {
    'use strict';

    // 原子级广告特征库
    const AD_SIGNATURES = {
        dom: [
            'ytd-ad-',           // YouTube广告组件前缀
            '#player-ads',      // 播放器广告容器
            '.ytp-ad-',         // 播放器广告相关
            '[aria-label="广告"]', // 语义识别
            'div[id^="ad-"]',    // 通用广告标识
            'ytd-promoted-',     // 推广内容
            'ytm-companion-ad',  // 移动端伴生广告
            'ytd-action-companion-ad' // 操作栏广告
        ],
        network: [
            /\/ad(\d|s|vert)/,    // 广告路径特征
            /doubleclick\.net/,   // 广告服务域名
            /\/pagead\//,        // 谷歌广告路径
            /\/log_event\?ad_/,  // 广告日志事件
            /\/get_midroll_/     // 片中广告请求
        ]
    };

    // 暴力DOM清理系统
    const domHunter = {
        observer: null,
        scan() {
            const scanner = setInterval(() => {
                this.clean(document.documentElement);
            }, 1500);

            this.observer = new MutationObserver(muts => {
                muts.forEach(mut => this.clean(mut.target));
            });

            this.observer.observe(document, {
                childList: true,
                subtree: true,
                attributes: false
            });
        },

        clean(root) {
            AD_SIGNATURES.dom.forEach(selector => {
                root.querySelectorAll(selector).forEach(ad => {
                    ad.remove();
                    this.fixVideoPlayer();
                });
            });
        },

        fixVideoPlayer() {
            const player = document.querySelector('#movie_player');
            if (player) {
                player.style.height = '100vh';
                player.style.width = '100%';
            }
        }
    };

    // 网络请求截杀系统
    const networkInterceptor = {
        init() {
            this.hijackFetch();
            this.hijackXHR();
        },

        hijackFetch() {
            const nativeFetch = window.fetch;
            window.fetch = (input) => 
                this.isAdRequest(input.url) 
                    ? new Response(null, {status: 403}) 
                    : nativeFetch(input);
        },

        hijackXHR() {
            const nativeOpen = XMLHttpRequest.prototype.open;
            XMLHttpRequest.prototype.open = function(_, url) {
                this._url = url;
                nativeOpen.apply(this, arguments);
            };

            const nativeSend = XMLHttpRequest.prototype.send;
            XMLHttpRequest.prototype.send = function() {
                this.isAdRequest() ? this.abort() : nativeSend.apply(this, arguments);
            };

            XMLHttpRequest.prototype.isAdRequest = function() {
                return AD_SIGNATURES.network.some(regex => regex.test(this._url));
            };
        },

        isAdRequest(url) {
            return AD_SIGNATURES.network.some(regex => regex.test(url));
        }
    };

    // 初始化系统
    domHunter.scan();
    networkInterceptor.init();

    // 视觉修正补丁
    const stylePatch = `
        ytd-display-ad-renderer, 
        .ytp-ad-module, 
        [aria-label="广告"] {
            display: none !important;
            height: 0 !important;
            opacity: 0 !important;
            pointer-events: none !important;
        }

        #player.ytd-watch {
            height: 100vh !important;
            min-height: 100vh !important;
        }

        .ytp-ad-progress-list,
        .ytp-ad-skip-button-container {
            visibility: hidden !important;
        }
    `;

    document.addEventListener('DOMContentLoaded', () => {
        const style = document.createElement('style');
        style.textContent = stylePatch;
        document.head.append(style);
    });
})();