YouTube 自动跳过广告

自动跳过 YouTube 视频广告

  1. // ==UserScript==
  2. // @name YouTube Bỏ qua quảng cáo video tự động
  3. // @name:en YouTube Auto Ad Skipper
  4. // @name:vi YouTube Bỏ qua quảng cáo video tự động
  5. // @name:zh-cn YouTube 自动跳过广告
  6. // @name:zh-tw YouTube 自動跳過廣告
  7. // @name:ja YouTube 広告自動スキップ
  8. // @name:ko YouTube 자동 광고 건너뛰기
  9. // @name:es YouTube Saltar anuncios automáticamente
  10. // @name:ru YouTube Автоматический пропуск рекламы
  11. // @name:id YouTube Lewati Iklan Otomatis
  12. // @name:hi YouTube स्वचालित विज्ञापन स्किपर
  13. // @namespace http://tampermonkey.net/
  14. // @version 2025.27.3.1
  15. // @description Tự động bỏ qua quảng cáo trên YouTube
  16. // @description:en Automatically skip ads on YouTube videos
  17. // @description:vi Tự động bỏ qua quảng cáo trên YouTube
  18. // @description:zh-cn 自动跳过 YouTube 视频广告
  19. // @description:zh-tw 自動跳過 YouTube 影片廣告
  20. // @description:ja YouTube動画の広告を自動的にスキップ
  21. // @description:ko YouTube 동영상의 광고를 자동으로 건너뛰기
  22. // @description:es Salta automáticamente los anuncios en videos de YouTube
  23. // @description:ru Автоматически пропускает рекламу в видео на YouTube
  24. // @description:id Otomatis melewati iklan di video YouTube
  25. // @description:hi YouTube वीडियो में विज्ञापनों को स्वचालित रूप से छोड़ें
  26. // @author RenjiYuusei
  27. // @license MIT
  28. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  29. // @match https://*.youtube.com/*
  30. // @grant GM_addStyle
  31. // @grant GM_getValue
  32. // @grant GM_setValue
  33. // @run-at document-start
  34. // @compatible chrome
  35. // @compatible firefox
  36. // @compatible edge
  37. // @compatible safari
  38. // ==/UserScript==
  39.  
  40. (function () {
  41. 'use strict';
  42.  
  43. const DEFAULT_CONFIG = {
  44. allowedReloadPage: true,
  45. dontReloadWhileBusy: true,
  46. maxScrollThreshold: 200,
  47. adSkipDelay: 250,
  48. maxPlaybackRate: 16,
  49. maxSkipAttempts: 20,
  50. autoMuteAds: true,
  51. hideAllAds: true,
  52. checkInterval: 300,
  53. minSkipInterval: 30,
  54. useAggressiveSkipping: true,
  55. };
  56.  
  57. class YouTubeAdSkipper {
  58. constructor() {
  59. this.video = null;
  60. this.currentVideoTime = 0;
  61. this.isTabBlurred = false;
  62. this.skipAttempts = 0;
  63. this.maxSkipAttempts = DEFAULT_CONFIG.maxSkipAttempts;
  64. this.lastSkipTime = 0;
  65. this.config = DEFAULT_CONFIG;
  66. this.errorCount = 0;
  67. this.maxErrors = 3;
  68. this.debounceTimeout = null;
  69. this.recoveryAttempts = 0;
  70. this.maxRecoveryAttempts = 3;
  71. this.recoveryTimeout = null;
  72. this.isAdPlaying = false;
  73. this.userPaused = false;
  74. this.lastAdCheckTime = 0;
  75. this.lastVerifiedAdTime = 0;
  76. this.lastVideoUrl = '';
  77. this.init();
  78. }
  79.  
  80. init() {
  81. try {
  82. this.loadConfig();
  83. this.setupEventListeners();
  84. this.setupMutationObserver();
  85.  
  86. if (this.config.hideAllAds) {
  87. this.addCSSHideAds();
  88. }
  89.  
  90. this.skipAd();
  91. this.startAdCheckInterval();
  92. } catch (error) {
  93. // Bỏ qua lỗi trong quá trình khởi tạo
  94. }
  95. }
  96.  
  97. loadConfig() {
  98. try {
  99. const savedConfig = GM_getValue('adSkipperConfig');
  100. if (savedConfig) {
  101. this.config = { ...DEFAULT_CONFIG, ...savedConfig };
  102. }
  103. } catch (error) {
  104. this.config = DEFAULT_CONFIG;
  105. this.saveConfig();
  106. }
  107. }
  108.  
  109. saveConfig() {
  110. try {
  111. GM_setValue('adSkipperConfig', this.config);
  112. } catch (error) {
  113. // Bỏ qua lỗi khi lưu cấu hình
  114. }
  115. }
  116.  
  117. setupEventListeners() {
  118. window.addEventListener('blur', () => (this.isTabBlurred = true));
  119. window.addEventListener('focus', () => {
  120. this.isTabBlurred = false;
  121. this.skipAd();
  122. });
  123.  
  124. document.addEventListener('timeupdate', this.handleTimeUpdate.bind(this), true);
  125.  
  126. document.addEventListener('yt-navigate-finish', () => {
  127. this.skipAttempts = 0;
  128. this.resetAdState();
  129. this.skipAd();
  130. });
  131.  
  132. document.addEventListener('loadstart', (e) => {
  133. if (e.target.matches('video.html5-main-video')) {
  134. this.skipAttempts = 0;
  135. this.resetAdState();
  136. const currentUrl = window.location.href;
  137. if (this.lastVideoUrl !== currentUrl) {
  138. this.lastVideoUrl = currentUrl;
  139. }
  140. setTimeout(() => this.skipAd(), 100);
  141. }
  142. }, true);
  143.  
  144. document.addEventListener(
  145. 'pause',
  146. (e) => {
  147. if (e.target === this.video) {
  148. const player = document.querySelector('#movie_player');
  149. const isAd = this.isAdCurrentlyPlaying(player) && this.isConfirmedAd();
  150. if (isAd && this.video.paused) {
  151. setTimeout(() => {
  152. this.video.play().catch(() => {});
  153. }, 500);
  154. } else {
  155. this.userPaused = true;
  156. }
  157. }
  158. },
  159. true
  160. );
  161. document.addEventListener(
  162. 'play',
  163. (e) => {
  164. if (e.target === this.video) {
  165. this.userPaused = false;
  166. }
  167. },
  168. true
  169. );
  170. }
  171.  
  172. isConfirmedAd() {
  173. const adInfo = document.querySelector('.ytp-ad-info-dialog-container');
  174. const adText = document.querySelector('.ytp-ad-text');
  175. const adSkipButton = document.querySelector('.ytp-ad-skip-button-container, .ytp-ad-skip-button-modern');
  176. const adPreview = document.querySelector('.ytp-ad-preview-container');
  177. const adByLine = document.querySelector('.ytp-ad-byline-container');
  178. const videoAdUiText = document.querySelector('.videoAdUiSkipButton');
  179. const adPlayerOverlay = document.querySelector('.ytp-ad-player-overlay');
  180. let confirmationCount = 0;
  181. if (adInfo) confirmationCount++;
  182. if (adText) confirmationCount++;
  183. if (adSkipButton) confirmationCount++;
  184. if (adPreview) confirmationCount++;
  185. if (adByLine) confirmationCount++;
  186. if (videoAdUiText) confirmationCount++;
  187. if (adPlayerOverlay) confirmationCount++;
  188. const player = document.querySelector('#movie_player');
  189. if (player && player.classList.contains('ad-showing')) {
  190. confirmationCount += 2;
  191. }
  192. return confirmationCount >= 2;
  193. }
  194.  
  195. resetAdState() {
  196. this.isAdPlaying = false;
  197. this.skipAttempts = 0;
  198. this.lastAdCheckTime = 0;
  199. this.lastVerifiedAdTime = 0;
  200. }
  201.  
  202. handleTimeUpdate(e) {
  203. if (e.target.matches('video.html5-main-video')) {
  204. const player = document.querySelector('#movie_player');
  205. const isAd = player && this.isAdCurrentlyPlaying(player) && this.isConfirmedAd();
  206. if (!isAd) {
  207. this.currentVideoTime = e.target.currentTime;
  208. } else if (this.config.useAggressiveSkipping) {
  209. const now = Date.now();
  210. if (now - this.lastAdCheckTime > 1000) {
  211. this.lastAdCheckTime = now;
  212. this.skipAd();
  213. }
  214. }
  215. }
  216. }
  217.  
  218. setupMutationObserver() {
  219. const observer = new MutationObserver((mutations) => {
  220. if (this.isTabBlurred) return;
  221.  
  222. const adRelatedChange = mutations.some(mutation => {
  223. if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
  224. return mutation.target.classList.contains('ad-showing') ||
  225. mutation.target.classList.contains('ytp-ad-player-overlay');
  226. }
  227. if (mutation.type === 'childList' && mutation.addedNodes.length) {
  228. for (const node of mutation.addedNodes) {
  229. if (node.nodeType === 1) {
  230. if (node.classList && (
  231. node.classList.contains('ad-showing') ||
  232. node.classList.contains('ytp-ad-player-overlay') ||
  233. node.querySelector('.ad-showing, .ytp-ad-player-overlay')
  234. )) {
  235. return true;
  236. }
  237. }
  238. }
  239. }
  240. return false;
  241. });
  242.  
  243. if (adRelatedChange) {
  244. this.skipAd();
  245. } else {
  246. clearTimeout(this.debounceTimeout);
  247. this.debounceTimeout = setTimeout(() => {
  248. this.skipAd();
  249. }, 100);
  250. }
  251. });
  252.  
  253. const observeBody = () => {
  254. if (document.body) {
  255. observer.observe(document.body, {
  256. attributes: true,
  257. attributeFilter: ['class', 'src', 'style'],
  258. childList: true,
  259. subtree: true,
  260. });
  261. } else {
  262. setTimeout(observeBody, 50);
  263. }
  264. };
  265.  
  266. observeBody();
  267. }
  268.  
  269. startAdCheckInterval() {
  270. setInterval(() => {
  271. if (!this.isTabBlurred) {
  272. this.skipAd();
  273. }
  274. }, this.config.checkInterval);
  275. }
  276.  
  277. async skipAd() {
  278. try {
  279. if (window.location.pathname.startsWith('/shorts/')) return;
  280.  
  281. const player = document.querySelector('#movie_player');
  282. if (!player) return;
  283.  
  284. const hasAd = this.isAdCurrentlyPlaying(player);
  285. const isConfirmedAd = hasAd && this.isConfirmedAd();
  286. this.isAdPlaying = isConfirmedAd;
  287.  
  288. this.video = player.querySelector('video.html5-main-video');
  289.  
  290. if (isConfirmedAd && this.video && !this.userPaused) {
  291. this.lastVerifiedAdTime = Date.now();
  292. await this.handleVideoAd();
  293. this.handlePrerollAds();
  294. } else if (!hasAd) {
  295. this.skipAttempts = 0;
  296. this.isAdPlaying = false;
  297. }
  298.  
  299. this.removeAdBlockerWarnings();
  300. this.removeShortVideoAds();
  301. this.removeOverlayAds();
  302. this.handleSponsoredItems();
  303. } catch (error) {
  304. this.errorCount++;
  305. if (this.errorCount >= this.maxErrors) {
  306. await this.attemptRecovery();
  307. }
  308. }
  309. }
  310.  
  311. isAdCurrentlyPlaying(player) {
  312. if (!player) return false;
  313. if (player.classList.contains('ad-showing')) return true;
  314. const adSelectors = [
  315. '.ytp-ad-player-overlay',
  316. '.ad-showing',
  317. '.ytp-ad-skip-button-slot',
  318. '.ytp-ad-preview-container',
  319. '.ytp-ad-skip-button-modern',
  320. '.ytp-ad-text-overlay',
  321. '.ytp-ad-feedback-dialog-container',
  322. '[id^="ad-placeholder"]'
  323. ];
  324. return adSelectors.some(selector => document.querySelector(selector) !== null);
  325. }
  326.  
  327. async handleVideoAd() {
  328. const now = Date.now();
  329. if (now - this.lastSkipTime < this.config.minSkipInterval) return;
  330. this.lastSkipTime = now;
  331.  
  332. this.clickSkipButtons();
  333.  
  334. if (this.isAdPlaying && this.video && this.video.src &&
  335. now - this.lastVerifiedAdTime < 3000) {
  336. this.video.currentTime = this.video.duration || 9999;
  337. this.video.playbackRate = this.config.maxPlaybackRate;
  338. if (this.config.autoMuteAds) {
  339. this.video.muted = true;
  340. this.video.volume = 0;
  341. }
  342. if (this.config.useAggressiveSkipping) {
  343. const adElement = document.querySelector('.html5-video-container');
  344. if (adElement && this.isConfirmedAd()) {
  345. adElement.style.visibility = 'hidden';
  346. }
  347. }
  348. }
  349.  
  350. if (this.skipAttempts < this.maxSkipAttempts && this.isAdPlaying) {
  351. this.skipAttempts++;
  352. await new Promise(resolve => setTimeout(resolve, this.config.adSkipDelay));
  353. this.skipAd();
  354. }
  355. }
  356.  
  357. clickSkipButtons() {
  358. const skipButtonSelectors = [
  359. '.ytp-skip-ad-button',
  360. '.ytp-ad-skip-button',
  361. '.ytp-ad-skip-button-modern',
  362. '.ytp-ad-survey-answer-button',
  363. '.ytp-ad-skip-button-container button',
  364. '[class*="skip-button"]',
  365. '[class*="skipButton"]',
  366. '.videoAdUiSkipButton',
  367. '.ytp-ad-preview-container button',
  368. '.ytp-ad-skip-button-container',
  369. '.ytp-ad-skip-button-modern-container',
  370. 'button[class*="skip"]',
  371. '[data-tooltip-content="Skip Ad"]',
  372. '[aria-label*="Skip Ad"]',
  373. '[aria-label*="Skip Ads"]',
  374. '[data-title-no-tooltip*="Skip Ad"]'
  375. ];
  376.  
  377. skipButtonSelectors.forEach(selector => {
  378. const buttons = document.querySelectorAll(selector);
  379. buttons.forEach(button => {
  380. if (button && button.offsetParent !== null) {
  381. button.click();
  382. }
  383. });
  384. });
  385. const adContainers = document.querySelectorAll('.ytp-ad-module, .ytp-ad-action-interstitial');
  386. adContainers.forEach(container => {
  387. if (container) {
  388. const allButtons = container.querySelectorAll('button');
  389. allButtons.forEach(button => {
  390. if (button && button.offsetParent !== null) {
  391. button.click();
  392. }
  393. });
  394. }
  395. });
  396. }
  397.  
  398. removeAdBlockerWarnings() {
  399. const warningSelectors = [
  400. 'tp-yt-paper-dialog:has(#feedback.ytd-enforcement-message-view-model)',
  401. '.yt-playability-error-supported-renderers:has(.ytd-enforcement-message-view-model)',
  402. 'ytd-enforcement-message-view-model',
  403. '.ytd-popup-container',
  404. 'tp-yt-paper-dialog.ytd-popup-container',
  405. 'ytd-enforcement-message-view-model',
  406. 'ytd-watch-flexy[player-unavailable]',
  407. 'div#error-screen',
  408. 'div.ytd-player-error-message-renderer',
  409. 'div.ytp-error'
  410. ];
  411.  
  412. warningSelectors.forEach(selector => {
  413. const warning = document.querySelector(selector);
  414. if (warning) {
  415. if (selector.includes('playability-error') && this.checkCanReloadPage()) {
  416. this.reloadPage();
  417. }
  418. warning.remove();
  419. }
  420. });
  421. }
  422.  
  423. removeShortVideoAds() {
  424. const shortAdSelectors = [
  425. 'ytd-reel-video-renderer:has(.ytd-ad-slot-renderer)',
  426. 'ytd-in-feed-ad-layout-renderer',
  427. 'ytd-promoted-video-renderer',
  428. 'ytd-compact-promoted-video-renderer',
  429. 'ytd-display-ad-renderer',
  430. 'ytd-brand-video-singleton-renderer',
  431. 'ytd-brand-video-shelf-renderer',
  432. 'ytd-statement-banner-renderer',
  433. 'ytd-video-masthead-ad-v3-renderer',
  434. 'ytd-ad-badge-renderer',
  435. 'ytd-promoted-sparkles-web-renderer',
  436. 'ytd-banner-promo-renderer',
  437. 'ytd-promoted-rising-renderer',
  438. 'ytd-carousel-ad-renderer',
  439. 'ytd-shopping-companion-ad-renderer',
  440. 'div#masthead-ad',
  441. '#player-ads',
  442. 'div.ytd-mealbar-promo-renderer',
  443. 'ytm-promoted-video-renderer'
  444. ].join(',');
  445.  
  446. document.querySelectorAll(shortAdSelectors).forEach(ad => ad.remove());
  447. }
  448.  
  449. removeOverlayAds() {
  450. const overlayAdSelectors = [
  451. '.ytp-ad-overlay-container',
  452. '.ytp-ad-text-overlay',
  453. '.ytp-ad-overlay-slot',
  454. 'div[id^="ad-overlay"]',
  455. '.video-ads',
  456. '.ytp-ad-overlay-image',
  457. '.ytp-ad-text-overlay-container',
  458. '.ytp-ad-overlay-ad-info-button-container',
  459. '.ytp-ad-overlay-close-container',
  460. '.ytp-ad-overlay-slot',
  461. '.yt-mealbar-promo-renderer',
  462. '.yt-tooltip-renderer',
  463. '.iv-promo',
  464. '#companion',
  465. '#player-overlay:has(.ytp-ad-overlay-container)',
  466. '#offer-module'
  467. ].join(',');
  468.  
  469. document.querySelectorAll(overlayAdSelectors).forEach(ad => {
  470. if (ad) {
  471. ad.style.display = 'none';
  472. ad.remove();
  473. }
  474. });
  475. }
  476.  
  477. handleSponsoredItems() {
  478. const sponsoredSelectors = [
  479. 'ytd-item-section-renderer:has(span:contains("Sponsored"))',
  480. 'ytd-item-section-renderer:has(span:contains("Quảng cáo"))',
  481. 'ytd-item-section-renderer:has(span:contains("広告"))',
  482. 'ytd-item-section-renderer:has(span:contains("广告"))',
  483. 'ytd-rich-item-renderer:has([id="ad-badge"])',
  484. 'ytd-compact-promoted-item-renderer',
  485. 'ytd-search ytd-video-renderer:has(div#badge)',
  486. 'ytd-browse ytd-rich-item-renderer:has(ytd-display-ad-renderer)',
  487. 'ytd-browse ytd-rich-item-renderer:has(ytd-ad-slot-renderer)'
  488. ];
  489. sponsoredSelectors.forEach(selector => {
  490. try {
  491. document.querySelectorAll(selector).forEach(item => item.remove());
  492. } catch (e) {
  493. // Bỏ qua lỗi selector không hợp lệ
  494. }
  495. });
  496. }
  497.  
  498. checkCanReloadPage() {
  499. if (!this.config.allowedReloadPage) return false;
  500. if (!this.config.dontReloadWhileBusy) return true;
  501. if (document.activeElement?.matches('input, textarea, select')) return false;
  502. if (document.documentElement.scrollTop > this.config.maxScrollThreshold) return false;
  503. if (this.isTabBlurred) return false;
  504. return true;
  505. }
  506.  
  507. reloadPage() {
  508. const params = new URLSearchParams(location.search);
  509. if (this.currentVideoTime > 0) {
  510. params.set('t', Math.floor(this.currentVideoTime) + 's');
  511. }
  512. location.replace(`${location.origin}${location.pathname}?${params.toString()}`);
  513. }
  514.  
  515. addCSSHideAds() {
  516. const styles = `
  517. #player-ads,
  518. #masthead-ad,
  519. ytd-ad-slot-renderer,
  520. ytd-rich-item-renderer:has(.ytd-ad-slot-renderer),
  521. ytd-rich-section-renderer:has(.ytd-statement-banner-renderer),
  522. ytd-reel-video-renderer:has(.ytd-ad-slot-renderer),
  523. tp-yt-paper-dialog:has(#feedback.ytd-enforcement-message-view-model),
  524. tp-yt-paper-dialog:has(> ytd-checkbox-survey-renderer),
  525. .ytp-suggested-action,
  526. .yt-mealbar-promo-renderer,
  527. ytmusic-mealbar-promo-renderer,
  528. ytmusic-statement-banner-renderer,
  529. .ytd-display-ad-renderer,
  530. .ytd-statement-banner-renderer,
  531. .ytd-in-feed-ad-layout-renderer,
  532. .ytp-ad-overlay-container,
  533. .ytp-ad-text-overlay,
  534. ytd-promoted-sparkles-web-renderer,
  535. ytd-promoted-video-renderer,
  536. .ytd-banner-promo-renderer,
  537. .ytd-video-masthead-ad-v3-renderer,
  538. .ytd-primetime-promo-renderer,
  539. .ytp-ad-skip-button-slot,
  540. .ytp-ad-preview-slot,
  541. .ytp-ad-message-slot,
  542. .ytp-ad-overlay-ad-info-button-container,
  543. .ytp-ad-survey-interstitial,
  544. ytd-in-feed-ad-layout-renderer,
  545. ytd-ad-slot-renderer,
  546. ytd-brand-video-singleton-renderer,
  547. ytd-compact-promoted-video-renderer,
  548. ytd-display-ad-renderer,
  549. ytd-banner-promo-renderer,
  550. ytd-statement-banner-renderer,
  551. ytd-brand-video-shelf-renderer,
  552. ytd-promoted-sparkles-web-renderer,
  553. ytd-ad-feedback-dialog-renderer,
  554. [class*="ytd-ad-badge-renderer"],
  555. ytd-ad-badge-renderer,
  556. div.ytp-ad-overlay-container,
  557. div.ytp-ad-text-overlay,
  558. div.ytp-ad-button-overlay,
  559. div#masthead-ad,
  560. div#offer-module,
  561. div#player-ads,
  562. ytd-item-section-renderer:has(ytd-ad-slot-renderer),
  563. .ytd-companion-slot-renderer,
  564. .ytd-action-companion-ad-renderer,
  565. .ytd-watch-next-secondary-results-renderer.ytd-item-section-renderer:has(.ytd-ad-slot-renderer),
  566. ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"],
  567. #related #items ytd-compact-promoted-video-renderer,
  568. .ytd-carousel-ad-renderer {
  569. display: none !important;
  570. }
  571. .html5-video-player.ad-showing .video-stream[src*="blob"] {
  572. visibility: hidden !important;
  573. }
  574. .html5-video-player:not(.ad-showing) .video-stream {
  575. visibility: visible !important;
  576. }
  577. `;
  578. GM_addStyle(styles);
  579. }
  580.  
  581. handlePrerollAds() {
  582. const prerollContainer = document.querySelector('.ytp-ad-preview-container');
  583. if (prerollContainer && this.isAdPlaying && this.isConfirmedAd()) {
  584. this.video.currentTime = this.video.duration || 9999;
  585. this.video.playbackRate = this.config.maxPlaybackRate;
  586. }
  587. }
  588.  
  589. async attemptRecovery() {
  590. if (this.recoveryAttempts >= this.maxRecoveryAttempts) return;
  591.  
  592. this.recoveryAttempts++;
  593.  
  594. clearTimeout(this.recoveryTimeout);
  595. this.recoveryTimeout = setTimeout(() => {
  596. this.errorCount = 0;
  597. this.skipAttempts = 0;
  598. this.resetAdState();
  599. this.init();
  600. this.recoveryAttempts = 0;
  601. }, 5000 * this.recoveryAttempts);
  602. }
  603. }
  604.  
  605. new YouTubeAdSkipper();
  606. })();

QingJ © 2025

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