Enhanced YouTube Ad Blocker

Blocks various YouTube ads, and Home page banners with fucking Sponsored Video Ads.

// ==UserScript==
// @name         Enhanced YouTube Ad Blocker
// @namespace    http://tampermonkey.net/
// @version      1.2.1
// @license      MIT
// @description  Blocks various YouTube ads, and Home page banners with fucking Sponsored Video Ads.
// @author       vippium
// @match        https://www.youtube.com/*
// @grant        none
// @run-at       document-start
// @require      https://cdn.jsdelivr.net/gh/cyfung1031/userscript-supports@8fac46500c5a916e6ed21149f6c25f8d1c56a6a3/library/ytZara.js
// ==/UserScript==

(function () {
  'use strict';

  // 1. Remove rich grid ad slots on homepage
  const wm = new WeakSet();
  const removeAdsSlot = async (grid) => {
    const td = grid.data;
    if (td && !wm.has(td)) {
      const md = Object.assign({}, td);
      md.contents = md.contents.filter(content => {
        let isadSlotRenderer = ((((content || 0).richItemRenderer || 0).content || 0).adSlotRenderer || null) !== null;
        return !isadSlotRenderer;
      });
      wm.add(md);
      grid.data = md;
    }
  };

  ytZara.ytProtoAsync("ytd-rich-grid-renderer").then(proto => {
    proto.dataChanged = ((orig) => function () {
      removeAdsSlot(this);
      return orig.apply(this, arguments);
    })(proto.dataChanged);
  });

  // 2. Observe and remove banner ads, masthead ads
  const adSelectors = [
    'ytd-banner-promo-renderer',
    'ytd-mealbar-promo-renderer',
    'ytd-display-ad-renderer',
    '#player-ads',
    '.ytp-ad-module',
    '.ytp-ad-overlay-container',
    'ytd-companion-slot-renderer',
    '.video-ads',
    'div[class*="ytd-in-feed"] ytd-ad-slot-renderer',
    'ytd-promoted-sparkles-web-renderer',
    'ytd-search-pyv-renderer',
    '.ytp-ad-skip-button-container',
    '.ytp-ad-player-overlay'
  ];

  const removeAdElements = () => {
    adSelectors.forEach(selector => {
      document.querySelectorAll(selector).forEach(el => el.remove());
    });
  };

  const adObserver = new MutationObserver(() => {
    removeAdElements();
  });

  const initAdObserver = () => {
    removeAdElements();
    if (document.body) {
      adObserver.observe(document.body, {
        childList: true,
        subtree: true
      });
    } else {
      // If document.body is not available yet, set up an interval to check
      const checkBody = setInterval(() => {
        if (document.body) {
          adObserver.observe(document.body, {
            childList: true,
            subtree: true
          });
          clearInterval(checkBody); // Stop checking once it's available
        }
      }, 100); // Check every 100ms
    }
  };

  document.addEventListener('DOMContentLoaded', initAdObserver);

  // 3. Block in-video ad playback by overriding player response
  const overridePlayerAds = () => {
    const originalDefineProperty = Object.defineProperty;
    Object.defineProperty = function(obj, prop, descriptor) {
      if (prop === 'playerResponse' && descriptor && descriptor.set) {
        const originalSetter = descriptor.set;
        descriptor.set = function(response) {
          if (response && response.adPlacements) {
            delete response.adPlacements;
          }
          originalSetter.call(this, response);
        };
      }
      return originalDefineProperty.call(Object, obj, prop, descriptor);
    };
  };

  overridePlayerAds();

  // 4. SponsorBlock API integration: Skip sponsor segments in videos
  const sponsorBlockApiUrl = 'https://sponsor.ajay.app/api/skipSegments';
  let videoId = null;

  const fetchSponsorSegments = (videoId) => {
    fetch(`${sponsorBlockApiUrl}?videoId=${videoId}`)
      .then(response => response.json())
      .then(data => {
        if (data && data.segments) {
          skipSponsorSegments(data.segments);
        }
      })
      .catch(error => {
        console.error('Error fetching SponsorBlock data:', error);
      });
  };

  const skipSponsorSegments = (segments) => {
    const player = document.querySelector('video');
    if (!player) return;

    const currentTime = player.currentTime;
    segments.forEach(segment => {
      if (currentTime >= segment.start && currentTime <= segment.end) {
        player.currentTime = segment.end; // Skip to the end of the sponsor segment
      }
    });
  };

  // 5. Monitor for video changes to grab the videoId and use SponsorBlock API
  const videoObserver = new MutationObserver(() => {
    const videoElement = document.querySelector('video');
    if (videoElement && videoElement.src && videoElement.src.includes('youtube.com/watch?v=')) {
      const videoUrlParams = new URLSearchParams(window.location.search);
      videoId = videoUrlParams.get('v');
      if (videoId) {
        fetchSponsorSegments(videoId);
      }
    }
  });

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

  // 6. Restore the Dislike button (like in older versions of YouTube)
  const restoreDislikeButton = () => {
    const dislikeButton = document.querySelector('.ytd-toggle-button-renderer.style-scope.ytd-video-primary-info-renderer');
    const dislikeButtonText = dislikeButton && dislikeButton.querySelector('#text');

    if (dislikeButtonText) {
      dislikeButtonText.style.display = 'inline'; // Make the dislike count visible
    }
  };

  // Monitor for dislike button visibility on the video page
  const dislikeObserver = new MutationObserver(() => {
    restoreDislikeButton();
  });

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

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址