屏蔽所有Youtube广告 Youtube AD blocker (Block all ad). Including blocking Youtube Music.

Block all video ads, insert ads, page ads. Including Youtube Music. 屏蔽所有视频广告、插入广告、页面广告。包括Youtube Music

目前为 2022-04-11 提交的版本,查看 最新版本

// ==UserScript==
// @name         屏蔽所有Youtube广告 Youtube AD blocker (Block all ad). Including blocking Youtube Music.   
// @name:zh-CN   屏蔽所有Youtube广告 包括Youtube Music
// @namespace    https://github.com/ChenZihan-sudo/Youtube-AD-blocker/
// @version      0.5 [Beta]
// @description  Block all video ads, insert ads, page ads. Including Youtube Music. 屏蔽所有视频广告、插入广告、页面广告。包括Youtube Music
// @description:zh-CN  屏蔽所有视频广告、插入广告、页面广告
// @author       ChenZihan
// @match        *.youtube.com/*
// @icon
// @grant        none
// ==/UserScript==
(function () {
    let rmAry = new Array();
    let { href } = location;

    setInterval(() => {
        // console.log("execute");
        let bufHref = location.href; //detect the change of href and execute 
        let detectAd; //detect the activity of insert ad
        if (document.getElementsByClassName("ytp-ad-preview-text").length) {
            detectAd = (document.getElementsByClassName("ytp-ad-preview-text")[0].innerHTML.constructor == String);
        }

        if (bufHref != href || detectAd) {
            console.log("[YT AD block] Detect the change of href", href);
            href = bufHref;
            executeManager('f');
            // frameworkAlreadyAdded = false; //From close video AD, change the frameworkAlreadyAdded to be false when href changed.
        }
    }, 500);

    if (location.hostname.search("youtube") != -1) {

        // action - companion - ad - info - button
        addBData('id', 'player-ads', 'f'); //add data
        addBData('id', 'masthead-ad', 'f');
        addBData('path', '#YtKevlarVisibilityIdentifier', 'f');
        addBData('class', 'ytp-ad-overlay-container', 'a');
        addBData('csClick', [['ytp-ad-feedback-dialog-reason-input', 'randomPara(0, 2)'], ['ytp-ad-feedback-dialog-confirm-button', '0'], 1], 'f');
        addBData('tag', 'ytd-display-ad-renderer', 'a');
        addBData('csClick', [['ytp-ad-skip-button', '0'], 0], 'a');
        // addBData("exe", "skipFixedAD();frameworkAlreadyAdded = true;", 'f');
        addBData("remTactic1", ["badge-style-type-ad", 7, "ytd-section-list-renderer"], 'f');
        addBData("remTactic1", ["badge-style-type-ad", 5, "ytd-item-section-renderer"], 'f');
        addBData("remTactic1", ["badge-style-type-ad", 5, "ytd-watch-next-secondary-results-renderer"], 'f');


        executeManager(); //execute at first time
    }


    /**
     * @param {String} exePara (undefined) or ('f' => execute the data for 20 times, every time interval 500ms )
     * */
    function executeManager(exePara) {
        let len = rmAry.length;


        for (let i = 0; i < len; i++) {
            if (rmAry[i][2] == 'a' && exePara == undefined) {

                setInterval(() => {
                    console.log("[YT AD block] Execute remove insert AD loop");
                    execute(); //always execute the data
                }, 800);

            } else if (rmAry[i][2] == 'f' || exePara == 'f') { //execute only in first time

                console.log("[YT AD block] Execute remove AD");

                window.onload = function () { intervalExecute(); } //if player already load
                if (exePara == 'f') { intervalExecute(); }
                intervalExecute();
                function intervalExecute() {
                    let times = 0;
                    let timer = setInterval(() => {
                        times++; if (times > 20) { clearInterval(timer); };
                        execute(); //excute 20 times in every 500ms
                    }, 500);
                }
            }

            function execute() {
                let isUniExeTag = rmAry[i][0] == "universal"; //Check is universal tag or not
                if (!isUniExeTag) {
                    let exeTag = rmAry[i][0]; //Execute Type
                    let exeData = rmAry[i][1]; //Data container of execute type
                    executeBase(exeTag, exeData);
                } else {
                    //[Beta] Execute Type: universal
                    // let exeLength = rmAry[i][1].length;
                    // for (let layeri = 0; layeri < exeLength; layeri++) {
                    //     let layerExeData = rmAry[i][1][layeri];
                    //     let exeTag = layerExeData[0];
                    //     let exeData = layerExeData[1];
                    //     executeBase(layerExeData, exeTag, exeData);
                    // }
                };
            }


            function executeBase(exeTag, exeData) {
                let cckClassDataPosi, cckType, cckCs;
                switch (exeTag) {
                    case 'id': idRm(exeData);
                        break;
                    case 'path': pathRm(exeData);
                        break;
                    case 'class': classRm(exeData);
                        break;
                    case 'csClick':
                        {
                            len = rmAry[i][1].length - 1; //find last one num
                            cckClassDataPosi = rmAry[i][1][len];
                            cckType = rmAry[i][1][len].constructor;

                            if (cckType == Number) {
                                cckCs = rmAry[i][1][cckClassDataPosi][0];
                            } else if (cckType == String) {
                                cckCs = rmAry[i][1][2];
                            }

                            csClick(cckCs, rmAry[i][1]);
                            break;
                        }
                    case 'tag': tagRm(exeData);
                        break;
                    case 'exe': { dataExe(exeData); }
                        break;
                    case 'remTactic1': {
                        let className = exeData[0];
                        let parentNum = exeData[1];
                        let expParentClassName = exeData[2];
                        remTactic1(className, parentNum, expParentClassName);
                    }
                        break;
                }
            }
        }
    }

    function addUniData(d2, d3) {
        addBData('universal', d2, d3);
    }

    function addExeData(d2, d3) {
        addBData('exe', d2, d3);
    }

    /**
     * @param d1 - Execute Type: 'id' => Use document.getElementById() to remove
     * @param d1 - Execute Type: 'path' => Use document.querySelector() to remove
     * @param d1 - Execute Type: 'class' => Use document.getElementsByClassName() to remove 
     * @param d1 - Execute Type: 'csClick' => css click event
     * @param d1 - Execute Type: 'tag' => Use document.getElementsByTagName() to remove
     * @param d1 - Execute Type: 'exe' => Use eval to execute the code
     * @param d1 - Execute Type: 'remTactic1' => See remTactic1();
     * @param d2 - Data container of d1 execute type
     * @param d3 - 'a' => Always execute the data, interval 800ms
     * @param d3 - 'f' => First execute the data for 20 times, every time interval 500ms
     */
    function addBData(d1, d2, d3) {
        let len = apdAry(rmAry);
        rmAry[len][0] = d1;
        rmAry[len][1] = d2;
        rmAry[len][2] = d3;
        rmAry[len][3] = rmAry.length - 1;
    }

    /**
     * By finding a className to find higher level of its parent element by class name and remove it
     * @param {String} className The child node class name (signature class name recommend)
     * @param {Number} parentNum How many node layers it parent elements have to get className(child node)
     * @param {String} expParentClassName The parent node class name(signature class name recommend)
     */
    function remTactic1(className, parentNum, expParentClassName) {
        var sl_length = document.getElementsByClassName(className).length;
        if (sl_length != 0) {
            if (parentNum == 0) {
                var len = document.getElementsByClassName(className).length;
                for (let i = 0; i < len; i++) {
                    document.getElementsByClassName(className)[0].remove();
                }
            } else {
                for (var s_i = 0; s_i < sl_length; s_i++) {
                    var parent = ".parentElement";
                    var finalParent = "";
                    for (var p_i = 0; p_i < parentNum; p_i++) {
                        finalParent = finalParent + parent;
                    }

                    //Find the parent name
                    var parentNode = "document.getElementsByClassName('" + className + "')[0]" + finalParent;
                    var parentNodeClassListLength = eval(parentNode + ".classList.length");
                    var isParentClassName = false;
                    for (let ii = 0; ii < parentNodeClassListLength; ii++) {
                        var parentClassName = eval(parentNode + ".classList[" + ii + "]");
                        if (parentClassName == expParentClassName) {
                            isParentClassName = true;
                            break;
                        }
                    }

                    if (isParentClassName) {
                        eval("document.getElementsByClassName('" + className + "')[0]" + finalParent + ".remove();");
                        remTactic1(className, parentNum, expParentClassName);
                        break;
                    }
                }
            }
        }
    }

    function csClick(cckCs, exAry) {
        if (document.getElementsByClassName(cckCs).length != 0) {
            let len = exAry.length;
            for (let i = 0; i < len; i++) {
                if (exAry[i].constructor != Array) { break; } //cck data type
                document.getElementsByClassName(exAry[i][0])[eval(exAry[i][1])].click(); //choose feedback option
            }
        }
    }

    function classRm(_class) {
        let times = 0;
        while (document.getElementsByClassName(_class).length != 0) {
            times++; if (times > 100) { break; }
            document.getElementsByClassName(_class)[0].remove();
        }
    }

    function idRm(id) {
        if (document.getElementById(id) != null) {
            document.getElementById(id).remove();
        }
    }

    function pathRm(path) {
        if (document.querySelector(path) != null) {
            document.querySelector(path).remove();
        }
    }

    function tagRm(tag) {
        let times = 0;
        while (document.getElementsByTagName(tag).length != 0) {
            times++; if (times > 100) { break; }
            document.getElementsByTagName(tag)[0].parentNode.parentNode.remove();
        }
    }

    function dataExe(exeData) {
        if (exeData) {
            let isArray = exeData.constructor == Array;
            if (!isArray) {
                eval(exeData);
            } else {
                //[Beta Test] Execute Type: universal
                if (exeData[0]) {
                    let finalData = exeData[0] + exeData[1];
                    eval(finalData);
                }
            }
        }
    }

    function randomPara(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    function apdAry(ary) {
        ary.splice(ary.length, 0, []);
        return ary.length - 1;
    }

    // var frameworkAlreadyAdded = false;
    // function optimizeUserExperience(type) {
    //     if (type == 1) {
    //         //Let user can't see this about ad card
    //         let adCard = document.getElementsByTagName("yt-about-this-ad-renderer")[0];
    //         if (adCard) { adCard.style.display = "none"; };

    //         //Let user can't see this backdrop
    //         let backdrop = document.getElementsByTagName("tp-yt-iron-overlay-backdrop")[0];
    //         if (backdrop) { backdrop.style.display = "none"; }
    //     }

    //     if (type == 2) {
    //         //Check the current path
    //         if (location.pathname.search("watch") != -1) {
    //             //click the play button when it load fininshed, avoid stopping in ad video. 
    //             let playButton = document.getElementsByClassName("ytp-play-button")[0];
    //             let isClick = document.getElementsByClassName("ytp-ad-preview-container").length;
    //             if (playButton && isClick) { playButton.click(); }
    //         }
    //     }

    //     if (type == 3) {
    //         //Set backdrop can be seen when ad is removed
    //         let backdrop = document.getElementsByTagName("tp-yt-iron-overlay-backdrop")[0];
    //         if (!backdrop) {
    //             backdrop.style.display = "";
    //         }
    //     }
    // }


    // /*————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
    //  Thanks for the contribution at https://github.com/ChenZihan-sudo/Youtube-AD-blocker/pull/2
    //  Author: Naccl
    //  AuthorLink: https://github.com/Naccl
    //  Function: skipFixedAD()
    // ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————*/
    // function skipFixedAD() {
    //     if (!frameworkAlreadyAdded) {
    //         console.log("triggered2");
    //         let adClickable, times = 0;
    //         let timer = setInterval(() => {
    //             //open the feedback iframe
    //             times++;
    //             adClickable = document.getElementsByClassName('ytp-ad-clickable').length;
    //             if (adClickable || times > 20) {
    //                 clearInterval(timer);
    //             };
    //         }, 500);

    //         if (adClickable) {
    //             optimizeUserExperience(1);
    //             console.log("triggered3");

    //             document.getElementsByClassName('ytp-ad-clickable')[0].click();

    //             const observerInit = { childList: true, subtree: true };

    //             //In the first time, wo should detect the iframe element.
    //             //After the first time, the iframe element will always exist.
    //             const iframeObserver = new MutationObserver(() => {
    //                 console.log("triggered4");
    //                 const iframe = document.getElementById('iframe');
    //                 if (iframe) {
    //                     iframeObserver.disconnect();

    //                     function iframeLoaded() {
    //                         iframe.removeEventListener('load', iframeLoaded, true);

    //                         //get feedback iframe document
    //                         const iframeDocument = iframe.contentWindow.document;
    //                         //click feedback button to open the confirm dialog (the element id or class are random, so we need XPath)
    //                         document.evaluate('/html/body/c-wiz/div/div/div[2]/div[2]/div/div[1]/div[1]/div/div[2]/div[2]/div/button', iframeDocument).iterateNext().click();

    //                         const iframeBody = document.evaluate('/html/body', iframeDocument).iterateNext();
    //                         const okBtnObserver = new MutationObserver(() => {
    //                             //get ok button after dialog created
    //                             const okBtn = document.evaluate('/html/body/div[2]/div/div[2]/span/div/div/div[2]/div[2]/button', iframeDocument).iterateNext();
    //                             if (okBtn) {
    //                                 okBtnObserver.disconnect();
    //                                 okBtn.click();

    //                                 //There may be delay and we need to detect the ok tips.
    //                                 //But ComputedStyle has not change event, MutationObserver can't do it.
    //                                 let okTipsTimer = setInterval(() => {
    //                                     //If the feedback is done, it will change from 'none' to 'flex'.
    //                                     const finishedTipsDisplay = getComputedStyle(document.evaluate('/html/body/c-wiz/div/div/div[2]/div[1]', iframeDocument).iterateNext(), null).display;
    //                                     if (finishedTipsDisplay !== 'none') {
    //                                         clearInterval(okTipsTimer);
    //                                         //Finished, close the iframe, the video will continue automatically.
    //                                         document.evaluate('/html/body/c-wiz/div/div/div[1]/div[2]/div/button', iframeDocument).iterateNext().click();
    //                                         // optimizeUserExperience(2); //click the play button when it load fininshed, avoid stopping in ad video. 
    //                                         // optimizeUserExperience(3); //reset the backdrop element
    //                                     }
    //                                 }, 50);
    //                             }
    //                         });
    //                         okBtnObserver.observe(iframeBody, observerInit);
    //                     }
    //                     //listen for the iframe element load event
    //                     iframe.addEventListener('load', iframeLoaded, true);
    //                 }
    //             });
    //             iframeObserver.observe(document, observerInit);
    //         };
    //     }
    // }
    // //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址