YouTube 廣告跳過器

在 YouTube 上自動跳過廣告,提供進階選項和效能改進

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

// ==UserScript==
// @name             YouTube Ad Skipper
// @name:en          YouTube Ad Skipper
// @name:vi          Trình Tự Động Bỏ Qua Quảng Cáo YouTube 
// @name:zh-cn       YouTube 广告跳过器
// @name:zh-tw       YouTube 廣告跳過器
// @name:ja          YouTube 広告スキッパー
// @name:ko          YouTube 광고 건너뛰기
// @name:es          Saltador de Anuncios de YouTube Mejorado
// @name:ru          Улучшенный пропуск рекламы на YouTube
// @name:id          YouTube Ad Skipper
// @name:hi          YouTube विज्ञापन स्किपर
// @namespace        http://tampermonkey.net/
// @version          1.5.0
// @description      Automatically skip ads on YouTube with advanced options and performance improvements
// @description:en   Automatically skip ads on YouTube with advanced options and performance improvements
// @description:vi   Tự động bỏ qua quảng cáo trên YouTube với các tùy chọn nâng cao và cải thiện hiệu suất
// @description:zh-cn 在 YouTube 上自动跳过广告,提供高级选项和性能改进
// @description:zh-tw 在 YouTube 上自動跳過廣告,提供進階選項和效能改進
// @description:ja   高度なオプションとパフォーマンス向上を備えた YouTube の広告を自動的にスキップ
// @description:ko   고급 옵션 및 성능 개선으로 YouTube 광고를 자동으로 건너뛰기
// @description:es   Salta automáticamente los anuncios en YouTube con opciones avanzadas y mejoras de rendimiento
// @description:ru   Автоматически пропускает рекламу на YouTube с расширенными настройками и улучшенной производительностью
// @description:id   Lewati iklan secara otomatis di YouTube dengan opsi lanjutan dan peningkatan kinerja
// @description:hi   उन्नत विकल्पों और प्रदर्शन सुधारों के साथ YouTube पर विज्ञापनों को स्वचालित रूप से छोड़ें
// @author           Jann
// @icon             https://www.google.com/s2/favicons?domain=youtube.com
// @license          MIT
// @match            https://*.youtube.com/*
// @grant            GM_setValue
// @grant            GM_getValue
// @grant            GM_registerMenuCommand
// @grant            GM_addStyle
// ==/UserScript==

(function () {
  'use strict';

  const DEFAULT_CONFIG = {
    skipInterval: 500,
    observerThrottle: 1000,
    muteAds: false, // Leave it as the default false. I recommend keeping it false because setting it to true, while muting ads, can also cause the video's audio to be muted as a side effect.
    hideAdOverlays: true,
    removeAdSlots: true,
    autoHideAnnotations: true,
    disableAutoplay: false,
    improvePerformance: true
  };

  let config = { ...DEFAULT_CONFIG, ...GM_getValue('AdSkipperConfig', {}) };

  function saveConfig() {
    GM_setValue('AdSkipperConfig', config);
  }

  function createSettingsMenu() {
    GM_registerMenuCommand("Configure Ad Skipper", () => {
      const newConfig = prompt("Enter new configuration (JSON):", JSON.stringify(config, null, 2));
      if (newConfig) {
        try {
          config = { ...DEFAULT_CONFIG, ...JSON.parse(newConfig) };
          saveConfig();
          alert("Configuration updated successfully!");
          location.reload();
        } catch (e) {
          alert("Error: Invalid configuration format!");
        }
      }
    });
  }

  function skipAds() {
    const video = document.querySelector('video');

    // Skip ads using the skip button
    document.querySelectorAll('.ytp-ad-skip-button, .ytp-ad-skip-button-modern').forEach(button => button.click());

    // Skip ads by seeking to the end
    if (document.querySelector('.ad-showing') && video) {
      video.currentTime = video.duration;
      if (config.muteAds) video.muted = true;
    }

    // Hide ad overlays
    if (config.hideAdOverlays) {
      document.querySelectorAll('.ytp-ad-overlay-close-button').forEach(button => button.click());
    }

    // Remove ad slots
    if (config.removeAdSlots) {
      document.querySelectorAll('ytd-ad-slot-renderer, ytm-promoted-video-renderer').forEach(slot => {
        slot.style.display = 'none';
      });
    }

    // Auto-hide annotations
    if (config.autoHideAnnotations) {
      document.querySelectorAll('.ytp-ce-covering-overlay').forEach(overlay => overlay.style.display = 'none');
    }

    // Disable autoplay
    if (config.disableAutoplay) {
      const autoplayToggle = document.querySelector('.ytp-autonav-toggle-button[aria-checked="true"]');
      if (autoplayToggle) autoplayToggle.click();
    }
  }

  function throttle(func, limit) {
    let inThrottle;
    return function() {
      const args = arguments;
      const context = this;
      if (!inThrottle) {
        func.apply(context, args);
        inThrottle = true;
        setTimeout(() => inThrottle = false, limit);
      }
    }
  }

  const throttledSkipAds = throttle(skipAds, config.observerThrottle);

  const observer = new MutationObserver(throttledSkipAds);

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

  setInterval(skipAds, config.skipInterval);

  if (config.improvePerformance) {
    GM_addStyle(`
      .ytd-watch-flexy:not([theater]) #secondary.ytd-watch-flexy,
      ytd-watch-next-secondary-results-renderer {
        display: none !important;
      }
    `);
  }

  createSettingsMenu();
  })();