Greasy Fork

Greasy Fork is available in English.

YouTube Ad Skipper

Automatically skip ads on YouTube with advanced options and performance improvements

当前为 2024-08-07 提交的版本,查看 最新版本

// ==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          de Anuncios de YouTube Mejorado
// @name:ru          пропуск рекламы на YouTube
// @name:id          YouTube Ad Skipper
// @name:hi          YouTube विज्ञापन स्किपर
// @namespace       http://tampermonkey.net/
// @version         1.3.1
// @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';

  // Configuration
  const defaultConfig = {
    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 = GM_getValue('AdSkipperConfig', defaultConfig);

  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 = {
            ...defaultConfig,
            ...JSON.parse(newConfig)
          };
          saveConfig();
          alert("Configuration updated successfully!");
          location.reload();
        }
        catch (e) {
          alert("Error: Invalid configuration format!");
        }
      }
    });
  }

  function skipAds() {
    const adElement = document.querySelector('.ad-showing');
    if (adElement) {
      const video = document.querySelector('video');
      if (video) {
        video.currentTime = video.duration;
        if (config.muteAds) {
          video.muted = true;
        }
      }
    }

    const skipButtons = document.querySelectorAll('.ytp-ad-skip-button, .ytp-ad-skip-button-modern');
    skipButtons.forEach(button => button.click());

    if (config.hideAdOverlays) {
      const closeBanner = document.querySelector('.ytp-ad-overlay-close-button');
      if (closeBanner) {
        closeBanner.click();
      }
    }

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

    if (config.autoHideAnnotations) {
      const annotationToggle = document.querySelector('.ytp-ce-covering-overlay');
      if (annotationToggle) {
        annotationToggle.style.display = 'none';
      }
    }

    if (config.disableAutoplay) {
      const autoplayToggle = document.querySelector('.ytp-autonav-toggle-button');
      if (autoplayToggle && autoplayToggle.getAttribute('aria-checked') === 'true') {
        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 {
                display: none !important;
            }
            ytd-watch-next-secondary-results-renderer {
                display: none !important;
            }
        `);
  }

  createSettingsMenu();
})();