Greasy Fork

YouTube 智能16倍广告加速器-不在屏蔽广告 v1.3 (最终稳定版)

跳过广告、过滤字幕、黑屏提示、AI自愈、本地缓存、自动识别广告拦截警告并重新加载、支持首页广告屏蔽!完全本地运行、无设置面板。

// ==UserScript==
// @name         YouTube 智能16倍广告加速器-不在屏蔽广告 v1.3 (最终稳定版)
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  跳过广告、过滤字幕、黑屏提示、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 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;
 
  const MAX_REFRESH_ATTEMPTS = 5;
  const REFRESH_INTERVAL = 5000;
 
  const log = (...args) => console.log('[YT净化 v1.9.1]', ...args);
 
  function getVideo() {
    return document.querySelector('video') || document.querySelector('ytd-player video') || document.querySelector('#movie_player 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);
            fallbackSelectors();
          }
        } catch (e) {
          log('[AI解析失败]', e);
          fallbackSelectors();
        }
      },
      onerror: () => {
        log('[AI请求失败]');
        fallbackSelectors();
      }
    });
  }
 
  function fallbackSelectors() {
    const history = GM_getValue(HISTORY_KEY, []);
    if (history.length > 0) {
      dynamicSelectors = history[0];
      log('[容灾] 使用历史缓存');
    } else {
      dynamicSelectors = [];
      log('[严重容灾] 无历史,仅用默认选择器');
    }
  }
 
  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 ad = document.querySelector('.ad-showing');
    if (!video) return;
    if (ad) {
      video.playbackRate = 16;
      if (!wasAdPlaying) {
        previousMuted = video.muted;
        video.muted = true;
        log('[广告中] 倍速+静音');
      }
    } else {
      video.playbackRate = 1;
      if (wasAdPlaying && previousMuted !== null) {
        video.muted = previousMuted;
        previousMuted = null;
        log('[广告结束] 恢复音量');
      }
    }
    wasAdPlaying = !!ad;
    if (!ad) refreshAttempts = 0;
  }
 
  function removeAdSubtitles() {
    const keywords = ['广告', '贊助', '推广', 'Sponsored', 'Sponsor'];
    document.querySelectorAll('.ytp-caption-segment').forEach(el => {
      if (keywords.some(k => el.textContent.includes(k))) {
        el.style.display = 'none';
        el.textContent = '';
      }
    });
  }
 
  function detectAdBlackScreen() {
    const video = getVideo();
    const isAd = document.querySelector('.ad-showing');
    const isBlack = video && video.readyState >= 2 && video.videoWidth === 0;
    const stuck = isAd && video?.currentTime < 2;
    if ((isAd && isBlack) || stuck) showTip('yt-ad-wait-tip', '⏳ 正在跳过广告,请稍候...');
    else removeTip('yt-ad-wait-tip');
  }
 
  function detectAdBlockWarning() {
    const signs = [
      '您似乎在使用广告拦截器',
      '广告支持我们的服务',
      '关闭广告拦截',
      'Ad blockers violate YouTube’s Terms',
      '广告拦截影响观看体验',
      '使用广告拦截器违反 YouTube 服务条款'
    ];
    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: id.includes('wait') ? '20px' : '60px',
        left: '50%',
        transform: 'translateX(-50%)',
        backgroundColor: id.includes('wait') ? '#111' : '#c00',
        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 => {
      const text = item.innerText;
      if (keywords.some(k => text.includes(k))) {
        item.style.display = 'none';
      }
    });
  }
 
  function observeAdElements() {
    const observer = new MutationObserver(() => {
      hideAds();
      clickSkipButton();
      accelerateAdPlayback();
      removeAdSubtitles();
    });
    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(() => {
        detectAdBlackScreen();
      }, 1000);
 
      setInterval(() => {
        if (detectAdBlockWarning()) {
          if (refreshAttempts < MAX_REFRESH_ATTEMPTS) {
            refreshAttempts++;
            showTip('yt-adblock-refresh-tip', `⚠️ 检测到广告拦截器提示,正在重新加载页面(第 ${refreshAttempts} 次)`);
            setTimeout(() => location.href = location.href, 800);
          } else {
            removeTip('yt-adblock-refresh-tip');
          }
        } else {
          removeTip('yt-adblock-refresh-tip');
        }
      }, REFRESH_INTERVAL);
    }
 
    if (isHomePage()) {
      setInterval(() => {
        hideHomepageAds();
      }, 2000);
    }
  }
 
  init();
})();