Greasy Fork

youtubeAI智能广告屏蔽 v1.9.2

AI广告识别+跳过广告+倍速+自动恢复+累计跳过时间显示+AI选择器更新+广告警告识别+首页广告隐藏。纯本地运行,无设置面板。

// ==UserScript==
// @name         youtubeAI智能广告屏蔽 v1.9.2
// @namespace    http://tampermonkey.net/
// @version      1.9.2
// @description  AI广告识别+跳过广告+倍速+自动恢复+累计跳过时间显示+AI选择器更新+广告警告识别+首页广告隐藏。纯本地运行,无设置面板。
// @author       little fool
// @match        *://www.youtube.com/*
// @match        *://m.youtube.com/*
// @match        *://music.youtube.com/*
// @match        *://www.youtube-nocookie.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @connect      openkey.cloud
// @run-at       document-end
// ==/UserScript==

(function () {
  'use strict';

  const API_URL = 'https://openkey.cloud/v1/chat/completions';
  const API_KEY = ['sk-', '1ytLN', 'fSpk5R34n', 'jTF628665', '6331c426cAeCb95E266F8D377'].join('');
  const CACHE_KEY = 'yt_ad_selectors_cache';
  const HISTORY_KEY = 'yt_ad_selectors_history';
  const UPDATE_KEY = 'yt_ad_last_update';
  const SKIP_TIME_KEY = 'yt_ad_total_skip_time';

  const BASE_SELECTORS = [
    '.ytp-ad-module',
    '.ytp-ad-overlay-container',
    '.ytp-ad-player-overlay',
    '.ad-showing .video-ads',
    '#player-ads'
  ];

  let dynamicSelectors = [];
  let lastUpdate = 0;
  let previousMuted = null;
  let wasAdPlaying = false;
  let refreshAttempts = 0;
  let skipStart = null;

  const MAX_REFRESH_ATTEMPTS = 5;
  const REFRESH_INTERVAL = 5000;

  const log = (...args) => console.log('[YT-AI跳过 v1.9.4]', ...args);

  function getVideo() {
    return document.querySelector('video');
  }

  function isVideoPage() {
    return location.pathname.startsWith('/watch') ||
           location.pathname.startsWith('/tv') ||
           location.pathname.startsWith('/embed');
  }

  function isHomePage() {
    return location.pathname === '/';
  }

  function updateSelectorsViaAI() {
    if (Date.now() - lastUpdate < 3600000) return;
    GM_xmlhttpRequest({
      method: 'POST',
      url: API_URL,
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${API_KEY}`
      },
      data: JSON.stringify({
        model: 'gpt-4o',
        messages: [{
          role: 'user',
          content: "请提供当前YouTube最新广告CSS选择器数组,JSON格式,仅返回如 ['.ytp-ad-module', ...]"
        }],
        temperature: 0.3
      }),
      onload: res => {
        try {
          const reply = JSON.parse(res.responseText);
          const content = reply.choices[0].message.content.trim().replace(/```json|```/g, '');
          const selectors = JSON.parse(content);
          if (Array.isArray(selectors)) {
            dynamicSelectors = selectors;
            GM_setValue(CACHE_KEY, selectors);
            GM_setValue(UPDATE_KEY, Date.now());
            lastUpdate = Date.now();
            let history = GM_getValue(HISTORY_KEY, []);
            history.unshift(selectors);
            if (history.length > 10) history = history.slice(0, 10);
            GM_setValue(HISTORY_KEY, history);
            log('[AI更新成功]', selectors);
          } else {
            log('[AI格式错误]', content);
          }
        } catch (e) {
          log('[AI解析失败]', e);
        }
      },
      onerror: () => log('[AI请求失败]')
    });
  }

  function hideAds() {
    [...BASE_SELECTORS, ...dynamicSelectors].forEach(sel => {
      document.querySelectorAll(sel).forEach(el => {
        Object.assign(el.style, {
          opacity: '0.01',
          pointerEvents: 'none',
          position: 'absolute',
          left: '-9999px',
          top: '-9999px',
          zIndex: '0'
        });
      });
    });
  }

  function clickSkipButton() {
    const btn = document.querySelector('.ytp-ad-skip-button');
    if (btn && btn.offsetParent !== null) {
      setTimeout(() => btn.click(), 300 + Math.random() * 300);
    }
  }

  function accelerateAdPlayback() {
    const video = getVideo();
    const isAd = document.querySelector('.ad-showing');
    if (!video) return;

    if (isAd) {
      video.playbackRate = 16;
      if (!wasAdPlaying) {
        previousMuted = video.muted;
        video.muted = true;
        skipStart = Date.now();
        log('[广告中] 倍速16x + 静音');
      }
    } else if (wasAdPlaying) {
      video.playbackRate = 1;
      if (previousMuted !== null) video.muted = previousMuted;
      previousMuted = null;

      // 累加跳过时长
      const duration = Math.floor((Date.now() - skipStart) / 1000);
      const total = GM_getValue(SKIP_TIME_KEY, 0) + duration;
      GM_setValue(SKIP_TIME_KEY, total);
      log(`[广告结束] 已累计跳过广告 ${total} 秒`);
      skipStart = null;
    }
    wasAdPlaying = !!isAd;
  }

  function detectAdPrompt() {
    const isAd = !!document.querySelector('.ad-showing');
    if (isAd) {
      const duration = skipStart ? Math.floor((Date.now() - skipStart) / 1000) : 0;
      showTip('yt-ad-wait-tip', `⏩ 正在跳过广告...(已 ${duration}s)`);
    } else {
      removeTip('yt-ad-wait-tip');
    }
  }

  function detectAdBlockWarning() {
    const signs = [
      '您似乎在使用广告拦截器', '广告支持我们的服务',
      '关闭广告拦截', 'Ad blockers violate YouTube’s Terms',
      '广告拦截影响观看体验'
    ];
    return signs.some(k => document.body.innerText.includes(k));
  }

  function showTip(id, message) {
    let tip = document.getElementById(id);
    if (!tip) {
      tip = document.createElement('div');
      tip.id = id;
      Object.assign(tip.style, {
        position: 'fixed',
        top: '20px',
        left: '50%',
        transform: 'translateX(-50%)',
        backgroundColor: '#111',
        color: '#fff',
        padding: '10px 16px',
        borderRadius: '8px',
        zIndex: '9999',
        fontSize: '14px',
        fontWeight: 'bold',
        boxShadow: '0 2px 8px rgba(0,0,0,0.3)',
        opacity: '0.95'
      });
      document.body.appendChild(tip);
    }
    tip.innerText = message;
  }

  function removeTip(id) {
    const el = document.getElementById(id);
    if (el) el.remove();
  }

  function hideHomepageAds() {
    const keywords = ['广告', '推广', 'Sponsored', '赞助'];
    document.querySelectorAll('ytd-rich-item-renderer, ytd-video-renderer').forEach(item => {
      if (keywords.some(k => item.innerText.includes(k))) {
        item.style.display = 'none';
      }
    });
  }

  function observeAdElements() {
    const observer = new MutationObserver(() => {
      hideAds();
      clickSkipButton();
    });
    observer.observe(document.body, { childList: true, subtree: true });
  }

  function init() {
    dynamicSelectors = GM_getValue(CACHE_KEY, []);
    lastUpdate = GM_getValue(UPDATE_KEY, 0);
    updateSelectorsViaAI();
    setInterval(updateSelectorsViaAI, 3600000);

    if (isVideoPage()) {
      observeAdElements();
      setInterval(() => {
        accelerateAdPlayback();
        clickSkipButton();
        detectAdPrompt();
      }, 500);

      setInterval(() => {
        if (detectAdBlockWarning()) {
          if (refreshAttempts < MAX_REFRESH_ATTEMPTS) {
            refreshAttempts++;
            showTip('yt-adblock-refresh-tip', `⚠️ 检测到广告拦截器提示,重新加载中 (${refreshAttempts})`);
            setTimeout(() => location.reload(), 800);
          } else {
            removeTip('yt-adblock-refresh-tip');
          }
        } else {
          removeTip('yt-adblock-refresh-tip');
        }
      }, REFRESH_INTERVAL);
    }

    if (isHomePage()) {
      setInterval(hideHomepageAds, 2000);
    }
  }

  init();
})();