Greasy Fork

Greasy Fork is available in English.

Youtube AD Skipper 油管广告拦截跳过

Quickly skip video ads in Youtube. 快速跳过油管中的视频广告。

当前为 2023-10-30 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name                  Youtube AD Skipper 油管广告拦截跳过
// @name:zh-CN            Youtube AD Skipper 油管广告拦截跳过
// @namespace             http://tampermonkey.net/
// @version               0.2
// @description           Quickly skip video ads in Youtube. 快速跳过油管中的视频广告。
// @description:zh-CN     Quickly skip video ads in Youtube. 快速跳过油管中的视频广告。
// @icon                  https://www.gstatic.com/youtube/img/branding/favicon/favicon_144x144.png
// @author                gabe
// @license               MIT
// @match                 https://*.youtube.com/*
// @grant                 none
// ==/UserScript==

(function () {
  "use strict";

  function sleep(delay = 100) {
    return new Promise(function (resolve) {
      const timer = setTimeout(function () {
        clearTimeout(timer);
        resolve();
      }, delay);
    });
  }

  function newTouch(el) {
    const rect = el.getBoundingClientRect();
    const x = (rect.left + rect.right) / 2;
    const y = (rect.top + rect.bottom) / 2;
    return new Touch({
      identifier: Date.now(),
      target: el,
      clientX: x,
      clientY: y,
      screenX: x,
      screenY: y,
      pageX: x + document.body.scrollLeft,
      pageY: y + document.body.scrollTop,
      radiusX: 10.0,
      radiusY: 10.0,
      rotationAngle: 0.0,
      force: 1,
    });
  }

  function newTouchEvent(touch, name) {
    return new TouchEvent(name, {
      cancelable: true,
      bubbles: true,
      touches: [touch],
      targetTouches: [touch],
      changedTouches: [touch],
    });
  }

  function dispatchTouch(el) {
    const touch = newTouch(el);
    btn.dispatchEvent(newTouchEvent(touch, "touchstart"));
    btn.dispatchEvent(newTouchEvent(touch, "touchmove"));
    btn.dispatchEvent(newTouchEvent(touch, "touchend"));
  }

  async function skipAd(moviePlayer, adOverlay) {
    let i = 0;
    while (true) {
      i++;
      if (i > 1) {
        adOverlay = moviePlayer.getElementsByClassName(
          "ytp-ad-player-overlay"
        )[0];
        if (!adOverlay) {
          return;
        }
      }

      const skipButton =
        adOverlay.getElementsByClassName("ytp-ad-skip-button")[0];

      if (skipButton) {
        const isMobile = location.hostname === "m.youtube.com";
        if (isMobile) {
          dispatchTouch(skipButton);
          console.info("[Youtube AD Skipper] skip touch ->", i);
        } else {
          skipButton.click();
          console.info("[Youtube AD Skipper] skip click ->", i);
        }
        await sleep();
        continue;
      }

      const video = moviePlayer.getElementsByTagName("video")[0];
      video.currentTime = video.duration;
      console.info("[Youtube AD Skipper] skip play ->", i);
      await sleep();
    }
  }

  let isBusying = false;
  const pageObserver = new MutationObserver(async function () {
    if (isBusying) {
      return;
    }

    const moviePlayer = document.getElementById("movie_player");
    if (!moviePlayer) {
      return;
    }

    const adOverlay = moviePlayer.getElementsByClassName(
      "ytp-ad-player-overlay"
    )[0];
    if (!adOverlay) {
      return;
    }

    isBusying = true;
    const adInfo = adOverlay.getElementsByClassName(
      "ytp-ad-player-overlay-instream-info"
    )[0];
    console.info("[Youtube AD Skipper] found ad: ", adInfo && adInfo.innerText);
    await skipAd(moviePlayer, adOverlay);
    isBusying = false;
  });

  pageObserver.observe(document.body, {
    childList: true,
    subtree: true,
  });
})();