動畫瘋自動播放廣告

自動同意年齡提示,到達時間後自動跳過廣告,並註冊一些快捷鍵(詳見最下方的更新日誌)

目前為 2024-12-15 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name               Auto play ads on ani.gamer.com.tw
// @name:zh-CN         动画疯自动播放广告
// @name:zh-TW         動畫瘋自動播放廣告
// @namespace          ling921
// @version            0.2
// @description        Agree to age prompt, auto skip ads when time is up, and register some keyboard shortcuts (see the release notes below for details)
// @description:zh-CN  自动同意年龄提示,到达时间后自动跳过广告,并注册一些快捷键(详见最下方的更新日志)
// @description:zh-TW  自動同意年齡提示,到達時間後自動跳過廣告,並註冊一些快捷鍵(詳見最下方的更新日誌)
// @author             ling921
// @match              https://ani.gamer.com.tw/animeVideo.php*
// @match              https://*.safeframe.googlesyndication.com/*
// @match              https://imasdk.googleapis.com/*
// @icon               http://gamer.com.tw/favicon.ico
// @grant              none
// @run-at             document-idle
// @tag                video
// @tag                anime
// @tag                utilities
// @license            MIT
// ==/UserScript==

/**
 * Global variable to store video player
 * @type {HTMLVideoElement}
 */
var videoPlayer;

(function () {
  "use strict";

  // Handle top level window
  if (window === window.top) {
    // Register keyboard shortcuts
    document.addEventListener("keydown", (event) => {
      // Ignore input fields event propagation
      if (
        event.target.tagName === "INPUT" ||
        event.target.tagName === "TEXTAREA" ||
        event.target.isContentEditable
      ) {
        return;
      }

      if (!event.ctrlKey && !event.metaKey && !event.shiftKey) {
        // [ goes to previous video
        if (event.key === "[") {
          const prevButton = document.querySelector(".vjs-pre-button");
          if (prevButton) {
            console.log("Go to previous video");
            prevButton.click();
          } else {
            console.log("No previous video button found");
          }
        }
        // ] goes to next video
        else if (event.key === "]") {
          const nextButton = document.querySelector(".vjs-next-button");
          if (nextButton) {
            console.log("Go to next video");
            nextButton.click();
          } else {
            console.log("No next video button found");
          }
        }
        // P pause or play
        else if (event.key === "p") {
          const playButton = document.querySelector(".vjs-play-control");
          if (playButton) {
            console.log("Pause or play");
            playButton.click();
          }
        }
        // T enter or exit theater mode
        else if (event.key === "t") {
          const theaterButton = document.querySelector(".vjs-indent-button");
          if (theaterButton) {
            console.log("Enter or exit theater mode");
            theaterButton.click();
          }
        }
        // F enter or exit fullscreen
        else if (event.key === "f") {
          const fullscreenButton = document.querySelector(
            ".vjs-fullscreen-control"
          );
          if (fullscreenButton) {
            console.log("Enter or exit fullscreen");
            fullscreenButton.click();
          }
        }
      }
    });

    videoPlayer = document.querySelector("#ani_video_html5_api");
    if (videoPlayer) {
      console.log("Add event listener to videoPlayer");
      videoPlayer.addEventListener("loadstart", () => {
        videoPlayer.muted = false;
      });
    }

    // Define observer to execute functions when DOM changes
    const observer = new MutationObserver(() => {
      agreeAgePrompt();
      handleVideoPlayerAds();
    });

    // Start observing the body for changes
    observer.observe(document.body, { childList: true, subtree: true });
  }
  // Handle iframe window
  else {
    if (window.location.href.includes("safeframe.googlesyndication.com")) {
      const observer = new MutationObserver(() => {
        handleIframeAds(document);
      });
      observer.observe(document.body, { childList: true, subtree: true });
    } else if (window.location.href.includes("imasdk.googleapis.com")) {
      const observer = new MutationObserver(() => {
        handleIframeAds2(document);
      });
      observer.observe(document.body, { childList: true, subtree: true });
    }
  }
})();

/**
 * Agree to age prompt
 */
function agreeAgePrompt() {
  const agePrompt = document.querySelector("button.choose-btn-agree#adult");
  if (agePrompt) {
    console.log("Agree to age prompt");
    agePrompt.click();
  }
}

/**
 * Handle ads in video player
 */
function handleVideoPlayerAds() {
  const skipButton = document.querySelector("#adSkipButton");
  if (skipButton) {
    if (skipButton.classList.contains("enable")) {
      console.log("Ads time is up, skip ads (#adSkipButton)");
      skipButton.click();
    } else {
      videoPlayer.muted = true;
    }
  }

  const skipButton2 = document.querySelector(".nativeAD-skip-button.enable");
  if (skipButton2 && !skipButton2.classList.contains("vjs-hidden")) {
    console.log("Ads time is up, skip ads (.nativeAD-skip-button)");
    skipButton2.click();
  }
}

/**
 * Handle ads in iframe
 * @param {Document} doc - The iframe document
 */
function handleIframeAds(doc) {
  const adsCountDown = doc.querySelector("#count-down-text");
  if (adsCountDown) {
    const dismissDialog = () => {
      const dismissButton = doc.querySelector("#card #dismiss-button-element");
      if (dismissButton) {
        if (dismissButton.style.display !== "none") {
          console.log("Dismiss dialog Ad");
          dismissButton.click();
        } else {
          console.log("Dismiss button is hidden, waiting for it to appear...");
        }
      } else {
        console.log("Dismiss button is not found");
      }
    };
    if (adsCountDown.offsetParent === null) {
      dismissDialog();
    } else if (adsCountDown.textContent === "1 秒後即可獲得獎勵") {
      setTimeout(dismissDialog, 1000);
    }
  }
}

/**
 * Handle ads in iframe
 * @param {Document} doc - The iframe document
 */
function handleIframeAds2(doc) {
  const skipButton = doc.querySelector('[aria-label="Skip Ad"]');
  if (skipButton) {
    if (skipButton.textContent === "Skip Ad") {
      console.log('Click Skip Ad ([aria-label="Skip Ad"])');
      skipButton.click();
    } else {
      videoPlayer.muted = true;
    }
  }
}

// Release notes

// 2024-12-15 version 0.2
// - 添加標籤 video, anime, utilities

// 2024-12-14 version 0.1
// - 自動同意年齡確認
// - 廣告倒數結束結束自動跳過廣告
// - 播放廣告時靜音,播放影片時取消靜音
// - 註冊快捷鍵 [ 和 ] 分別跳到上一個和下一個視頻
// - 註冊快速鍵 P 暫停或播放
// - 註冊快速鍵 T 進入或退出劇院模式
// - 註冊快捷鍵 F 進入或退出全螢幕