自动跳过 YouTube 广告

几乎立即自动跳过 YouTube 广告。删除广告拦截器警告弹出窗口。

目前为 2025-01-22 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Auto Skip YouTube Ads
  3. // @name:vi Tự Động Bỏ Qua Quảng Cáo YouTube
  4. // @name:zh-CN 自动跳过 YouTube 广告
  5. // @name:zh-TW 自動跳過 YouTube 廣告
  6. // @name:ja YouTube 広告を自動スキップ
  7. // @name:ko YouTube 광고 자동 건너뛰기
  8. // @name:es Saltar Automáticamente Anuncios De YouTube
  9. // @name:pt-BR Pular Automaticamente Anúncios Do YouTube
  10. // @name:ru Автоматический Пропуск Рекламы На YouTube
  11. // @name:id Lewati Otomatis Iklan YouTube
  12. // @name:hi YouTube विज्ञापन स्वचालित रूप से छोड़ें
  13. // @namespace https://github.com/tientq64/userscripts
  14. // @version 5.3.0
  15. // @description Automatically skip YouTube ads almost instantly. Remove the ad blocker warning pop-up.
  16. // @description:vi Tự động bỏ qua quảng cáo YouTube gần như ngay lập tức. Loại bỏ cửa sổ bật lên cảnh báo trình chặn quảng cáo.
  17. // @description:zh-CN 几乎立即自动跳过 YouTube 广告。删除广告拦截器警告弹出窗口。
  18. // @description:zh-TW 幾乎立即自動跳過 YouTube 廣告。刪除廣告攔截器警告彈出視窗。
  19. // @description:ja YouTube 広告をほぼ瞬時に自動的にスキップします。広告ブロッカーの警告ポップアップを削除します。
  20. // @description:ko YouTube 광고를 거의 즉시 자동으로 건너뜁니다. 광고 차단 경고 팝업을 제거합니다.
  21. // @description:es Omite automáticamente los anuncios de YouTube casi al instante. Elimina la ventana emergente de advertencia del bloqueador de anuncios.
  22. // @description:pt-BR Pule automaticamente os anúncios do YouTube quase instantaneamente. Remova o pop-up de aviso do bloqueador de anúncios.
  23. // @description:ru Автоматически пропускайте рекламу YouTube почти мгновенно. Уберите всплывающее предупреждение о блокировщике рекламы.
  24. // @description:id Lewati iklan YouTube secara otomatis hampir seketika. Hapus pop-up peringatan pemblokir iklan.
  25. // @description:hi YouTube विज्ञापनों को लगभग तुरंत ही स्वचालित रूप से छोड़ दें। विज्ञापन अवरोधक चेतावनी पॉप-अप हटाएँ।
  26. // @author tientq64
  27. // @icon https://cdn-icons-png.flaticon.com/64/2504/2504965.png
  28. // @match https://www.youtube.com/*
  29. // @match https://m.youtube.com/*
  30. // @match https://music.youtube.com/*
  31. // @grant none
  32. // @license MIT
  33. // @compatible firefox
  34. // @compatible chrome
  35. // @compatible opera
  36. // @compatible safari
  37. // @compatible edge
  38. // @noframes
  39. // @homepage https://github.com/tientq64/userscripts/tree/main/scripts/Auto-Skip-YouTube-Ads
  40. // ==/UserScript==
  41.  
  42. /**
  43. * Skip ads. Remove ad blocker warning.
  44. */
  45. function skipAd() {
  46. hideAdElements()
  47.  
  48. video = null
  49. fineScrubber = document.querySelector('.ytp-fine-scrubbing')
  50.  
  51. // Check if the current URL is a YouTube Shorts URL and exit the function if true.
  52. if (window.location.pathname.startsWith('/shorts/')) return
  53.  
  54. const moviePlayer = document.querySelector('#movie_player')
  55.  
  56. if (moviePlayer) {
  57. hasAd = moviePlayer.classList.contains('ad-showing')
  58. video = moviePlayer.querySelector('video.html5-main-video')
  59. }
  60.  
  61. if (hasAd) {
  62. const skipButton = document.querySelector(`
  63. .ytp-skip-ad-button,
  64. .ytp-ad-skip-button,
  65. .ytp-ad-skip-button-modern,
  66. .ytp-ad-survey-answer-button
  67. `)
  68. // Click the skip ad button if available.
  69. if (skipButton) {
  70. skipButton.click()
  71. skipButton.remove()
  72. }
  73. // Otherwise, fast forward to the end of the ad video.
  74. // Use `9999` instead of `video.duration` to avoid errors when `duration` is not a number.
  75. else if (video && video.src) {
  76. video.currentTime = 9999
  77. }
  78. }
  79.  
  80. if (video) {
  81. video.addEventListener('pause', handleVideoPause)
  82. video.addEventListener('pointerup', allowPauseVideo)
  83. video.addEventListener('timeupdate', handleVideoTimeUpdate)
  84. }
  85.  
  86. // Timed pie countdown ad.
  87. const pieCountdown = document.querySelector('.ytp-ad-timed-pie-countdown-container')
  88. if (pieCountdown) {
  89. pieCountdown.remove()
  90. replaceCurrentVideo()
  91. }
  92.  
  93. // Handle when ad blocker warning appears inside video player.
  94. const adBlockerWarningInner = document.querySelector(
  95. '.yt-playability-error-supported-renderers'
  96. )
  97. if (adBlockerWarningInner) {
  98. adBlockerWarningInner.remove()
  99. document.addEventListener('yt-navigate-finish', handleYouTubeNavigateFinish)
  100. replaceCurrentVideo()
  101. }
  102.  
  103. // Video play/pause button.
  104. const playButton = document.querySelector(
  105. 'button.ytp-play-button, button.player-control-play-pause-icon'
  106. )
  107. if (playButton) {
  108. playButton.addEventListener('click', allowPauseVideo)
  109. }
  110. }
  111.  
  112. function queryHasSelector(selector, hasSelector, element = document) {
  113. const el = element.querySelector(selector)
  114. if (el === null) return null
  115. const hasEl = el.querySelector(hasSelector)
  116. if (hasEl === null) return null
  117. return el
  118. }
  119.  
  120. function queryHasSelectorAll(selector, hasSelector, element = document) {
  121. const els = element.querySelectorAll(selector)
  122. const result = []
  123. for (const el of els) {
  124. const hasEl = el.querySelector(hasSelector)
  125. if (hasEl === null) continue
  126. result.push(el)
  127. }
  128. return result
  129. }
  130.  
  131. function getCurrentVideoId() {
  132. const params = new URLSearchParams(location.search)
  133. const videoId = params.get('v')
  134. return videoId
  135. }
  136.  
  137. /**
  138. * Check if the user is focused on the input.
  139. */
  140. function checkEnteringInput() {
  141. if (document.activeElement === null) {
  142. return false
  143. }
  144. return document.activeElement.matches('input, textarea, select')
  145. }
  146.  
  147. /**
  148. * Temporarily allows the video to be paused, for a short period of time.
  149. */
  150. function allowPauseVideo() {
  151. pausedByUser = true
  152. window.clearTimeout(allowPauseVideoTimeoutId)
  153. allowPauseVideoTimeoutId = window.setTimeout(disallowPauseVideo, 500)
  154. }
  155.  
  156. /**
  157. * Pausing the video is not allowed. The purpose is to prevent video from being paused, against the
  158. * behavior of pausing video when YouTube ad blocking warning dialog appears. Unless certain
  159. * conditions, such as pausing by user, etc.
  160. */
  161. function disallowPauseVideo() {
  162. pausedByUser = false
  163. window.clearTimeout(allowPauseVideoTimeoutId)
  164. }
  165.  
  166. function handleWindowBlur() {
  167. isTabBlurred = true
  168. }
  169.  
  170. function handleWindowFocus() {
  171. isTabBlurred = false
  172. }
  173.  
  174. /**
  175. * Handle when video is paused. If certain conditions are not met, it will continue playing.
  176. * Returning early in this function means the video should be paused as it should be.
  177. */
  178. function handleVideoPause() {
  179. if (isYouTubeMusic) return
  180.  
  181. // If it was stopped by the user, it's ok, let the video pause as it should, and exit the function.
  182. if (pausedByUser) {
  183. disallowPauseVideo()
  184. return
  185. }
  186.  
  187. // The video will pause normally if the tab is not focused. This is to allow for pausing the video via the media controller (of the browser or operating system), etc.
  188. // Note: While this also gives YouTube the opportunity to pause videos to annoy users, it's an acceptable trade-off.
  189. if (document.hidden) return
  190. if (isTabBlurred) return
  191.  
  192. if (fineScrubber && fineScrubber.style.display !== 'none') return
  193. if (video === null) return
  194. if (video.duration - video.currentTime < 0.1) return
  195.  
  196. // This is YouTube's disruptive behavior towards users, so the video should continue to play as normal.
  197. video.play()
  198. }
  199.  
  200. function handleVideoTimeUpdate() {
  201. if (hasAd || video === null) return
  202. currentVideoTime = video.currentTime
  203. }
  204.  
  205. /**
  206. * Handle both keyboard press or release events.
  207. */
  208. function handleWindowKeyDownAndKeyUp(event) {
  209. if (isYouTubeMusic) return
  210. if (checkEnteringInput()) return
  211. const code = event.code
  212. if (event.type === 'keydown') {
  213. if (code === 'KeyK' || code === 'MediaPlayPause') {
  214. allowPauseVideo()
  215. }
  216. } else {
  217. if (code === 'Space') {
  218. allowPauseVideo()
  219. }
  220. }
  221. }
  222.  
  223. function handleYouTubeNavigateFinish() {
  224. currentVideoTime = 0
  225. replaceCurrentVideo()
  226. }
  227.  
  228. async function replaceCurrentVideo() {
  229. const start = Math.floor(currentVideoTime)
  230. for (let i = 0; i < 16; i++) {
  231. await waitFor(500)
  232. const videoId = getCurrentVideoId()
  233. const player = document.querySelector('#ytd-player')
  234. if (video && !video.src && videoId && player) {
  235. player.loadVideoWithPlayerVars({ videoId, start })
  236. }
  237. }
  238. }
  239.  
  240. function waitFor(millis) {
  241. return new Promise((resolve) => {
  242. window.setTimeout(resolve, millis)
  243. })
  244. }
  245.  
  246. /**
  247. * Add CSS hides some ad elements on the page.
  248. */
  249. function addCss() {
  250. const hideCssSelector = [
  251. // Ad banner in the upper right corner, above the video playlist.
  252. '#player-ads',
  253.  
  254. // Masthead ad on home page.
  255. '#masthead-ad',
  256.  
  257. 'ytd-ad-slot-renderer',
  258.  
  259. // Ad blocker warning inside the player.
  260. 'yt-playability-error-supported-renderers#error-screen',
  261.  
  262. '.ytp-suggested-action',
  263. '.yt-mealbar-promo-renderer',
  264.  
  265. // YouTube Music Premium trial promotion dialog, bottom left corner.
  266. 'ytmusic-mealbar-promo-renderer',
  267.  
  268. // YouTube Music Premium trial promotion banner on home page.
  269. 'ytmusic-statement-banner-renderer'
  270. ].join(',')
  271. const css = `
  272. #ytd-player { visibility: visible !important }
  273. ${hideCssSelector} { display: none !important }
  274. `
  275. const style = document.createElement('style')
  276. style.textContent = css
  277. document.head.appendChild(style)
  278. }
  279.  
  280. /**
  281. * Remove ad elements using javascript because these selectors require the use of the CSS `:has`
  282. * selector which is not supported in older browser versions.
  283. */
  284. function hideAdElements() {
  285. const adSelectors = [
  286. // Ad banner in the upper right corner, above the video playlist.
  287. ['#panels', 'ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]'],
  288.  
  289. // Temporarily comment out this selector to fix issue [#265124](https://gf.qytechs.cn/en/scripts/498197-auto-skip-youtube-ads/discussions/265124).
  290. // ['#panels', 'ytd-ads-engagement-panel-content-renderer'],
  291.  
  292. // Sponsored ad video items on home page.
  293. ['ytd-rich-item-renderer', '.ytd-ad-slot-renderer'],
  294.  
  295. ['ytd-rich-section-renderer', '.ytd-statement-banner-renderer'],
  296.  
  297. // Ad videos on YouTube Short.
  298. ['ytd-reel-video-renderer', '.ytd-ad-slot-renderer'],
  299.  
  300. // Ad blocker warning dialog.
  301. ['tp-yt-paper-dialog', '#feedback.ytd-enforcement-message-view-model'],
  302.  
  303. // Survey dialog on home page, located at bottom right.
  304. ['tp-yt-paper-dialog', ':scope > ytd-checkbox-survey-renderer'],
  305.  
  306. // Survey to rate suggested content, located at bottom right.
  307. ['tp-yt-paper-dialog', ':scope > ytd-single-option-survey-renderer']
  308. ]
  309. for (const adSelector of adSelectors) {
  310. const adEls = queryHasSelectorAll(adSelector[0], adSelector[1])
  311. for (const adEl of adEls) {
  312. adEl.remove()
  313. }
  314. }
  315. }
  316.  
  317. /**
  318. * Is it YouTube mobile version.
  319. */
  320. const isYouTubeMobile = location.hostname === 'm.youtube.com'
  321.  
  322. /**
  323. * Is the current page YouTube Music.
  324. */
  325. const isYouTubeMusic = location.hostname === 'music.youtube.com'
  326.  
  327. /**
  328. * Current video element.
  329. */
  330. let video = null
  331.  
  332. let fineScrubber = null
  333. let hasAd = false
  334. let currentVideoTime = 0
  335.  
  336. /**
  337. * Is the video paused by the user, not paused by YouTube's ad blocker warning dialog.
  338. */
  339. let pausedByUser = false
  340.  
  341. /**
  342. * Is the current tab blurred.
  343. */
  344. let isTabBlurred = false
  345.  
  346. let allowPauseVideoTimeoutId = 0
  347.  
  348. // Observe DOM changes to detect ads.
  349. if (window.MutationObserver) {
  350. const observer = new MutationObserver(skipAd)
  351. observer.observe(document.body, {
  352. attributes: true,
  353. attributeFilter: ['class', 'src'],
  354. childList: true,
  355. subtree: true
  356. })
  357. }
  358. // If DOM observation is not supported. Detect ads every 500ms (2 times per second).
  359. else {
  360. window.setInterval(skipAd, 500)
  361. }
  362.  
  363. window.addEventListener('blur', handleWindowBlur)
  364. window.addEventListener('focus', handleWindowFocus)
  365. window.addEventListener('keydown', handleWindowKeyDownAndKeyUp)
  366. window.addEventListener('keyup', handleWindowKeyDownAndKeyUp)
  367.  
  368. addCss()
  369. skipAd()

QingJ © 2025

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