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