動畫瘋自動播放廣告

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

目前為 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.4.0
// @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) {
    videoPlayer = document.querySelector("#ani_video_html5_api");
    if (videoPlayer) {
      console.log("Add event listener to videoPlayer");
      videoPlayer.addEventListener("loadstart", () => {
        videoPlayer.muted = false;
      });
    }

    // Register keyboard shortcuts
    registerKeyboardShortcuts(document);

    // 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 });
    }
  }
})();

/**
 * Register keyboard shortcuts
 * @param {Document} doc - The document
 */
function registerKeyboardShortcuts(doc) {
  doc.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) {
      /**
       * Get the document of the event target
       * @type {Document}
       */
      const _doc = event.target.ownerDocument || doc;

      // P pause or play
      if (event.key === "p") {
        const playButton = _doc.querySelector(".vjs-play-control");
        if (playButton) {
          console.log("Pause or play");
          playButton.click();
        } else {
          console.log("No play button found");
        }
      }
      // [ goes to previous video
      else if (event.key === "[") {
        const prevButton = _doc.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 = _doc.querySelector(".vjs-next-button");
        if (nextButton) {
          console.log("Go to next video");
          nextButton.click();
        } else {
          console.log("No next video button found");
        }
      }
      // D enable or disable danmu
      else if (event.key === "d") {
        const danmuButton = _doc.querySelector(
          ".vjs-danmu-button .vjs-menu-button"
        );
        if (danmuButton) {
          console.log("Enable or disable danmu");
          danmuButton.click();
        } else {
          console.log("No danmu button found");
        }
      }
      // T enter or exit theater mode
      else if (event.key === "t") {
        const theaterButton = _doc.querySelector(".vjs-indent-button");
        if (theaterButton) {
          console.log("Enter or exit theater mode");
          theaterButton.click();
        } else {
          console.log("No theater button found");
        }
      }
      // F enter or exit fullscreen
      else if (event.key === "f") {
        const fullscreenButton = _doc.querySelector(".vjs-fullscreen-control");
        if (fullscreenButton) {
          console.log("Enter or exit fullscreen");
          fullscreenButton.click();
        } else {
          console.log("No fullscreen button found");
        }
      }
      // Video player control
      else if (!event.target.closest("video-js")) {
        const dispatchEvent = (eventType) => {
          videoPlayer.dispatchEvent(
            new KeyboardEvent(eventType, {
              key: event.key,
              code: event.code,
              keyCode: event.keyCode,
              which: event.which,
              bubbles: true,
              cancelable: true,
              composed: true,
              isTrusted: true,
            })
          );
        };

        // ↑ video volume up
        if (event.key === "ArrowUp") {
          if (videoPlayer) {
            if (videoPlayer.volume < 1) {
              event.preventDefault();
              console.log("Dispatch video volume up");
              dispatchEvent("keydown");
            }
          } else {
            console.log("No video player found");
          }
        }
        // ↓ video volume down
        else if (event.key === "ArrowDown") {
          if (videoPlayer) {
            if (videoPlayer.volume > 0) {
              event.preventDefault();
              console.log("Dispatch video volume down");
              dispatchEvent("keydown");
            }
          } else {
            console.log("No video player found");
          }
        }
        // ← video backward
        else if (event.key === "ArrowLeft") {
          if (videoPlayer) {
            if (videoPlayer.currentTime > 0) {
              event.preventDefault();
              console.log("Dispatch video backward");
              dispatchEvent("keydown");
            }
          } else {
            console.log("No video player found");
          }
        }
        // → video forward
        else if (event.key === "ArrowRight") {
          if (videoPlayer) {
            if (videoPlayer.currentTime < videoPlayer.duration) {
              event.preventDefault();
              console.log("Dispatch video forward");
              dispatchEvent("keydown");
            }
          } else {
            console.log("No video player found");
          }
        }
      }
    }
  });
}

/**
 * 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-16 version 0.4.0
// - 規範版本號

// 2024-12-16 version 0.3
// - 註冊快捷鍵 ↑ ↓ ← → 分別控制音量、時間軸
// - 註冊快捷鍵 D 控制彈幕

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

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