NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name YouBlock - youtube adblocker
// @version 0.1.3
// @description yt adblocker bypassing detections
// @match https://www.youtube.com/*
// @match https://www.youtube-nocookie.com/*
// @run-at document-start
// @sandbox raw
// @grant none
// @license MIT
// @namespace https://greasyfork.org/users/1461063
// ==/UserScript==
(() => {
"use strict";
const Fetch = window.fetch;
const JSONParse = JSON.parse;
const JSONStringify = JSON.stringify;
const ResponseJSON = Response.prototype.json;
const XMLHttpRequestOpen = XMLHttpRequest.prototype.open;
const XMLHttpRequestSend = XMLHttpRequest.prototype.send;
const MapHas = Map.prototype.has;
const PromiseThen = Promise.prototype.then;
const FunctionToString = Function.prototype.toString;
const XMLHttpRequestUrls = new WeakMap();
const CallbackCache = new WeakMap();
const PendingNodes = new Set();
const AdSelectors = [
"ytd-ad-slot-renderer",
"ytd-display-ad-renderer",
"ytd-promoted-video-renderer",
"ytd-promoted-sparkles-web-renderer",
"ytd-compact-promoted-video-renderer",
"ytd-in-feed-ad-layout-renderer",
"ytd-banner-promo-renderer",
"ytd-action-companion-ad-renderer",
"ytd-carousel-ad-renderer",
"ytd-player-legacy-desktop-watch-ads-renderer",
".ytd-in-feed-ad-layout-renderer",
".ytd-display-ad-renderer"
].join(",");
const FeedWrappers = [
"ytd-rich-item-renderer",
"ytd-video-renderer",
"ytd-grid-video-renderer",
"ytd-compact-video-renderer",
"ytd-reel-item-renderer"
].join(",");
const WarningSelectors = [
"tp-yt-paper-dialog",
"tp-yt-paper-toast",
"ytd-enforcement-message-view-model",
"ytd-modal-with-title-and-button-renderer",
"yt-notification-action-renderer",
"ytd-notification-action-renderer",
"[role=\"alert\"]"
].join(",");
const RetryTypes = ["channel", "lactmilli"];
const ContractUserAgent = /(?:^|;\s*)(?:adunit|channel|lactmilli|instream|eafg)(?:;|$)/;
let RetryQueue = RetryTypes.slice();
let SnackbarDetected = false;
let SnackbarTime = 0;
let NormalUserAgent = null;
let PlayingVideoID = null;
let ReloadTime = 0;
let Frame = 0;
function HasAdFields(Data, Seen = new WeakSet()) {
if (!Data || typeof Data !== "object" || Seen.has(Data)) return false;
Seen.add(Data);
for (const Name of Object.keys(Data)) {
if (Name === "adPlacements" || Name === "adSlots") return true;
if (HasAdFields(Data[Name], Seen)) return true;
}
return false;
}
function RewriteData(Data, Seen) {
if (!Data || typeof Data !== "object") return Data;
if (Seen.has(Data)) return Seen.get(Data);
const Output = Array.isArray(Data) ? [] : {};
Seen.set(Data, Output);
for (const Name of Object.keys(Data)) {
const NewName = Name === "adPlacements" || Name === "adSlots" ? "no_ads" : Name;
Output[NewName] = RewriteData(Data[Name], Seen);
}
return Output;
}
function FixInitialResponse(Response) {
if (!Response || typeof Response !== "object") return Response;
if (!HasAdFields(Response)) return Response;
return RewriteData(Response, new WeakMap());
}
function HookInitialResponse() {
const Name = "ytInitialPlayerResponse";
const Descriptor = Object.getOwnPropertyDescriptor(window, Name);
if (Descriptor && !Descriptor.configurable) return;
let SavedResponse;
try {
SavedResponse = FixInitialResponse(window[Name]);
} catch {}
try {
Object.defineProperty(window, Name, {
configurable: true,
enumerable: Descriptor?.enumerable ?? true,
get() {
return SavedResponse;
},
set(Response) {
SavedResponse = FixInitialResponse(Response);
}
});
} catch {}
}
HookInitialResponse();
function GetURL(Input) {
let Text;
if (Input instanceof Request) {
Text = Input.url;
} else if (Input instanceof URL) {
Text = Input.href;
} else {
Text = String(Input);
}
if (!Text.includes("/youtubei/v1/player") && !Text.includes("/youtubei/v1/get_watch")) return null;
try {
return new URL(Text, location.href);
} catch {
return null;
}
}
function IsAdRequest(URL) {
if (!URL) return false;
return URL.pathname === "/youtubei/v1/player" || URL.pathname === "/youtubei/v1/get_watch";
}
function IsPlayerRequest(URL) {
return typeof URL === "string" && URL.includes("/youtubei/v1/player");
}
function IsAdXMLRequest(URL) {
return typeof URL === "string" && (URL.includes("/youtubei/v1/player") || URL.includes("/youtubei/v1/get_watch"));
}
function FixAdResponse(Text) {
if (typeof Text !== "string") return Text;
return Text
.replace(/"adPlacements"/g, "\"no_ads\"")
.replace(/"adSlots"/g, "\"no_ads\"");
}
function RebuildResponse(Response, Text) {
const Headers = new window.Headers(Response.headers);
Headers.delete("content-length");
const Output = new window.Response(Text, {
status: Response.status,
statusText: Response.statusText,
headers: Headers
});
try {
Object.defineProperties(Output, {
url: {
value: Response.url,
configurable: true
},
redirected: {
value: Response.redirected,
configurable: true
},
type: {
value: Response.type,
configurable: true
}
});
} catch {}
return Output;
}
window.fetch = new Proxy(Fetch, {
apply: async (Target, ThisValue, Arguments) => {
const URL = GetURL(Arguments[0]);
if (!IsAdRequest(URL)) return Reflect.apply(Target, ThisValue, Arguments);
const Response = await Reflect.apply(Target, ThisValue, Arguments);
if (!Response.ok) return Response;
let Text;
try {
Text = await Response.clone().text();
} catch {
return Response;
}
const Output = FixAdResponse(Text);
if (Output === Text) return Response;
return RebuildResponse(Response, Output);
}
});
function IsShortsAd(Item) {
return Item?.command?.reelWatchEndpoint?.adClientParams?.isAd === true;
}
function RemoveShortsAds(Data) {
if (!Data || typeof Data !== "object") return Data;
const Lists = [
Data.entries,
Data.reelWatchSequenceResponse?.entries
];
for (const Items of Lists) {
if (!Array.isArray(Items)) continue;
for (let Index = Items.length - 1; Index >= 0; Index--) {
if (IsShortsAd(Items[Index])) Items.splice(Index, 1);
}
}
return Data;
}
JSON.parse = function(Text, Reviver) {
const Data = JSONParse.call(this, Text, Reviver);
if (typeof Text === "string" && Text.includes("reelWatchEndpoint") && Text.includes("isAd")) RemoveShortsAds(Data);
return Data;
};
Response.prototype.json = async function() {
const Data = await ResponseJSON.call(this);
if (this.url.includes("/reel_watch_sequence")) RemoveShortsAds(Data);
return Data;
};
function AddReloadMarker(Data, Seen = new WeakSet()) {
if (!Data || typeof Data !== "object" || Seen.has(Data)) return;
Seen.add(Data);
for (const Name of Object.keys(Data)) {
if (Name === "referer" && typeof Data[Name] === "string") {
Data[Name] = Data[Name].replace(/(?:#reloadxhr)?$/, "#reloadxhr");
continue;
}
AddReloadMarker(Data[Name], Seen);
}
}
function FixPlayerRequestBody(Body) {
if (typeof Body !== "string") return Body;
let Request;
try {
Request = JSONParse(Body);
} catch {
return Body;
}
const Client = Request?.context?.client;
const UserAgent = Client?.userAgent || "";
if (Client?.clientName === "WEB" && UserAgent.includes("channel")) Client.clientScreen = "CHANNEL";
if (UserAgent.includes("lactmilli")) {
Request.params = "8AUB";
const PlaybackContext = Request?.playbackContext?.contentPlaybackContext;
if (PlaybackContext) PlaybackContext.lactMilliseconds = String(Date.now());
}
if (ContractUserAgent.test(UserAgent)) AddReloadMarker(Request);
try {
return JSONStringify(Request);
} catch {
return Body;
}
}
XMLHttpRequest.prototype.open = function(Method, URL, ...Arguments) {
const Text = String(URL);
if (IsAdXMLRequest(Text)) {
XMLHttpRequestUrls.set(this, Text);
} else {
XMLHttpRequestUrls.delete(this);
}
return XMLHttpRequestOpen.call(this, Method, URL, ...Arguments);
};
XMLHttpRequest.prototype.send = function(Body) {
const URL = XMLHttpRequestUrls.get(this);
if (IsPlayerRequest(URL)) Body = FixPlayerRequestBody(Body);
return XMLHttpRequestSend.call(this, Body);
};
function HookXMLHttpRequest(Name) {
const Descriptor = Object.getOwnPropertyDescriptor(XMLHttpRequest.prototype, Name);
if (!Descriptor?.get || !Descriptor.configurable) return;
Object.defineProperty(XMLHttpRequest.prototype, Name, {
configurable: Descriptor.configurable,
enumerable: Descriptor.enumerable,
get() {
const Output = Descriptor.get.call(this);
if (!XMLHttpRequestUrls.has(this) || typeof Output !== "string") return Output;
return FixAdResponse(Output);
}
});
}
try {
HookXMLHttpRequest("responseText");
HookXMLHttpRequest("response");
} catch {}
function RemoveUserAgentMarkers(UserAgent) {
if (typeof UserAgent !== "string") return UserAgent;
return UserAgent.replace(/;\s*(?:adunit|channel|lactmilli|instream|eafg)(?=[;)])/g, "").replace(/;\s*(?:adunit|channel|lactmilli|instream|eafg)$/g, "");
}
function GetYouTubeClient() {
return window.ytcfg?.data_?.INNERTUBE_CONTEXT?.client || null;
}
function SaveUserAgent() {
const Client = GetYouTubeClient();
if (!Client?.userAgent) return false;
if (NormalUserAgent === null) NormalUserAgent = RemoveUserAgentMarkers(Client.userAgent);
return true;
}
function UseRetryMarker(Marker) {
const Client = GetYouTubeClient();
if (!Client?.userAgent) return false;
SaveUserAgent();
if (!NormalUserAgent) return false;
if (!Marker) {
Client.userAgent = NormalUserAgent;
return true;
}
Client.userAgent = NormalUserAgent.replace(/(Mozilla\/5\.0 \([^)]+)/, "$1; " + Marker);
return true;
}
function GetVideoID() {
if (location.pathname !== "/watch") return null;
return new URLSearchParams(location.search).get("v");
}
function ResetRetry(VideoID = GetVideoID()) {
RetryQueue = RetryTypes.slice();
SnackbarDetected = false;
SnackbarTime = 0;
PlayingVideoID = VideoID;
ReloadTime = 0;
UseRetryMarker("");
}
function GetPlayerInfo() {
const Player = document.getElementById("movie_player");
if (!Player) return null;
try {
return {
Player,
Response: Player.getPlayerResponse?.(),
Progress: Player.getProgressState?.(),
Stats: Player.getStatsForNerds?.(),
State: Player.getPlayerStateObject?.()
};
} catch {
return null;
}
}
function IsDeadBuffer(PlayerInfo) {
if (!PlayerInfo) return false;
return PlayerInfo.State?.isBuffering === true && PlayerInfo.Stats?.buffer_health_seconds === "0.00 s" && PlayerInfo.Stats?.resolution === "0x0";
}
function SnackbarHit() {
SnackbarTime = performance.now();
if (SnackbarDetected) return;
const PlayerInfo = GetPlayerInfo();
if (!IsDeadBuffer(PlayerInfo) || RetryQueue.length === 0) return;
const PlaybackStatsURL = PlayerInfo.Response?.playbackTracking?.videostatsPlaybackUrl?.baseUrl || "";
if (PlaybackStatsURL.includes("reloadxhr")) RetryQueue.shift();
SnackbarDetected = true;
}
Map.prototype.has = new Proxy(MapHas, {
apply(Target, ThisValue, Arguments) {
if (Arguments?.[0] === "onSnackbarMessage") SnackbarHit();
return Reflect.apply(Target, ThisValue, Arguments);
}
});
function IsBlockedCallback(Callback) {
if (CallbackCache.has(Callback)) return CallbackCache.get(Callback);
let Blocked = false;
try {
Blocked = FunctionToString.call(Callback).includes("onAbnormalityDetected");
} catch {}
CallbackCache.set(Callback, Blocked);
return Blocked;
}
Promise.prototype.then = new Proxy(PromiseThen, {
apply(Target, ThisValue, Arguments) {
for (let Index = 0; Index < Arguments.length; Index++) {
const Callback = Arguments[Index];
if (typeof Callback !== "function") continue;
if (!IsBlockedCallback(Callback)) continue;
Arguments[Index] = function() {};
}
return Reflect.apply(Target, ThisValue, Arguments);
}
});
function ReloadVideo(Player, VideoID, StartTime) {
const CurrentTime = performance.now();
if (CurrentTime - ReloadTime < 250) return;
ReloadTime = CurrentTime;
try {
Player.loadVideoById(VideoID, StartTime);
} catch {}
}
function CheckContract() {
if (location.pathname !== "/watch") return;
const PlayerInfo = GetPlayerInfo();
if (!PlayerInfo) return;
const Player = PlayerInfo.Player;
const PlayerResponse = PlayerInfo.Response;
const Progress = PlayerInfo.Progress;
const Stats = PlayerInfo.Stats;
const VideoID = PlayerResponse?.videoDetails?.videoId || GetVideoID();
if (VideoID && VideoID !== PlayingVideoID) ResetRetry(VideoID);
if (!PlayerResponse || !Progress) return;
const HasVideo = Progress.duration > 0 && (Progress.loaded < Progress.duration || Progress.duration - Progress.current > 1) || PlayerResponse.videoDetails?.isLive === true;
if (!HasVideo) return;
if (Stats?.debug_info?.startsWith?.("SSAP, AD")) return;
const StartTime = PlayerResponse?.playerConfig?.playbackStartConfig?.startSeconds ?? 0;
if (PlayerResponse?.playabilityStatus?.status === "UNPLAYABLE") {
const ErrorScreen = PlayerResponse?.playabilityStatus?.errorScreen;
let ErrorText = "";
try {
ErrorText = JSONStringify(ErrorScreen?.playerErrorMessageRenderer?.subreason?.runs || ErrorScreen?.playerInterstitialRenderer?.content?.interstitialViewModel?.description?.commandRuns || []);
} catch {}
const ContractError = !ErrorScreen?.playerErrorMessageRenderer?.playerCaptchaViewModel && ErrorText.includes("WEB_PAGE_TYPE_UNKNOWN") && ErrorText.includes("https://support.google.com/youtube/answer/3037019");
if (ContractError) {
RetryQueue.shift();
UseRetryMarker(RetryQueue[0] || "");
SnackbarDetected = false;
if (VideoID) ReloadVideo(Player, VideoID, StartTime);
return;
}
}
if (!RetryQueue.length) {
SnackbarDetected = false;
UseRetryMarker("");
return;
}
if (!SnackbarDetected || !IsDeadBuffer(PlayerInfo)) return;
UseRetryMarker(RetryQueue[0]);
SnackbarDetected = false;
if (VideoID) ReloadVideo(Player, VideoID, StartTime);
}
function RemoveAd(Element) {
if (!(Element instanceof window.Element)) return;
const Parent = Element.closest(FeedWrappers);
if (Parent?.querySelector(AdSelectors)) {
Parent.remove();
return;
}
Element.remove();
}
function RemovePageAds(Root = document) {
if (Root instanceof window.Element) {
if (Root.matches(AdSelectors)) {
RemoveAd(Root);
return;
}
if (Root.matches(FeedWrappers) && Root.querySelector(AdSelectors)) {
Root.remove();
return;
}
}
Root.querySelectorAll?.(AdSelectors).forEach(RemoveAd);
Root.querySelectorAll?.(FeedWrappers).forEach(Item => {
if (Item.querySelector(AdSelectors)) Item.remove();
});
}
function RemoveWarnings(Root = document) {
const Warnings = [];
if (Root instanceof window.Element && Root.matches(WarningSelectors)) Warnings.push(Root);
Root.querySelectorAll?.(WarningSelectors).forEach(Element => Warnings.push(Element));
let RemovedWarning = false;
for (const Element of Warnings) {
const Text = (Element.innerText || Element.textContent || "").toLowerCase();
const RecentSnackbar = performance.now() - SnackbarTime < 1500 && Element.matches("tp-yt-paper-toast, [role=\"alert\"]");
if (RecentSnackbar || Text.includes("ad blocker") || Text.includes("adblock") || Text.includes("video player will be blocked") || Text.includes("allow youtube ads") || Text.includes("werbeblocker")) {
Element.remove();
RemovedWarning = true;
}
}
if (!RemovedWarning) return;
document.querySelectorAll("tp-yt-iron-overlay-backdrop").forEach(Element => Element.remove());
if (document.body) document.body.style.overflow = "";
document.documentElement.style.overflow = "";
}
function AddStyle() {
if (!document.documentElement || document.getElementById("yt-adblock-style")) return;
const Style = document.createElement("style");
Style.id = "yt-adblock-style";
Style.textContent = [
"ytd-ad-slot-renderer",
"ytd-display-ad-renderer",
"ytd-promoted-video-renderer",
"ytd-promoted-sparkles-web-renderer",
"ytd-compact-promoted-video-renderer",
"ytd-in-feed-ad-layout-renderer",
"ytd-banner-promo-renderer",
"ytd-action-companion-ad-renderer",
"ytd-carousel-ad-renderer",
"ytd-player-legacy-desktop-watch-ads-renderer",
"ytd-rich-item-renderer:has(ytd-ad-slot-renderer)",
"ytd-rich-item-renderer:has(ytd-display-ad-renderer)",
"ytd-rich-item-renderer:has(ytd-promoted-video-renderer)",
"ytd-rich-item-renderer:has(ytd-promoted-sparkles-web-renderer)",
"ytd-rich-item-renderer:has(ytd-in-feed-ad-layout-renderer)",
"ytd-video-renderer:has(ytd-ad-slot-renderer)",
"ytd-video-renderer:has(ytd-promoted-video-renderer)",
"ytd-grid-video-renderer:has(ytd-ad-slot-renderer)",
"ytd-compact-video-renderer:has(ytd-ad-slot-renderer)",
"ytd-reel-item-renderer:has(ytd-ad-slot-renderer)"
].join(",") + "{display:none!important;}";
document.documentElement.appendChild(Style);
}
function FlushDOMWork() {
Frame = 0;
const Nodes = [...PendingNodes];
PendingNodes.clear();
for (const Node of Nodes) {
if (!(Node instanceof window.Element)) continue;
RemovePageAds(Node);
RemoveWarnings(Node);
}
CheckContract();
}
function ScheduleDOMWork(Node) {
if (Node instanceof window.Element) PendingNodes.add(Node);
if (Frame) return;
Frame = requestAnimationFrame(FlushDOMWork);
}
function Run(Root = document) {
AddStyle();
SaveUserAgent();
RemovePageAds(Root);
RemoveWarnings(Root);
CheckContract();
}
const WatchTimer = setInterval(() => {
SaveUserAgent();
CheckContract();
}, 100);
const StartupTimer = setInterval(() => {
if (!document.documentElement) return;
clearInterval(StartupTimer);
AddStyle();
const Observer = new MutationObserver(Changes => {
for (const Change of Changes) {
for (const Node of Change.addedNodes) ScheduleDOMWork(Node);
}
});
Observer.observe(document.documentElement, {
childList: true,
subtree: true
});
Run();
}, 1);
document.addEventListener("yt-navigate-finish", () => {
ResetRetry(GetVideoID());
Run();
}, true);
document.addEventListener("yt-page-data-updated", () => Run(), true);
window.addEventListener("beforeunload", () => {
clearInterval(WatchTimer);
if (Frame) cancelAnimationFrame(Frame);
}, {once: true});
})();