Greasy Fork is available in English.

Auto Skip YouTube Ads

Automatically skip YouTube ads almost instantly. Remove the ad blocker warning pop-up.

Встановити цей скрипт?
Скрипт запропонований Автором

Вам також може сподобатись YouTube Shorts To Normal Video.

Встановити цей скрипт
  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.1.3
  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://music.youtube.com/*
  30. // @grant none
  31. // @license MIT
  32. // @compatible firefox
  33. // @compatible chrome
  34. // @compatible opera
  35. // @compatible safari
  36. // @compatible edge
  37. // @noframes
  38. // @homepage https://github.com/tientq64/userscripts/tree/main/scripts/Auto-Skip-YouTube-Ads
  39. // ==/UserScript==
  40.  
  41. /**
  42. * Skip ads. Remove ad blocker warning.
  43. */
  44. function skipAd() {
  45. video = null
  46. fineScrubber = document.querySelector('.ytp-fine-scrubbing')
  47.  
  48. // Check if the current URL is a YouTube Shorts URL and exit the function if true.
  49. if (window.location.pathname.startsWith('/shorts/')) return
  50.  
  51. const moviePlayer = document.querySelector('#movie_player')
  52.  
  53. if (moviePlayer) {
  54. hasAd = moviePlayer.classList.contains('ad-showing')
  55. video = moviePlayer.querySelector('video.html5-main-video')
  56. }
  57.  
  58. if (hasAd) {
  59. const skipButton = document.querySelector(`
  60. .ytp-skip-ad-button,
  61. .ytp-ad-skip-button,
  62. .ytp-ad-skip-button-modern,
  63. .ytp-ad-survey-answer-button
  64. `)
  65. // Click the skip ad button if available.
  66. if (skipButton) {
  67. skipButton.click()
  68. skipButton.remove()
  69. }
  70. // Otherwise, fast forward to the end of the ad video.
  71. // Use `9999` instead of `video.duration` to avoid errors when `duration` is not a number.
  72. else if (video && video.src) {
  73. video.currentTime = 9999
  74. }
  75. }
  76.  
  77. if (video) {
  78. video.addEventListener('pause', handleVideoPause)
  79. video.addEventListener('pointerup', allowPauseVideo)
  80. video.addEventListener('timeupdate', handleVideoTimeUpdate)
  81. }
  82.  
  83. // Pie countdown ad.
  84. const pieCountdown = document.querySelector('.ytp-ad-timed-pie-countdown-container')
  85. if (pieCountdown) {
  86. pieCountdown.remove()
  87. replaceCurrentVideo()
  88. }
  89.  
  90. // Remove ad blocker warning dialog if it appears.
  91. const adBlockerWarningDialog = document.querySelector(
  92. 'tp-yt-paper-dialog:has(#feedback.ytd-enforcement-message-view-model)'
  93. )
  94. if (adBlockerWarningDialog) {
  95. adBlockerWarningDialog.remove()
  96. }
  97.  
  98. // Handle when ad blocker warning appears inside video player.
  99. const adBlockerWarningInner = document.querySelector(
  100. '.yt-playability-error-supported-renderers:has(.ytd-enforcement-message-view-model)'
  101. )
  102. if (adBlockerWarningInner) {
  103. adBlockerWarningInner.remove()
  104. document.addEventListener('yt-navigate-finish', handleYouTubeNavigateFinish)
  105. replaceCurrentVideo()
  106. }
  107.  
  108. // Video pause button.
  109. const playButton = document.querySelector('button.ytp-play-button')
  110. if (playButton) {
  111. playButton.addEventListener('click', allowPauseVideo)
  112. }
  113.  
  114. // Remove short video ads.
  115. // Note: Do this because can't just use CSS to hide it, as it will cause problems when scrolling to the next video.
  116. const adShortVideos = document.querySelectorAll(
  117. 'ytd-reel-video-renderer:has(.ytd-ad-slot-renderer)'
  118. )
  119. for (const adShortVideo of adShortVideos) {
  120. adShortVideo.remove()
  121. }
  122. }
  123.  
  124. function getCurrentVideoId() {
  125. const params = new URLSearchParams(location.search)
  126. const videoId = params.get('v')
  127. return videoId
  128. }
  129.  
  130. /**
  131. * Check if the user is focused on the input.
  132. */
  133. function checkEnteringInput() {
  134. if (document.activeElement === null) {
  135. return false
  136. }
  137. return document.activeElement.matches('input, textarea, select')
  138. }
  139.  
  140. /**
  141. * Temporarily allows the video to be paused, for a short period of time.
  142. */
  143. function allowPauseVideo() {
  144. pausedByUser = true
  145. window.clearTimeout(allowPauseVideoTimeoutId)
  146. allowPauseVideoTimeoutId = window.setTimeout(disallowPauseVideo, 500)
  147. }
  148.  
  149. /**
  150. * Pausing the video is not allowed. The purpose is to prevent video from being paused, against the
  151. * behavior of pausing video when YouTube ad blocking warning dialog appears. Unless certain
  152. * conditions, such as pausing by user, etc.
  153. */
  154. function disallowPauseVideo() {
  155. pausedByUser = false
  156. window.clearTimeout(allowPauseVideoTimeoutId)
  157. }
  158.  
  159. function handleWindowBlur() {
  160. isTabBlurred = true
  161. }
  162.  
  163. function handleWindowFocus() {
  164. isTabBlurred = false
  165. }
  166.  
  167. /**
  168. * Handle when video is paused. If certain conditions are not met, it will continue playing.
  169. * Returning early in this function means the video should be paused as it should be.
  170. */
  171. function handleVideoPause() {
  172. if (isYouTubeMusic) return
  173.  
  174. // If it was stopped by the user, it's ok, let the video pause as it should, and exit the function.
  175. if (pausedByUser) {
  176. disallowPauseVideo()
  177. return
  178. }
  179.  
  180. // 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.
  181. // Note: While this also gives YouTube the opportunity to pause videos to annoy users, it's an acceptable trade-off.
  182. if (document.hidden) return
  183. if (isTabBlurred) return
  184.  
  185. if (fineScrubber && fineScrubber.style.display !== 'none') return
  186. if (video === null) return
  187. if (video.duration - video.currentTime < 0.1) return
  188.  
  189. // This is YouTube's disruptive behavior towards users, so the video should continue to play as normal.
  190. video.play()
  191. }
  192.  
  193. function handleVideoTimeUpdate() {
  194. if (hasAd || video === null) return
  195. currentVideoTime = video.currentTime
  196. }
  197.  
  198. /**
  199. * Handle both keyboard press or release events.
  200. */
  201. function handleWindowKeyDownAndKeyUp(event) {
  202. if (isYouTubeMusic) return
  203. if (checkEnteringInput()) return
  204. const code = event.code
  205. if (event.type === 'keydown') {
  206. if (code === 'KeyK' || code === 'MediaPlayPause') {
  207. allowPauseVideo()
  208. }
  209. } else {
  210. if (code === 'Space') {
  211. allowPauseVideo()
  212. }
  213. }
  214. }
  215.  
  216. function handleYouTubeNavigateFinish() {
  217. currentVideoTime = 0
  218. replaceCurrentVideo()
  219. }
  220.  
  221. async function replaceCurrentVideo() {
  222. const start = Math.floor(currentVideoTime)
  223. for (let i = 0; i < 8; i++) {
  224. await waitFor(500)
  225. const videoId = getCurrentVideoId()
  226. const player = document.querySelector('#ytd-player')
  227. if (video && !video.src && videoId && player) {
  228. player.loadVideoWithPlayerVars({ videoId, start })
  229. }
  230. }
  231. }
  232.  
  233. function waitFor(millis) {
  234. return new Promise((resolve) => {
  235. window.setTimeout(resolve, millis)
  236. })
  237. }
  238.  
  239. /**
  240. * Add CSS hides some ad elements on the page.
  241. */
  242. function addCss() {
  243. const hideCssSelector = [
  244. // Ad banner in the upper right corner, above the video playlist.
  245. '#player-ads',
  246. '#panels:has(ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"])',
  247.  
  248. // Masthead ad on home page.
  249. '#masthead-ad',
  250.  
  251. // Temporarily comment out this selector to fix issue [#265124](https://greasyfork.org/en/scripts/498197-auto-skip-youtube-ads/discussions/265124).
  252. // '#panels:has(ytd-ads-engagement-panel-content-renderer)',
  253.  
  254. 'ytd-ad-slot-renderer',
  255.  
  256. // Sponsored ad video items on home page.
  257. 'ytd-rich-item-renderer:has(.ytd-ad-slot-renderer)',
  258.  
  259. 'ytd-rich-section-renderer:has(.ytd-statement-banner-renderer)',
  260.  
  261. // Ad videos on YouTube Short.
  262. 'ytd-reel-video-renderer:has(.ytd-ad-slot-renderer)',
  263.  
  264. // Ad blocker warning dialog.
  265. 'tp-yt-paper-dialog:has(#feedback.ytd-enforcement-message-view-model)',
  266.  
  267. // Ad blocker warning inside the player.
  268. 'yt-playability-error-supported-renderers#error-screen',
  269.  
  270. // Survey dialog on home page, located at bottom right.
  271. 'tp-yt-paper-dialog:has(> ytd-checkbox-survey-renderer)',
  272.  
  273. // Survey to rate suggested content, located at bottom right.
  274. 'tp-yt-paper-dialog:has(> ytd-single-option-survey-renderer)',
  275.  
  276. '.ytp-suggested-action',
  277. '.yt-mealbar-promo-renderer',
  278.  
  279. // YouTube Music Premium trial promotion dialog, bottom left corner.
  280. 'ytmusic-mealbar-promo-renderer',
  281.  
  282. // YouTube Music Premium trial promotion banner on home page.
  283. 'ytmusic-statement-banner-renderer'
  284. ].join(',')
  285. const css = `
  286. #ytd-player { visibility: visible !important }
  287. ${hideCssSelector} { display: none !important }
  288. `
  289. const style = document.createElement('style')
  290. style.textContent = css
  291. document.head.appendChild(style)
  292. }
  293.  
  294. /**
  295. * Is the current page YouTube Music.
  296. */
  297. const isYouTubeMusic = location.hostname === 'music.youtube.com'
  298.  
  299. /**
  300. * Current video element.
  301. */
  302. let video = null
  303.  
  304. let fineScrubber = null
  305. let hasAd = false
  306. let currentVideoTime = 0
  307.  
  308. /**
  309. * Is the video paused by the user, not paused by YouTube's ad blocker warning dialog.
  310. */
  311. let pausedByUser = false
  312.  
  313. /**
  314. * Is the current tab blurred.
  315. */
  316. let isTabBlurred = false
  317.  
  318. let allowPauseVideoTimeoutId = 0
  319.  
  320. // Observe DOM changes to detect ads.
  321. if (window.MutationObserver) {
  322. const observer = new MutationObserver(skipAd)
  323. observer.observe(document.body, {
  324. attributes: true,
  325. attributeFilter: ['class', 'src'],
  326. childList: true,
  327. subtree: true
  328. })
  329. }
  330. // If DOM observation is not supported. Detect ads every 500ms (2 times per second).
  331. else {
  332. window.setInterval(skipAd, 500)
  333. }
  334.  
  335. window.addEventListener('blur', handleWindowBlur)
  336. window.addEventListener('focus', handleWindowFocus)
  337. window.addEventListener('keydown', handleWindowKeyDownAndKeyUp)
  338. window.addEventListener('keyup', handleWindowKeyDownAndKeyUp)
  339.  
  340. addCss()
  341. skipAd()