自動跳過 YouTube 廣告

幾乎立即自動跳過 YouTube 廣告。刪除廣告攔截器警告彈出視窗。

目前為 2024-11-25 提交的版本,檢視 最新版本

  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 4.8.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://music.youtube.com/*
  30. // @grant GM_getValue
  31. // @grant GM_setValue
  32. // @grant GM_registerMenuCommand
  33. // @license MIT
  34. // @compatible firefox
  35. // @compatible chrome
  36. // @compatible opera
  37. // @compatible safari
  38. // @compatible edge
  39. // @noframes
  40. // @homepage https://github.com/tientq64/userscripts/tree/main/scripts/Auto-Skip-YouTube-Ads
  41. // ==/UserScript==
  42.  
  43. /**
  44. * Skip ads. Remove ad blocker warning.
  45. */
  46. function skipAd() {
  47. video = null
  48. fineScrub = document.querySelector('.ytp-fine-scrubbing')
  49.  
  50. // Check if the current URL is a YouTube Shorts URL and exit the function if true.
  51. if (window.location.pathname.startsWith('/shorts/')) return
  52.  
  53. const player = document.querySelector('#movie_player')
  54.  
  55. if (player) {
  56. hasAd = player.classList.contains('ad-showing')
  57. video = player.querySelector('video.html5-main-video')
  58. }
  59.  
  60. if (hasAd) {
  61. const skipButton = document.querySelector(`
  62. .ytp-skip-ad-button,
  63. .ytp-ad-skip-button,
  64. .ytp-ad-skip-button-modern,
  65. .ytp-ad-survey-answer-button
  66. `)
  67. // Click the skip ad button if available.
  68. if (skipButton) {
  69. skipButton.click()
  70. skipButton.remove()
  71. }
  72. // Otherwise, fast forward to the end of the ad video.
  73. // Use `9999` instead of `video.duration` to avoid errors when `duration`
  74. // 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('mouseup', allowPauseVideo)
  83. video.addEventListener('timeupdate', handleVideoTimeUpdate)
  84. }
  85.  
  86. // Remove ad blocker warning dialog if it appears.
  87. const adBlockerWarningDialog = document.querySelector(
  88. 'tp-yt-paper-dialog:has(#feedback.ytd-enforcement-message-view-model)'
  89. )
  90. if (adBlockerWarningDialog) {
  91. adBlockerWarningDialog.remove()
  92. }
  93.  
  94. // Handle when ad blocker warning appears inside video player.
  95. // Currently there is NO WAY TO REMOVE it.
  96. // Temporary workaround is to reload the page.
  97. const adBlockerWarningInner = document.querySelector(
  98. '.yt-playability-error-supported-renderers:has(.ytd-enforcement-message-view-model)'
  99. )
  100. if (adBlockerWarningInner) {
  101. if (checkCanReloadPage()) {
  102. adBlockerWarningInner.remove()
  103. const params = new URLSearchParams(location.search)
  104. params.set('t', Math.floor(currentVideoTime) + 's')
  105. const newUrl = location.origin + location.pathname + '?' + params.toString()
  106. location.replace(newUrl)
  107. }
  108. }
  109.  
  110. // Video pause button.
  111. const playButton = document.querySelector('button.ytp-play-button')
  112. if (playButton) {
  113. playButton.addEventListener('click', allowPauseVideo)
  114. }
  115.  
  116. // Remove short video ads.
  117. // Can't just use CSS to hide it, because it will cause problems when scrolling
  118. // to the next video.
  119. const adShortVideos = document.querySelectorAll(
  120. 'ytd-reel-video-renderer:has(.ytd-ad-slot-renderer)'
  121. )
  122. for (const adShortVideo of adShortVideos) {
  123. adShortVideo.remove()
  124. }
  125. }
  126.  
  127. /**
  128. * Check if the user is focused on the input.
  129. */
  130. function checkEnteringInput() {
  131. if (document.activeElement === null) {
  132. return false
  133. }
  134. return document.activeElement.matches('input, textarea, select')
  135. }
  136.  
  137. /**
  138. * Check if the page can be reloaded.
  139. */
  140. function checkCanReloadPage() {
  141. if (!config.allowedReloadPage) {
  142. return false
  143. }
  144. if (!config.dontReloadWhileBusy) {
  145. return true
  146. }
  147. if (checkEnteringInput()) {
  148. return false
  149. }
  150. // Do not reload while the user is reading comments.
  151. if (document.documentElement.scrollTop > 200) {
  152. return false
  153. }
  154. return true
  155. }
  156.  
  157. /**
  158. * Temporarily allows the video to be paused, for a short period of time.
  159. */
  160. function allowPauseVideo() {
  161. pausedByUser = true
  162. window.clearTimeout(allowPauseVideoTimeoutId)
  163. allowPauseVideoTimeoutId = window.setTimeout(disallowPauseVideo, 500)
  164. }
  165.  
  166. /**
  167. * Pausing the video is not allowed.
  168. * The purpose is to prevent video from being paused, against the behavior of pausing
  169. * video when YouTube ad blocking warning dialog appears. Unless certain conditions,
  170. * such as pausing by user, etc.
  171. */
  172. function disallowPauseVideo() {
  173. pausedByUser = false
  174. window.clearTimeout(allowPauseVideoTimeoutId)
  175. }
  176.  
  177. function handleGlobalBlur() {
  178. isTabBlurred = true
  179. }
  180.  
  181. function handleGlobalFocus() {
  182. isTabBlurred = false
  183. }
  184.  
  185. /**
  186. * Handle when video is paused.
  187. * If certain conditions are not met, it will continue playing.
  188. */
  189. function handleVideoPause() {
  190. if (isYouTubeMusic) return
  191. if (pausedByUser) {
  192. disallowPauseVideo()
  193. return
  194. }
  195. if (document.hidden) return
  196. if (isTabBlurred) return
  197. if (fineScrub && fineScrub.style.display !== 'none') return
  198. if (video === null) return
  199. if (video.duration - video.currentTime < 0.1) return
  200. video.play()
  201. }
  202.  
  203. function handleVideoTimeUpdate() {
  204. if (hasAd || video === null) return
  205. currentVideoTime = video.currentTime
  206. }
  207.  
  208. /**
  209. * Handle both keyboard press or release events.
  210. */
  211. function handleGlobalKeyDownAndKeyUp(event) {
  212. if (isYouTubeMusic) return
  213. if (checkEnteringInput()) return
  214. const code = event.code
  215. if (event.type === 'keydown') {
  216. if (code === 'KeyK' || code === 'MediaPlayPause') {
  217. allowPauseVideo()
  218. }
  219. } else {
  220. if (code === 'Space') {
  221. allowPauseVideo()
  222. }
  223. }
  224. }
  225.  
  226. /**
  227. * Save current configuration.
  228. */
  229. function saveConfig() {
  230. GM_setValue('config', config)
  231. }
  232.  
  233. /**
  234. * Register menu commands, or update the menu.
  235. */
  236. function registerMenuCommands() {
  237. {
  238. const status = config.allowedReloadPage ? 'Yes' : 'No'
  239. GM_registerMenuCommand(
  240. `Reload page if ad cannot be skipped: ${status}`,
  241. () => {
  242. config.allowedReloadPage = !config.allowedReloadPage
  243. saveConfig()
  244. updateMenuCommands()
  245. },
  246. {
  247. id: 0,
  248. autoClose: false
  249. }
  250. )
  251. }
  252. {
  253. const status = config.dontReloadWhileBusy ? 'Yes' : 'No'
  254. GM_registerMenuCommand(
  255. `Don't reload while the user is busy: ${status}`,
  256. () => {
  257. config.dontReloadWhileBusy = !config.dontReloadWhileBusy
  258. saveConfig()
  259. updateMenuCommands()
  260. },
  261. {
  262. id: 1,
  263. autoClose: false
  264. }
  265. )
  266. }
  267. }
  268.  
  269. /**
  270. * Update menu commands.
  271. * @alias registerMenuCommands
  272. */
  273. function updateMenuCommands() {
  274. registerMenuCommands()
  275. }
  276.  
  277. /**
  278. * Add CSS hides some ad elements on the page.
  279. */
  280. function addCSSHideAds() {
  281. const selectors = [
  282. // Ad banner in the upper right corner, above the video playlist.
  283. '#player-ads',
  284. '#panels:has(ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"])',
  285. //
  286. '#masthead-ad',
  287. // Temporarily comment out this selector to fix issue [#265124](https://gf.qytechs.cn/en/scripts/498197-auto-skip-youtube-ads/discussions/265124).
  288. // '#panels:has(ytd-ads-engagement-panel-content-renderer)',
  289. 'ytd-ad-slot-renderer',
  290. // Sponsored ad video items on home page.
  291. 'ytd-rich-item-renderer:has(.ytd-ad-slot-renderer)',
  292. //
  293. 'ytd-rich-section-renderer:has(.ytd-statement-banner-renderer)',
  294. // Ad videos on YouTube Short.
  295. 'ytd-reel-video-renderer:has(.ytd-ad-slot-renderer)',
  296. // Ad blocker warning dialog.
  297. 'tp-yt-paper-dialog:has(#feedback.ytd-enforcement-message-view-model)',
  298. //
  299. '.ytp-suggested-action',
  300. '.yt-mealbar-promo-renderer',
  301. // YouTube Music Premium trial promotion dialog, bottom left corner.
  302. 'ytmusic-mealbar-promo-renderer',
  303. // YouTube Music Premium trial promotion banner on home page.
  304. 'ytmusic-statement-banner-renderer'
  305. ]
  306. const css = `${selectors.join(',')}{display:none!important}`
  307. const style = document.createElement('style')
  308. style.textContent = css
  309. document.head.appendChild(style)
  310. }
  311.  
  312. /**
  313. * Default configuration.
  314. */
  315. const defaultConfig = {
  316. allowedReloadPage: true,
  317. dontReloadWhileBusy: true
  318. }
  319.  
  320. // Load configuration.
  321. const config = GM_getValue('config', defaultConfig)
  322. for (const key in defaultConfig) {
  323. if (config[key] == null) {
  324. config[key] = defaultConfig[key]
  325. }
  326. }
  327.  
  328. /**
  329. * Is the current page YouTube Music.
  330. */
  331. const isYouTubeMusic = location.hostname === 'music.youtube.com'
  332. /**
  333. * Current video element.
  334. */
  335. let video = null
  336. let fineScrub = null
  337. let hasAd = false
  338. let currentVideoTime = 0
  339. /**
  340. * Is the video paused by the user, not paused by YouTube's ad blocker warning dialog.
  341. */
  342. let pausedByUser = false
  343. /**
  344. * Is the current tab blurred.
  345. */
  346. let isTabBlurred = false
  347. let allowPauseVideoTimeoutId = 0
  348.  
  349. // Observe DOM changes to detect ads.
  350. if (window.MutationObserver) {
  351. const observer = new MutationObserver(skipAd)
  352. observer.observe(document.body, {
  353. attributes: true,
  354. attributeFilter: ['class', 'src'],
  355. childList: true,
  356. subtree: true
  357. })
  358. }
  359. // If DOM observation is not supported. Detect ads every 500ms (2 times per second).
  360. else {
  361. window.setInterval(skipAd, 500)
  362. }
  363.  
  364. window.addEventListener('blur', handleGlobalBlur)
  365. window.addEventListener('focus', handleGlobalFocus)
  366. window.addEventListener('keydown', handleGlobalKeyDownAndKeyUp)
  367. window.addEventListener('keyup', handleGlobalKeyDownAndKeyUp)
  368.  
  369. addCSSHideAds()
  370. registerMenuCommands()
  371. skipAd()

QingJ © 2025

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