您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Mutes, blurs and skips ads on YouTube. Speeds up ad playback. Clicks "yes" on "are you there?" on YouTube Music.
当前为
- // ==UserScript==
- // @name YouTube Mute and Skip Ads
- // @namespace https://github.com/ion1/userscripts
- // @version 0.0.26
- // @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==
- (e=>{if(typeof GM_addStyle=="function"){GM_addStyle(e);return}const n=document.createElement("style");n.textContent=e,document.head.append(n)})(` /* 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,
- .ytp-suggested-action-badge,
- 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,.ytp-suggested-action-badge,ytmusic-app:has(#movie_player.ad-showing) ytmusic-player-bar :is(.title,.subtitle)):is(:hover,:focus-within) {
- filter: none;
- }
- #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.klass("ytp-featured-product").klass("ytp-suggested-action-badge-dismiss-button-icon").visible().onCreated(click("suggested action close"));
- watcher.tag("ytmusic-you-there-renderer").tag("button").visible().onCreated(click("are-you-there"));
- })();