// ==UserScript==
// @name YouTube Mute and Skip Ads
// @namespace https://github.com/ion1/userscripts
// @version 0.0.27
// @author ion
// @description Mutes, blurs and skips ads on YouTube. Speeds up ad playback. Clicks "yes" on "are you there?" on YouTube Music.
// @license MIT
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @homepage https://github.com/ion1/userscripts/tree/master/packages/youtube-mute-skip-ads
// @homepageURL https://github.com/ion1/userscripts/tree/master/packages/youtube-mute-skip-ads
// @match *://www.youtube.com/*
// @match *://music.youtube.com/*
// @grant GM_addStyle
// @run-at document-body
// ==/UserScript==
(n=>{if(typeof GM_addStyle=="function"){GM_addStyle(n);return}const e=document.createElement("style");e.textContent=n,document.head.append(e)})(` /* Keep these in sync with the watchers. */
#movie_player
:is(.ytp-ad-skip-button, .ytp-ad-skip-button-modern, .ytp-skip-ad-button) {
anchor-name: --youtube-mute-skip-ads-unclickable-button;
}
body:has(
#movie_player
:is(
.ytp-ad-skip-button,
.ytp-ad-skip-button-modern,
.ytp-skip-ad-button
):not([style*="display: none"], [aria-hidden="true"])
)::after {
content: "\u{1D606}\u{1D5FC}\u{1D602}\u{1D601}\u{1D602}\u{1D5EF}\u{1D5F2}-\u{1D5FA}\u{1D602}\u{1D601}\u{1D5F2}-\u{1D600}\u{1D5F8}\u{1D5F6}\u{1D5FD}-\u{1D5EE}\u{1D5F1}\u{1D600}\\A\\A"
"Unfortunately, YouTube has started to block automated clicks based on isTrusted being false.\\A\\A"
"Please click on the skip button manually.";
white-space: pre-line;
pointer-events: none;
z-index: 9999;
position: fixed;
position-anchor: --youtube-mute-skip-ads-unclickable-button;
padding: 1.5em;
border-radius: 1.5em;
margin-bottom: 1em;
bottom: anchor(--youtube-mute-skip-ads-unclickable-button top);
right: anchor(--youtube-mute-skip-ads-unclickable-button right);
max-width: 25em;
font-size: 1.4rem;
line-height: 2rem;
font-weight: 400;
color: rgb(240 240 240);
background-color: rgb(0 0 0 / 0.7);
backdrop-filter: blur(10px);
animation: fade-in 3s linear;
}
@keyframes fade-in {
0% {
opacity: 0;
}
67% {
opacity: 0;
}
100% {
opacity: 1;
}
}
#movie_player.ad-showing video {
filter: blur(100px) opacity(0.25) grayscale(0.5);
}
#movie_player.ad-showing .ytp-title,
#movie_player.ad-showing .ytp-title-channel,
.ytp-visit-advertiser-link,
.ytp-ad-visit-advertiser-button,
ytmusic-app:has(#movie_player.ad-showing)
ytmusic-player-bar
:is(.title, .subtitle) {
filter: blur(4px) opacity(0.5) grayscale(0.5);
transition: 0.05s filter linear;
}
:is(#movie_player.ad-showing .ytp-title,#movie_player.ad-showing .ytp-title-channel,.ytp-visit-advertiser-link,.ytp-ad-visit-advertiser-button,ytmusic-app:has(#movie_player.ad-showing) ytmusic-player-bar :is(.title,.subtitle)):is(:hover,:focus-within) {
filter: none;
}
/* These popups are showing up on top of the video with a hidden dismiss button
* since 2024-09-25.
*/
.ytp-suggested-action-badge {
visibility: hidden !important;
}
#movie_player.ad-showing .caption-window,
.ytp-ad-player-overlay-flyout-cta,
.ytp-ad-player-overlay-layout__player-card-container, /* Seen since 2024-04-06. */
.ytp-ad-action-interstitial-slot, /* Added on 2024-08-25. */
ytd-action-companion-ad-renderer,
ytd-display-ad-renderer,
ytd-ad-slot-renderer,
ytd-promoted-sparkles-web-renderer,
ytd-player-legacy-desktop-watch-ads-renderer,
ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"],
ytd-merch-shelf-renderer {
filter: blur(10px) opacity(0.25) grayscale(0.5);
transition: 0.05s filter linear;
}
:is(#movie_player.ad-showing .caption-window,.ytp-ad-player-overlay-flyout-cta,.ytp-ad-player-overlay-layout__player-card-container,.ytp-ad-action-interstitial-slot,ytd-action-companion-ad-renderer,ytd-display-ad-renderer,ytd-ad-slot-renderer,ytd-promoted-sparkles-web-renderer,ytd-player-legacy-desktop-watch-ads-renderer,ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"],ytd-merch-shelf-renderer):is(:hover,:focus-within) {
filter: none;
}
.ytp-ad-action-interstitial-background-container /* Added on 2024-08-25. */ {
/* An image ad in place of the video. */
filter: blur(10px) opacity(0.25) grayscale(0.5);
} `);
(function () {
'use strict';
var __defProp = Object.defineProperty;
var __typeError = (msg) => {
throw TypeError(msg);
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
var _onCreatedCallbacks, _onRemovedCallbacks, _nodeObserver, _nodeWatchers, _textObserver, _onTextChangedCallbacks, _onAttrChangedCallbacks, _visibilityObserver, _isVisible, _visibilityWatchers, _Watcher_instances, assertElement_fn, assertVisibilityAncestor_fn, connect_fn, disconnect_fn, registerNodeObserver_fn, registerTextObserver_fn, registerAttrObservers_fn, registerVisibilityObserver_fn, deregisterNodeObserver_fn, deregisterTextObserver_fn, deregisterAttrObservers_fn, deregisterVisibilityObserver_fn;
const logPrefix = "youtube-mute-skip-ads:";
const _Watcher = class _Watcher {
constructor(name, elem) {
__privateAdd(this, _Watcher_instances);
__publicField(this, "name");
__publicField(this, "element");
__privateAdd(this, _onCreatedCallbacks);
__privateAdd(this, _onRemovedCallbacks);
__privateAdd(this, _nodeObserver);
__privateAdd(this, _nodeWatchers);
__privateAdd(this, _textObserver);
__privateAdd(this, _onTextChangedCallbacks);
__privateAdd(this, _onAttrChangedCallbacks);
__publicField(this, "visibilityAncestor");
__privateAdd(this, _visibilityObserver);
__privateAdd(this, _isVisible);
__privateAdd(this, _visibilityWatchers);
this.name = name;
this.element = null;
__privateSet(this, _onCreatedCallbacks, []);
__privateSet(this, _onRemovedCallbacks, []);
__privateSet(this, _nodeObserver, null);
__privateSet(this, _nodeWatchers, []);
__privateSet(this, _textObserver, null);
__privateSet(this, _onTextChangedCallbacks, []);
__privateSet(this, _onAttrChangedCallbacks, []);
this.visibilityAncestor = null;
__privateSet(this, _visibilityObserver, null);
__privateSet(this, _isVisible, null);
__privateSet(this, _visibilityWatchers, []);
if (elem != null) {
__privateMethod(this, _Watcher_instances, connect_fn).call(this, elem);
}
}
onCreated(onCreatedCb) {
__privateGet(this, _onCreatedCallbacks).push(onCreatedCb);
if (this.element != null) {
const onRemovedCb = onCreatedCb(this.element);
if (onRemovedCb) {
__privateGet(this, _onRemovedCallbacks).push(onRemovedCb);
}
}
return this;
}
descendant(selector, name) {
var _a;
const watcher2 = new _Watcher(`${this.name} → ${name}`);
__privateGet(this, _nodeWatchers).push({ selector, name, watcher: watcher2 });
if (this.element != null) {
for (const descElem of getDescendantsBy(this.element, selector, name)) {
__privateMethod(_a = watcher2, _Watcher_instances, connect_fn).call(_a, descElem, this.element);
}
__privateMethod(this, _Watcher_instances, registerNodeObserver_fn).call(this);
}
return watcher2;
}
id(idName) {
return this.descendant("id", idName);
}
klass(className) {
return this.descendant("class", className);
}
tag(tagName) {
return this.descendant("tag", tagName);
}
visible() {
var _a;
const watcher2 = new _Watcher(`${this.name} (visible)`);
__privateGet(this, _visibilityWatchers).push(watcher2);
if (this.element != null) {
const visibilityAncestor = __privateMethod(this, _Watcher_instances, assertVisibilityAncestor_fn).call(this);
if (__privateGet(this, _isVisible)) {
__privateMethod(_a = watcher2, _Watcher_instances, connect_fn).call(_a, this.element, visibilityAncestor);
}
__privateMethod(this, _Watcher_instances, registerVisibilityObserver_fn).call(this);
}
return watcher2;
}
/// `null` implies null textContent. `undefined` implies that the watcher is
/// being disconnected.
text(callback) {
__privateGet(this, _onTextChangedCallbacks).push(callback);
if (this.element != null) {
callback(this.element, this.element.textContent);
__privateMethod(this, _Watcher_instances, registerTextObserver_fn).call(this);
}
return this;
}
/// `null` implies no such attribute. `undefined` implies that the watcher is
/// being disconnected.
attr(name, callback) {
__privateGet(this, _onAttrChangedCallbacks).push({ name, callback, observer: null });
if (this.element != null) {
callback(this.element, this.element.getAttribute(name));
__privateMethod(this, _Watcher_instances, registerAttrObservers_fn).call(this);
}
return this;
}
};
_onCreatedCallbacks = new WeakMap();
_onRemovedCallbacks = new WeakMap();
_nodeObserver = new WeakMap();
_nodeWatchers = new WeakMap();
_textObserver = new WeakMap();
_onTextChangedCallbacks = new WeakMap();
_onAttrChangedCallbacks = new WeakMap();
_visibilityObserver = new WeakMap();
_isVisible = new WeakMap();
_visibilityWatchers = new WeakMap();
_Watcher_instances = new WeakSet();
assertElement_fn = function() {
if (this.element == null) {
throw new Error(`Watcher not connected to an element`);
}
return this.element;
};
assertVisibilityAncestor_fn = function() {
if (this.visibilityAncestor == null) {
throw new Error(`Watcher is missing a visibilityAncestor`);
}
return this.visibilityAncestor;
};
connect_fn = function(element, visibilityAncestor) {
var _a;
if (this.element != null) {
if (this.element !== element) {
console.error(
logPrefix,
`Watcher already connected to`,
this.element,
`while trying to connect to`,
element
);
}
return;
}
this.element = element;
this.visibilityAncestor = visibilityAncestor ?? null;
for (const onCreatedCb of __privateGet(this, _onCreatedCallbacks)) {
const onRemovedCb = onCreatedCb(this.element);
if (onRemovedCb) {
__privateGet(this, _onRemovedCallbacks).push(onRemovedCb);
}
}
for (const { selector, name, watcher: watcher2 } of __privateGet(this, _nodeWatchers)) {
for (const descElem of getDescendantsBy(this.element, selector, name)) {
__privateMethod(_a = watcher2, _Watcher_instances, connect_fn).call(_a, descElem, this.element);
}
}
for (const callback of __privateGet(this, _onTextChangedCallbacks)) {
callback(this.element, this.element.textContent);
}
for (const { name, callback } of __privateGet(this, _onAttrChangedCallbacks)) {
callback(this.element, this.element.getAttribute(name));
}
__privateMethod(this, _Watcher_instances, registerNodeObserver_fn).call(this);
__privateMethod(this, _Watcher_instances, registerTextObserver_fn).call(this);
__privateMethod(this, _Watcher_instances, registerAttrObservers_fn).call(this);
__privateMethod(this, _Watcher_instances, registerVisibilityObserver_fn).call(this);
};
disconnect_fn = function() {
var _a, _b;
if (this.element == null) {
return;
}
for (const child of __privateGet(this, _nodeWatchers)) {
__privateMethod(_a = child.watcher, _Watcher_instances, disconnect_fn).call(_a);
}
for (const callback of __privateGet(this, _onTextChangedCallbacks)) {
callback(this.element, void 0);
}
for (const { callback } of __privateGet(this, _onAttrChangedCallbacks)) {
callback(this.element, void 0);
}
for (const child of __privateGet(this, _visibilityWatchers)) {
__privateMethod(_b = child, _Watcher_instances, disconnect_fn).call(_b);
}
__privateMethod(this, _Watcher_instances, deregisterNodeObserver_fn).call(this);
__privateMethod(this, _Watcher_instances, deregisterTextObserver_fn).call(this);
__privateMethod(this, _Watcher_instances, deregisterAttrObservers_fn).call(this);
__privateMethod(this, _Watcher_instances, deregisterVisibilityObserver_fn).call(this);
while (__privateGet(this, _onRemovedCallbacks).length > 0) {
const onRemovedCb = __privateGet(this, _onRemovedCallbacks).shift();
onRemovedCb();
}
this.element = null;
};
registerNodeObserver_fn = function() {
if (__privateGet(this, _nodeObserver) != null) {
return;
}
if (__privateGet(this, _nodeWatchers).length === 0) {
return;
}
const elem = __privateMethod(this, _Watcher_instances, assertElement_fn).call(this);
__privateSet(this, _nodeObserver, new MutationObserver((mutations) => {
var _a, _b;
for (const mut of mutations) {
for (const node of mut.addedNodes) {
for (const { selector, name, watcher: watcher2 } of __privateGet(this, _nodeWatchers)) {
for (const descElem of getSelfOrDescendantsBy(
node,
selector,
name
)) {
__privateMethod(_a = watcher2, _Watcher_instances, connect_fn).call(_a, descElem, elem);
}
}
}
for (const node of mut.removedNodes) {
for (const { selector, name, watcher: watcher2 } of __privateGet(this, _nodeWatchers)) {
for (const _descElem of getSelfOrDescendantsBy(
node,
selector,
name
)) {
__privateMethod(_b = watcher2, _Watcher_instances, disconnect_fn).call(_b);
}
}
}
}
}));
__privateGet(this, _nodeObserver).observe(elem, {
subtree: true,
childList: true
});
};
registerTextObserver_fn = function() {
if (__privateGet(this, _textObserver) != null) {
return;
}
if (__privateGet(this, _onTextChangedCallbacks).length === 0) {
return;
}
const elem = __privateMethod(this, _Watcher_instances, assertElement_fn).call(this);
__privateSet(this, _textObserver, new MutationObserver((_mutations) => {
for (const callback of __privateGet(this, _onTextChangedCallbacks)) {
callback(elem, elem.textContent);
}
}));
__privateGet(this, _textObserver).observe(elem, {
subtree: true,
// This is needed when elements are replaced to update their text.
childList: true,
characterData: true
});
};
registerAttrObservers_fn = function() {
const elem = __privateMethod(this, _Watcher_instances, assertElement_fn).call(this);
for (const handler of __privateGet(this, _onAttrChangedCallbacks)) {
if (handler.observer != null) {
continue;
}
const { name, callback } = handler;
handler.observer = new MutationObserver((_mutations) => {
callback(elem, elem.getAttribute(name));
});
handler.observer.observe(elem, {
attributes: true,
attributeFilter: [name]
});
}
};
registerVisibilityObserver_fn = function() {
if (__privateGet(this, _visibilityObserver) != null) {
return;
}
if (__privateGet(this, _visibilityWatchers).length === 0) {
return;
}
__privateSet(this, _isVisible, false);
const elem = __privateMethod(this, _Watcher_instances, assertElement_fn).call(this);
const visibilityAncestor = __privateMethod(this, _Watcher_instances, assertVisibilityAncestor_fn).call(this);
__privateSet(this, _visibilityObserver, new IntersectionObserver(
(entries) => {
var _a, _b;
const oldVisible = __privateGet(this, _isVisible);
for (const entry of entries) {
__privateSet(this, _isVisible, entry.isIntersecting);
}
if (__privateGet(this, _isVisible) !== oldVisible) {
if (__privateGet(this, _isVisible)) {
for (const watcher2 of __privateGet(this, _visibilityWatchers)) {
__privateMethod(_a = watcher2, _Watcher_instances, connect_fn).call(_a, elem, visibilityAncestor);
}
} else {
for (const watcher2 of __privateGet(this, _visibilityWatchers)) {
__privateMethod(_b = watcher2, _Watcher_instances, disconnect_fn).call(_b);
}
}
}
},
{
root: visibilityAncestor
}
));
__privateGet(this, _visibilityObserver).observe(elem);
};
deregisterNodeObserver_fn = function() {
if (__privateGet(this, _nodeObserver) == null) {
return;
}
__privateGet(this, _nodeObserver).disconnect();
__privateSet(this, _nodeObserver, null);
};
deregisterTextObserver_fn = function() {
if (__privateGet(this, _textObserver) == null) {
return;
}
__privateGet(this, _textObserver).disconnect();
__privateSet(this, _textObserver, null);
};
deregisterAttrObservers_fn = function() {
for (const handler of __privateGet(this, _onAttrChangedCallbacks)) {
if (handler.observer == null) {
continue;
}
handler.observer.disconnect();
handler.observer = null;
}
};
deregisterVisibilityObserver_fn = function() {
if (__privateGet(this, _visibilityObserver) == null) {
return;
}
__privateGet(this, _visibilityObserver).disconnect();
__privateSet(this, _visibilityObserver, null);
__privateSet(this, _isVisible, null);
};
let Watcher = _Watcher;
function getSelfOrDescendantsBy(node, selector, name) {
if (!(node instanceof HTMLElement)) {
return [];
}
if (selector === "id" || selector === "class" || selector === "tag") {
if (selector === "id" && node.id === name || selector === "class" && node.classList.contains(name) || selector === "tag" && node.tagName.toLowerCase() === name.toLowerCase()) {
return [node];
} else {
return getDescendantsBy(node, selector, name);
}
} else {
const impossible = selector;
throw new Error(`Impossible selector type: ${JSON.stringify(impossible)}`);
}
}
function getDescendantsBy(node, selector, name) {
if (!(node instanceof HTMLElement)) {
return [];
}
let cssSelector = "";
if (selector === "id") {
cssSelector += "#";
} else if (selector === "class") {
cssSelector += ".";
} else if (selector === "tag") ;
else {
const impossible = selector;
throw new Error(`Impossible selector type: ${JSON.stringify(impossible)}`);
}
cssSelector += CSS.escape(name);
return Array.from(node.querySelectorAll(cssSelector));
}
const videoSelector = "#movie_player video";
function getVideoElement() {
const videoElem = document.querySelector(videoSelector);
if (!(videoElem instanceof HTMLVideoElement)) {
console.error(
logPrefix,
"Expected",
JSON.stringify(videoSelector),
"to be a video element, got:",
videoElem == null ? void 0 : videoElem.cloneNode(true)
);
return null;
}
return videoElem;
}
function callMoviePlayerMethod(name, onSuccess, args) {
var _a;
try {
const movieElem = document.getElementById("movie_player");
if (movieElem == null) {
console.warn(logPrefix, "movie_player element not found");
return;
}
const method = (_a = Object.getOwnPropertyDescriptor(
movieElem,
name
)) == null ? void 0 : _a.value;
if (method == null) {
console.warn(
logPrefix,
`movie_player element has no ${JSON.stringify(name)} property`
);
return;
}
if (!(typeof method === "function")) {
console.warn(
logPrefix,
`movie_player element property ${JSON.stringify(name)} is not a function`
);
return;
}
const result = method.apply(movieElem, args);
if (onSuccess != null) {
onSuccess(result);
}
return result;
} catch (e) {
console.warn(
logPrefix,
`movie_player method ${JSON.stringify(name)} failed:`,
e
);
return;
}
}
function disableVisibilityChecks() {
for (const eventName of ["visibilitychange", "blur", "focus"]) {
document.addEventListener(
eventName,
(ev) => {
ev.stopImmediatePropagation();
},
{ capture: true }
);
}
document.hasFocus = () => true;
Object.defineProperties(document, {
visibilityState: { value: "visible" },
hidden: { value: false }
});
}
function adIsPlaying(_elem) {
console.info(logPrefix, "An ad is playing, muting and speeding up");
const video = getVideoElement();
if (video == null) {
return;
}
const onRemovedCallbacks = [
mute(video),
speedup(video),
cancelPlayback(video)
];
return function onRemoved() {
for (const callback of onRemovedCallbacks) {
callback();
}
};
}
function mute(video) {
console.debug(logPrefix, "Muting");
video.muted = true;
return unmute;
}
function unmute() {
{
return;
}
}
function speedup(video) {
const originalRate = video.playbackRate;
for (let rate = 16; rate >= 2; rate /= 2) {
try {
video.playbackRate = rate;
break;
} catch (e) {
console.debug(logPrefix, `Setting playback rate to`, rate, `failed:`, e);
}
}
return function onRemoved() {
console.debug(logPrefix, `Restoring playback rate:`, originalRate);
try {
video.playbackRate = originalRate;
} catch (e) {
console.debug(
logPrefix,
`Restoring playback rate to`,
originalRate,
`failed:`,
e
);
}
};
}
function cancelPlayback(video) {
let shouldResume = false;
function doCancelPlayback() {
console.info(logPrefix, "Attempting to cancel playback");
callMoviePlayerMethod("cancelPlayback", () => {
shouldResume = true;
});
}
if (video.paused) {
console.debug(
logPrefix,
"Ad paused, waiting for it to play before canceling playback"
);
video.addEventListener("play", doCancelPlayback);
} else {
doCancelPlayback();
}
return function onRemoved() {
video.removeEventListener("play", doCancelPlayback);
if (shouldResume) {
resumePlaybackIfNotAtEnd();
}
};
}
function resumePlaybackIfNotAtEnd() {
const currentTime = callMoviePlayerMethod("getCurrentTime");
const duration = callMoviePlayerMethod("getDuration");
const isAtLiveHead = callMoviePlayerMethod("isAtLiveHead");
if (currentTime == null || duration == null || typeof currentTime !== "number" || typeof duration !== "number" || isNaN(currentTime) || isNaN(duration)) {
console.warn(
logPrefix,
`movie_player methods getCurrentTime/getDuration failed, got time: ${JSON.stringify(currentTime)}, duration: ${JSON.stringify(duration)}`
);
return;
}
if (isAtLiveHead == null || typeof isAtLiveHead !== "boolean") {
console.warn(
logPrefix,
`movie_player method isAtLiveHead failed, got: ${JSON.stringify(isAtLiveHead)}`
);
return;
}
const atEnd = duration - currentTime < 1;
if (atEnd && !isAtLiveHead) {
console.info(
logPrefix,
`Video is at the end (${currentTime}/${duration}), not attempting to resume playback`
);
return;
}
console.info(logPrefix, "Attempting to resume playback");
callMoviePlayerMethod("playVideo");
}
function click(description) {
return (elem) => {
if (elem.getAttribute("aria-hidden")) {
console.info(logPrefix, "Not clicking (aria-hidden):", description);
} else {
console.info(logPrefix, "Clicking:", description);
elem.click();
}
};
}
disableVisibilityChecks();
const watcher = new Watcher("body", document.body);
const adPlayerOverlayClasses = [
"ytp-ad-player-overlay",
"ytp-ad-player-overlay-layout"
// Seen since 2024-04-06.
];
for (const adPlayerOverlayClass of adPlayerOverlayClasses) {
watcher.klass(adPlayerOverlayClass).onCreated(adIsPlaying);
}
const adSkipButtonClasses = [
"ytp-ad-skip-button",
"ytp-ad-skip-button-modern",
// Seen since 2023-11-10.
"ytp-skip-ad-button"
// Seen since 2024-04-06.
];
for (const adSkipButtonClass of adSkipButtonClasses) {
watcher.id("movie_player").klass(adSkipButtonClass).visible().attr("aria-hidden", (elem, value) => {
if (value === null) {
click(`skip (${adSkipButtonClass})`)(elem);
}
});
}
watcher.klass("ytp-ad-overlay-close-button").visible().onCreated(click("overlay close"));
watcher.tag("ytmusic-you-there-renderer").tag("button").visible().onCreated(click("are-you-there"));
})();