From ee1063d9b642f594ed900379352e11196b41408b Mon Sep 17 00:00:00 2001 From: tientq64 Date: Sun, 16 Feb 2025 00:36:29 +0700 Subject: [PATCH 1/2] Version 6 had many bug reports, reverting to the previous version 5 (#6). --- .resources/global.d.ts | 4 + scripts/Auto-Skip-YouTube-Ads/README.md | 378 ++++++++++--------- scripts/Auto-Skip-YouTube-Ads/script.user.js | 352 +++++++++++++---- scripts/Auto-Skip-YouTube-Ads/script.user.ts | 376 +++++++++++++----- 4 files changed, 757 insertions(+), 353 deletions(-) diff --git a/.resources/global.d.ts b/.resources/global.d.ts index 8b51306..f3fbb53 100644 --- a/.resources/global.d.ts +++ b/.resources/global.d.ts @@ -53,5 +53,9 @@ interface YtdPlayerElement extends HTMLElement { loadVideoWithPlayerVars(options: { videoId: string; start?: number }): void } +interface YouTubeMoviePlayerElement extends HTMLElement { + loadVideoByPlayerVars(options: { videoId: string; start?: number }): void +} + declare type TrustedTypePolicyFactory = import('trusted-types/lib').TrustedTypePolicyFactory declare const trustedTypes: TrustedTypePolicyFactory diff --git a/scripts/Auto-Skip-YouTube-Ads/README.md b/scripts/Auto-Skip-YouTube-Ads/README.md index bbb4e43..27aa020 100644 --- a/scripts/Auto-Skip-YouTube-Ads/README.md +++ b/scripts/Auto-Skip-YouTube-Ads/README.md @@ -1,186 +1,192 @@ -## 📰 Introduction - -Automatically skip YouTube ads instantly. Undetected by YouTube ad blocker warnings. - -立即自动跳过 YouTube 广告。不会被 YouTube 广告拦截器警告检测到。 - -Tự động bỏ qua quảng cáo YouTube ngay lập tức. Không bị phát hiện bởi cảnh báo trình chặn quảng cáo của YouTube. - -## 📑 Changelog - -### 6.0.2 - 2025-02-02 - -- Disable some unnecessary CSS. - -### 6.0.0 - 2025-01-29 - -_Happy Lunar New Year!_ - -- Completely rewritten way to skip ads, more efficient, not detected by YouTube ad blocker warning. -- New way to skip ads is temporarily not working on YouTube Music. - -### 5.3.0 - 2025-01-23 - -- Supports older browser versions. - -### 5.2.0 - 2025-01-21 - -- Support for **YouTube mobile** version 🎉 -- Revisit fix for issue [#2]: Fully resolved the problem where videos couldn't be paused on mobile. The previous fix was incomplete. - -### 5.1.3 - 2025-01-18 - -- Fix ad skipping issue. - -### 5.1.2 - 2025-01-17 - -- Fix issue [#2] where video can't be paused on mobile. - -### 5.1.1 - 2024-12-27 - -- Hide the survey to rate suggested content, located at bottom right. - -### 5.1.0 - 2024-12-26 - -- Skip pie countdown ads 🎉 - -### 5.0.0 - 2024-12-25 - -_Merry Christmas!_ - -- **No need to reload the page** when there is no way to skip the ad anymore 🤯 -- Configuration removed, no longer needed. - -### 4.8.2 - 2024-12-21 - -- Fix timestamp loss when reloading. - -### 4.8.1 - 2024-12-03 - -- Hide survey dialog on home page. - -### 4.8.0 - 2024-11-26 - -- The current video's timestamp will be preserved when the page is reloaded ([#267857]). - -### 4.7.4 - 2024-11-20 - -- Improved ad skipping. - -### 4.7.0 - 2024-10-26 - -- Add option "Don't reload while the user is busy" in Tampermonkey's menu to avoid reloading page when user is busy doing something, like reading comments, entering text. Enabled by default. - -### 4.6.2 - 2024-10-13 - -- Improve hiding of ad banners. - -### 4.6.0 - 2024-10-07 - -- Support skipping ads on **YouTube Music** (PR [#1]). - -### 4.5.2 - 2024-09-30 - -- Fix Shorts reload infinitely ([#258626], [#259545], [#261679]). - -### 4.5.0 - 2024-09-26 - -- Add option to enable/disable "Reload the page when there is no other way to skip ads" feature in Tampermonkey's menu. Enabled by default.\ - ![Screenshot-001] - -### 4.4.0 - 2024-08-30 - -- Automatically reload web page when ad blocker warnings appear. - -### 4.3.13 - 2024-08-26 - -- Fixed bug where video could not be paused using pause/play key on keyboard or media controls ([#257424]). -- Improve the performance. - -### 4.3.9 - 2024-08-21 - -- Fix `@match` invalid syntax ([#256841]). - -### 4.3.8 - 2024-08-20 - -- Fix the issue of removing ad videos in Shorts. - -### 4.3.6 - 2024-08-07 - -- Fix bug where video rewinds a segment after skipping an ad ([#254113]). - -### 4.3.4 - 2024-08-02 - -- Improve the performance. - -### 4.2.1 - 2024-07-30 - -- Fixed video automatically replay when ended. - -### 4.2.0 - 2024-07-30 - -- Videos will now no longer occasionally pause due to ad blocker use. -- Faster ad video skipping speed. - -### 4.1.0 - 2024-07-10 - -- No need to reload the page when the ad blocker warning dialog appears. - -### 4.0.0 - 2024-07-09 - -- The page will now reload if an ad blocker warning dialog appears. Because YouTube now pauses the video at first if an ad blocker is detected. -- Write to the Console every time skip an ad video, etc. Purpose to help debug. To open the Console, press `Ctrl+Shift+J`. - -### 3.1.2 - 2024-07-06 - -- Playing video after clicking dismiss the ad blocker warning popup. - -### 3.1.1 - 2024-07-04 - -- Add a few CSS that hides the ads. - -### 3.1.0 - 2024-07-02 - -- Skip ads faster when the tab is active. -- Fixed bug when set time to end of ad video without the video duration being available. -- Change icon. - -### 3.0.2 - 2024-06-28 - -- Rewriting to only use `setInterval` simplifies things, and fix some bugs. - -### 2.1.3 - 2024-06-21 - -- Fix `popupContainer` not found error. - -### 2.1.0 - 2024-06-20 - -- Auto close YouTube's ad blocker warning popup. - -### 2.0.1 - 2024-06-19 - -- Improved skip ad button detection. -- Fall back to `setInterval` when `MutationObserver` is not supported. - -### 2.0.0 - 2024-06-18 - -- Rewrite the entire code, use `MutationObserver` instead of `setInterval`. - -### 1.0.0 - 2024-06-17 - -- Stable release. - -## 💳 Credits - -Youtube icons created by Ruslan Babkin - Flaticon. - -[#2]: https://github.com/tientq64/userscripts/issues/2 -[#267857]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/267857 -[#258626]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/258626 -[#259545]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/259545 -[#261679]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/261679 -[#257424]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/257424 -[#256841]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/256841 -[#254113]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/254113 -[#1]: https://github.com/tientq64/userscripts/pull/1 -[Screenshot-001]: https://cdn.jsdelivr.net/gh/tientq64/userscripts/scripts/Auto-Skip-YouTube-Ads/assets/screenshot-001.png +## 📰 Introduction + +Automatically skip YouTube ads instantly. Undetected by YouTube ad blocker warnings. + +立即自动跳过 YouTube 广告。不会被 YouTube 广告拦截器警告检测到。 + +Tự động bỏ qua quảng cáo YouTube ngay lập tức. Không bị phát hiện bởi cảnh báo trình chặn quảng cáo của YouTube. + +## 📑 Changelog + +### 7.0.0 - 2025-02-15 + +- Version 6 had many bug reports, reverting to the previous version 5 ([#6], [#279168]). + +### 6.0.2 - 2025-02-02 + +- Disable some unnecessary CSS. + +### 6.0.0 - 2025-01-29 + +_Happy Lunar New Year!_ + +- Completely rewritten way to skip ads, more efficient, not detected by YouTube ad blocker warning. +- New way to skip ads is temporarily not working on YouTube Music. + +### 5.3.0 - 2025-01-23 + +- Supports older browser versions. + +### 5.2.0 - 2025-01-21 + +- Support for **YouTube mobile** version 🎉 +- Revisit fix for issue [#2]: Fully resolved the problem where videos couldn't be paused on mobile. The previous fix was incomplete. + +### 5.1.3 - 2025-01-18 + +- Fix ad skipping issue. + +### 5.1.2 - 2025-01-17 + +- Fix issue [#2] where video can't be paused on mobile. + +### 5.1.1 - 2024-12-27 + +- Hide the survey to rate suggested content, located at bottom right. + +### 5.1.0 - 2024-12-26 + +- Skip pie countdown ads 🎉 + +### 5.0.0 - 2024-12-25 + +_Merry Christmas!_ + +- **No need to reload the page** when there is no way to skip the ad anymore 🤯 +- Configuration removed, no longer needed. + +### 4.8.2 - 2024-12-21 + +- Fix timestamp loss when reloading. + +### 4.8.1 - 2024-12-03 + +- Hide survey dialog on home page. + +### 4.8.0 - 2024-11-26 + +- The current video's timestamp will be preserved when the page is reloaded ([#267857]). + +### 4.7.4 - 2024-11-20 + +- Improved ad skipping. + +### 4.7.0 - 2024-10-26 + +- Add option "Don't reload while the user is busy" in Tampermonkey's menu to avoid reloading page when user is busy doing something, like reading comments, entering text. Enabled by default. + +### 4.6.2 - 2024-10-13 + +- Improve hiding of ad banners. + +### 4.6.0 - 2024-10-07 + +- Support skipping ads on **YouTube Music** (PR [#1]). + +### 4.5.2 - 2024-09-30 + +- Fix Shorts reload infinitely ([#258626], [#259545], [#261679]). + +### 4.5.0 - 2024-09-26 + +- Add option to enable/disable "Reload the page when there is no other way to skip ads" feature in Tampermonkey's menu. Enabled by default.\ + ![Screenshot-001] + +### 4.4.0 - 2024-08-30 + +- Automatically reload web page when ad blocker warnings appear. + +### 4.3.13 - 2024-08-26 + +- Fixed bug where video could not be paused using pause/play key on keyboard or media controls ([#257424]). +- Improve the performance. + +### 4.3.9 - 2024-08-21 + +- Fix `@match` invalid syntax ([#256841]). + +### 4.3.8 - 2024-08-20 + +- Fix the issue of removing ad videos in Shorts. + +### 4.3.6 - 2024-08-07 + +- Fix bug where video rewinds a segment after skipping an ad ([#254113]). + +### 4.3.4 - 2024-08-02 + +- Improve the performance. + +### 4.2.1 - 2024-07-30 + +- Fixed video automatically replay when ended. + +### 4.2.0 - 2024-07-30 + +- Videos will now no longer occasionally pause due to ad blocker use. +- Faster ad video skipping speed. + +### 4.1.0 - 2024-07-10 + +- No need to reload the page when the ad blocker warning dialog appears. + +### 4.0.0 - 2024-07-09 + +- The page will now reload if an ad blocker warning dialog appears. Because YouTube now pauses the video at first if an ad blocker is detected. +- Write to the Console every time skip an ad video, etc. Purpose to help debug. To open the Console, press `Ctrl+Shift+J`. + +### 3.1.2 - 2024-07-06 + +- Playing video after clicking dismiss the ad blocker warning popup. + +### 3.1.1 - 2024-07-04 + +- Add a few CSS that hides the ads. + +### 3.1.0 - 2024-07-02 + +- Skip ads faster when the tab is active. +- Fixed bug when set time to end of ad video without the video duration being available. +- Change icon. + +### 3.0.2 - 2024-06-28 + +- Rewriting to only use `setInterval` simplifies things, and fix some bugs. + +### 2.1.3 - 2024-06-21 + +- Fix `popupContainer` not found error. + +### 2.1.0 - 2024-06-20 + +- Auto close YouTube's ad blocker warning popup. + +### 2.0.1 - 2024-06-19 + +- Improved skip ad button detection. +- Fall back to `setInterval` when `MutationObserver` is not supported. + +### 2.0.0 - 2024-06-18 + +- Rewrite the entire code, use `MutationObserver` instead of `setInterval`. + +### 1.0.0 - 2024-06-17 + +- Stable release. + +## 💳 Credits + +Youtube icons created by Ruslan Babkin - Flaticon. + +[#6]: https://github.com/tientq64/userscripts/issues/6 +[#2]: https://github.com/tientq64/userscripts/issues/2 +[#1]: https://github.com/tientq64/userscripts/pull/1 +[#279168]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/279168 +[#267857]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/267857 +[#261679]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/261679 +[#259545]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/259545 +[#258626]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/258626 +[#257424]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/257424 +[#256841]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/256841 +[#254113]: https://greasyfork.org/scripts/498197-auto-skip-youtube-ads/discussions/254113 +[Screenshot-001]: https://cdn.jsdelivr.net/gh/tientq64/userscripts/scripts/Auto-Skip-YouTube-Ads/assets/screenshot-001.png diff --git a/scripts/Auto-Skip-YouTube-Ads/script.user.js b/scripts/Auto-Skip-YouTube-Ads/script.user.js index b47c3de..feebd61 100644 --- a/scripts/Auto-Skip-YouTube-Ads/script.user.js +++ b/scripts/Auto-Skip-YouTube-Ads/script.user.js @@ -1,38 +1,35 @@ // ==UserScript== // @name Auto Skip YouTube Ads -// @name:ar تخطي إعلانات YouTube تلقائيًا +// @name:ar التخطي التلقائي لإعلانات YouTube // @name:es Saltar Automáticamente Anuncios De YouTube -// @name:fr Ignorer Automatiquement Les Publicités YouTube // @name:hi YouTube विज्ञापन स्वचालित रूप से छोड़ें // @name:id Lewati Otomatis Iklan YouTube // @name:ja YouTube 広告を自動スキップ // @name:ko YouTube 광고 자동 건너뛰기 -// @name:nl YouTube-Advertenties Automatisch Overslaan // @name:pt-BR Pular Automaticamente Anúncios Do YouTube // @name:ru Автоматический Пропуск Рекламы На YouTube // @name:vi Tự Động Bỏ Qua Quảng Cáo YouTube // @name:zh-CN 自动跳过 YouTube 广告 // @name:zh-TW 自動跳過 YouTube 廣告 // @namespace https://github.com/tientq64/userscripts -// @version 6.0.3 -// @description Automatically skip YouTube ads instantly. Undetected by YouTube ad blocker warnings. -// @description:ar تخطي إعلانات YouTube تلقائيًا على الفور. دون أن يتم اكتشاف ذلك من خلال تحذيرات أداة حظر الإعلانات في YouTube. -// @description:es Omite automáticamente los anuncios de YouTube al instante. Sin que te detecten las advertencias del bloqueador de anuncios de YouTube. -// @description:fr Ignorez automatiquement et instantanément les publicités YouTube. Non détecté par les avertissements du bloqueur de publicités YouTube. -// @description:hi YouTube विज्ञापनों को स्वचालित रूप से तुरंत छोड़ दें। YouTube विज्ञापन अवरोधक चेतावनियों द्वारा पता नहीं लगाया गया। -// @description:id Lewati iklan YouTube secara otomatis secara instan. Tidak terdeteksi oleh peringatan pemblokir iklan YouTube. -// @description:ja YouTube 広告を即座に自動的にスキップします。YouTube 広告ブロッカーの警告には検出されません。 -// @description:ko YouTube 광고를 즉시 자동으로 건너뜁니다. YouTube 광고 차단 경고에 감지되지 않습니다. -// @description:nl Sla YouTube-advertenties direct automatisch over. Ongemerkt door YouTube-adblockerwaarschuwingen. -// @description:pt-BR Pule anúncios do YouTube instantaneamente. Não detectado pelos avisos do bloqueador de anúncios do YouTube. -// @description:ru Автоматически пропускать рекламу YouTube мгновенно. Не обнаруживается предупреждениями блокировщиков рекламы YouTube. -// @description:vi Tự động bỏ qua quảng cáo YouTube ngay lập tức. Không bị phát hiện bởi cảnh báo trình chặn quảng cáo của YouTube. -// @description:zh-CN 立即自动跳过 YouTube 广告。不会被 YouTube 广告拦截器警告检测到。 -// @description:zh-TW 立即自動跳過 YouTube 廣告。 YouTube 廣告攔截器警告未被偵測到。 +// @version 7.0.0 +// @description Automatically skip YouTube ads almost instantly. Remove the ad blocker warning pop-up. +// @description:ar تخطي إعلانات YouTube تلقائيًا في الحال. إزالة النافذة المنبثقة لتحذير مانع الإعلانات. +// @description:es Omite automáticamente los anuncios de YouTube casi al instante. Elimina la ventana emergente de advertencia del bloqueador de anuncios. +// @description:hi YouTube विज्ञापनों को लगभग तुरंत ही स्वचालित रूप से छोड़ दें। विज्ञापन अवरोधक चेतावनी पॉप-अप हटाएँ। +// @description:id Lewati iklan YouTube secara otomatis hampir seketika. Hapus pop-up peringatan pemblokir iklan. +// @description:ja YouTube 広告をほぼ瞬時に自動的にスキップします。広告ブロッカーの警告ポップアップを削除します。 +// @description:ko YouTube 광고를 거의 즉시 자동으로 건너뜁니다. 광고 차단 경고 팝업을 제거합니다. +// @description:pt-BR Pule automaticamente os anúncios do YouTube quase instantaneamente. Remova o pop-up de aviso do bloqueador de anúncios. +// @description:ru Автоматически пропускайте рекламу YouTube почти мгновенно. Уберите всплывающее предупреждение о блокировщике рекламы. +// @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. +// @description:zh-CN 几乎立即自动跳过 YouTube 广告。删除广告拦截器警告弹出窗口。 +// @description:zh-TW 幾乎立即自動跳過 YouTube 廣告。刪除廣告攔截器警告彈出視窗。 // @author tientq64 // @icon https://cdn-icons-png.flaticon.com/64/2504/2504965.png // @match https://www.youtube.com/* // @match https://m.youtube.com/* +// @match https://music.youtube.com/* // @grant none // @license MIT // @compatible firefox @@ -44,95 +41,253 @@ // @homepage https://github.com/tientq64/userscripts/tree/main/scripts/Auto-Skip-YouTube-Ads // ==/UserScript== +/** + * Skip ads. Remove ad elements. + */ function skipAd() { - const isYouTubeShorts = checkIsYouTubeShorts() - if (isYouTubeShorts) return + removeAdElements() - const ad = getInterruptiveAd() - if (ad === null) return + video = null + fineScrubber = document.querySelector('.ytp-fine-scrubbing') - const player = getYouTubePlayer() - if (player === null) return + // Check if the current URL is a YouTube Shorts URL and exit the function if true. + if (window.location.pathname.startsWith('/shorts/')) return - ad.classList.remove('ad-showing') + const moviePlayer = document.querySelector('#movie_player') - const videoData = player.getVideoData() - const videoId = videoData.video_id - const startTime = Math.floor(player.getCurrentTime()) - player.loadVideoById(videoId, startTime) + if (moviePlayer) { + hasAd = moviePlayer.classList.contains('ad-showing') + video = moviePlayer.querySelector('video.html5-main-video') + } - console.log('Ad skipped!', videoId, startTime, videoData.title) -} + if (hasAd) { + const skipButton = document.querySelector(` + .ytp-skip-ad-button, + .ytp-ad-skip-button, + .ytp-ad-skip-button-modern, + .ytp-ad-survey-answer-button + `) + // Click the skip ad button if available. + if (skipButton) { + skipButton.click() + skipButton.remove() + } + // Otherwise, fast forward to the end of the ad video. + // Use `9999` instead of `video.duration` to avoid errors when `duration` is not a number. + else if (video && video.src) { + video.currentTime = 9999 + } + } -function getInterruptiveAd() { - // This element appears when a video ad appears. - const adShowing = document.querySelector('.ad-showing') - if (adShowing !== null) return adShowing + if (video) { + video.addEventListener('pause', handleVideoPause) + video.addEventListener('pointerup', allowPauseVideo) + video.addEventListener('timeupdate', handleVideoTimeUpdate) + } // Timed pie countdown ad. const pieCountdown = document.querySelector('.ytp-ad-timed-pie-countdown-container') - if (pieCountdown !== null) return pieCountdown + if (pieCountdown) { + pieCountdown.remove() + replaceCurrentVideo() + } + + // Handle when ad blocker warning appears inside video player. + const adBlockerWarningInner = document.querySelector( + '.yt-playability-error-supported-renderers' + ) + if (adBlockerWarningInner) { + adBlockerWarningInner.remove() + document.addEventListener('yt-navigate-finish', handleYouTubeNavigateFinish) + replaceCurrentVideo() + } + + // Video play/pause button. + const playButton = document.querySelector( + 'button.ytp-play-button, button.player-control-play-pause-icon' + ) + if (playButton) { + playButton.addEventListener('click', allowPauseVideo) + } +} + +function queryHasSelector(selector, hasSelector, element = document) { + const el = element.querySelector(selector) + if (el === null) return null + const hasEl = el.querySelector(hasSelector) + if (hasEl === null) return null + return el +} + +function queryHasSelectorAll(selector, hasSelector, element = document) { + const els = element.querySelectorAll(selector) + const result = [] + for (const el of els) { + const hasEl = el.querySelector(hasSelector) + if (hasEl === null) continue + result.push(el) + } + return result +} - return null +function getCurrentVideoId() { + const params = new URLSearchParams(location.search) + const videoId = params.get('v') + return videoId } -function checkIsYouTubeShorts() { - return location.pathname.startsWith('/shorts/') +/** + * Check if the user is focused on the input. + */ +function checkEnteringInput() { + if (document.activeElement === null) { + return false + } + return document.activeElement.matches('input, textarea, select') } /** - * Finds and returns the current YouTube video player. - * - * @returns The current YouTube video player, or `null` if not found. + * Temporarily allows the video to be paused, for a short period of time. */ -function getYouTubePlayer() { - let player - if (isYouTubeMobile) { - const playerEl = document.querySelector('#movie_player') - player = playerEl +function allowPauseVideo() { + pausedByUser = true + window.clearTimeout(allowPauseVideoTimeoutId) + allowPauseVideoTimeoutId = window.setTimeout(disallowPauseVideo, 500) +} + +/** + * Pausing the video is not allowed. The purpose is to prevent video from being paused, + * against the behavior of pausing video when YouTube ad blocking warning dialog appears. + * Unless certain conditions, such as pausing by user, etc. + */ +function disallowPauseVideo() { + pausedByUser = false + window.clearTimeout(allowPauseVideoTimeoutId) +} + +function handleWindowBlur() { + isTabBlurred = true +} + +function handleWindowFocus() { + isTabBlurred = false +} + +/** + * Handle when video is paused. If certain conditions are not met, it will continue + * playing. Returning early in this function means the video should be paused as it should + * be. + */ +function handleVideoPause() { + if (isYouTubeMusic) return + + // If it was stopped by the user, it's ok, let the video pause as it should, and exit the function. + if (pausedByUser) { + disallowPauseVideo() + return + } + + // 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. + // Note: While this also gives YouTube the opportunity to pause videos to annoy users, it's an acceptable trade-off. + if (document.hidden) return + if (isTabBlurred) return + + if (fineScrubber && fineScrubber.style.display !== 'none') return + if (video === null) return + if (video.duration - video.currentTime < 0.1) return + + // This is YouTube's disruptive behavior towards users, so the video should continue to play as normal. + video.play() +} + +function handleVideoTimeUpdate() { + if (hasAd || video === null) return + currentVideoTime = video.currentTime +} + +/** + * Handle both keyboard press or release events. + */ +function handleWindowKeyDownAndKeyUp(event) { + if (isYouTubeMusic) return + if (checkEnteringInput()) return + const code = event.code + if (event.type === 'keydown') { + if (code === 'KeyK' || code === 'MediaPlayPause') { + allowPauseVideo() + } } else { - const playerEl = document.querySelector('#ytd-player') - if (playerEl === null) return null - player = playerEl.getPlayer() + if (code === 'Space') { + allowPauseVideo() + } + } +} + +function handleYouTubeNavigateFinish() { + currentVideoTime = 0 + replaceCurrentVideo() +} + +async function replaceCurrentVideo() { + const start = Math.floor(currentVideoTime) + for (let i = 0; i < 16; i++) { + await waitFor(500) + const videoId = getCurrentVideoId() + if (!videoId || !video || video.src) continue + if (isYouTubeMobile) { + const player = document.querySelector('#movie_player') + if (!player) continue + player.loadVideoByPlayerVars({ videoId, start }) + } else { + const player = document.querySelector('#ytd-player') + if (!player) continue + player.loadVideoWithPlayerVars({ videoId, start }) + } } - return player } +function waitFor(millis) { + return new Promise((resolve) => { + window.setTimeout(resolve, millis) + }) +} + +/** + * Add CSS hides some ad elements on the page. + */ function addCss() { - const adsSelectors = [ + const hideCssSelector = [ // Ad banner in the upper right corner, above the video playlist. '#player-ads', // Masthead ad on home page. '#masthead-ad', - // Sponsored ad video items on home page. - // 'ytd-ad-slot-renderer', + 'ytd-ad-slot-renderer', - // '.ytp-suggested-action', - '.yt-mealbar-promo-renderer', + // Ad blocker warning inside the player. + 'yt-playability-error-supported-renderers#error-screen', - // Featured product ad banner at the bottom left of the video. - '.ytp-featured-product', - - // Products shelf ad banner below the video description. - 'ytd-merch-shelf-renderer', + '.ytp-suggested-action', + '.yt-mealbar-promo-renderer', // YouTube Music Premium trial promotion dialog, bottom left corner. 'ytmusic-mealbar-promo-renderer', // YouTube Music Premium trial promotion banner on home page. 'ytmusic-statement-banner-renderer' - ] - const adsSelector = adsSelectors.join(',') - const css = `${adsSelector} { display: none !important; }` + ].join(',') + const css = ` + #ytd-player { visibility: visible !important; } + ${hideCssSelector} { display: none !important; } + ` const style = document.createElement('style') style.textContent = css document.head.appendChild(style) } /** - * Remove ad elements using JavaScript because these selectors require the use of the CSS + * Remove ad elements using javascript because these selectors require the use of the CSS * `:has` selector which is not supported in older browser versions. */ function removeAdElements() { @@ -140,16 +295,19 @@ function removeAdElements() { // Ad banner in the upper right corner, above the video playlist. ['#panels', 'ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]'], + // Temporarily comment out this selector to fix issue [#265124](https://greasyfork.org/en/scripts/498197-auto-skip-youtube-ads/discussions/265124). + // ['#panels', 'ytd-ads-engagement-panel-content-renderer'], + // Sponsored ad video items on home page. // ['ytd-rich-item-renderer', '.ytd-ad-slot-renderer'], // ['ytd-rich-section-renderer', '.ytd-statement-banner-renderer'], // Ad videos on YouTube Short. - ['ytd-reel-video-renderer', '.ytd-ad-slot-renderer'] + ['ytd-reel-video-renderer', '.ytd-ad-slot-renderer'], // Ad blocker warning dialog. - // ['tp-yt-paper-dialog', '#feedback.ytd-enforcement-message-view-model'], + ['tp-yt-paper-dialog', '#feedback.ytd-enforcement-message-view-model'] // Survey dialog on home page, located at bottom right. // ['tp-yt-paper-dialog', ':scope > ytd-checkbox-survey-renderer'], @@ -158,19 +316,63 @@ function removeAdElements() { // ['tp-yt-paper-dialog', ':scope > ytd-single-option-survey-renderer'] ] for (const adSelector of adSelectors) { - const adEl = document.querySelector(adSelector[0]) - if (adEl === null) continue - const neededEl = adEl.querySelector(adSelector[1]) - if (neededEl === null) continue - adEl.remove() + const adEls = queryHasSelectorAll(adSelector[0], adSelector[1]) + for (const adEl of adEls) { + adEl.remove() + } } } +/** + * Is it YouTube mobile version. + */ const isYouTubeMobile = location.hostname === 'm.youtube.com' -window.setInterval(skipAd, 500) -window.setInterval(removeAdElements, 1000) +/** + * Is the current page YouTube Music. + */ +const isYouTubeMusic = location.hostname === 'music.youtube.com' + +/** + * Current video element. + */ +let video = null + +let fineScrubber = null +let hasAd = false +let currentVideoTime = 0 + +/** + * Is the video paused by the user, not paused by YouTube's ad blocker warning dialog. + */ +let pausedByUser = false + +/** + * Is the current tab blurred. + */ +let isTabBlurred = false + +let allowPauseVideoTimeoutId = 0 + +// Observe DOM changes to detect ads. +if (window.MutationObserver) { + const observer = new MutationObserver(skipAd) + observer.observe(document.body, { + attributes: true, + attributeFilter: ['class', 'src'], + childList: true, + subtree: true + }) +} +// If DOM observation is not supported. Detect ads every 500ms (2 times per second). +else { + window.setInterval(skipAd, 500) +} + +window.addEventListener('blur', handleWindowBlur) +window.addEventListener('focus', handleWindowFocus) +window.addEventListener('keydown', handleWindowKeyDownAndKeyUp) +window.addEventListener('keyup', handleWindowKeyDownAndKeyUp) addCss() -removeAdElements() skipAd() diff --git a/scripts/Auto-Skip-YouTube-Ads/script.user.ts b/scripts/Auto-Skip-YouTube-Ads/script.user.ts index b934c94..22c5f94 100644 --- a/scripts/Auto-Skip-YouTube-Ads/script.user.ts +++ b/scripts/Auto-Skip-YouTube-Ads/script.user.ts @@ -1,38 +1,35 @@ // ==UserScript== // @name Auto Skip YouTube Ads -// @name:ar تخطي إعلانات YouTube تلقائيًا +// @name:ar التخطي التلقائي لإعلانات YouTube // @name:es Saltar Automáticamente Anuncios De YouTube -// @name:fr Ignorer Automatiquement Les Publicités YouTube // @name:hi YouTube विज्ञापन स्वचालित रूप से छोड़ें // @name:id Lewati Otomatis Iklan YouTube // @name:ja YouTube 広告を自動スキップ // @name:ko YouTube 광고 자동 건너뛰기 -// @name:nl YouTube-Advertenties Automatisch Overslaan // @name:pt-BR Pular Automaticamente Anúncios Do YouTube // @name:ru Автоматический Пропуск Рекламы На YouTube // @name:vi Tự Động Bỏ Qua Quảng Cáo YouTube // @name:zh-CN 自动跳过 YouTube 广告 // @name:zh-TW 自動跳過 YouTube 廣告 // @namespace https://github.com/tientq64/userscripts -// @version 6.0.3 -// @description Automatically skip YouTube ads instantly. Undetected by YouTube ad blocker warnings. -// @description:ar تخطي إعلانات YouTube تلقائيًا على الفور. دون أن يتم اكتشاف ذلك من خلال تحذيرات أداة حظر الإعلانات في YouTube. -// @description:es Omite automáticamente los anuncios de YouTube al instante. Sin que te detecten las advertencias del bloqueador de anuncios de YouTube. -// @description:fr Ignorez automatiquement et instantanément les publicités YouTube. Non détecté par les avertissements du bloqueur de publicités YouTube. -// @description:hi YouTube विज्ञापनों को स्वचालित रूप से तुरंत छोड़ दें। YouTube विज्ञापन अवरोधक चेतावनियों द्वारा पता नहीं लगाया गया। -// @description:id Lewati iklan YouTube secara otomatis secara instan. Tidak terdeteksi oleh peringatan pemblokir iklan YouTube. -// @description:ja YouTube 広告を即座に自動的にスキップします。YouTube 広告ブロッカーの警告には検出されません。 -// @description:ko YouTube 광고를 즉시 자동으로 건너뜁니다. YouTube 광고 차단 경고에 감지되지 않습니다. -// @description:nl Sla YouTube-advertenties direct automatisch over. Ongemerkt door YouTube-adblockerwaarschuwingen. -// @description:pt-BR Pule anúncios do YouTube instantaneamente. Não detectado pelos avisos do bloqueador de anúncios do YouTube. -// @description:ru Автоматически пропускать рекламу YouTube мгновенно. Не обнаруживается предупреждениями блокировщиков рекламы YouTube. -// @description:vi Tự động bỏ qua quảng cáo YouTube ngay lập tức. Không bị phát hiện bởi cảnh báo trình chặn quảng cáo của YouTube. -// @description:zh-CN 立即自动跳过 YouTube 广告。不会被 YouTube 广告拦截器警告检测到。 -// @description:zh-TW 立即自動跳過 YouTube 廣告。 YouTube 廣告攔截器警告未被偵測到。 +// @version 7.0.0 +// @description Automatically skip YouTube ads almost instantly. Remove the ad blocker warning pop-up. +// @description:ar تخطي إعلانات YouTube تلقائيًا في الحال. إزالة النافذة المنبثقة لتحذير مانع الإعلانات. +// @description:es Omite automáticamente los anuncios de YouTube casi al instante. Elimina la ventana emergente de advertencia del bloqueador de anuncios. +// @description:hi YouTube विज्ञापनों को लगभग तुरंत ही स्वचालित रूप से छोड़ दें। विज्ञापन अवरोधक चेतावनी पॉप-अप हटाएँ। +// @description:id Lewati iklan YouTube secara otomatis hampir seketika. Hapus pop-up peringatan pemblokir iklan. +// @description:ja YouTube 広告をほぼ瞬時に自動的にスキップします。広告ブロッカーの警告ポップアップを削除します。 +// @description:ko YouTube 광고를 거의 즉시 자동으로 건너뜁니다. 광고 차단 경고 팝업을 제거합니다. +// @description:pt-BR Pule automaticamente os anúncios do YouTube quase instantaneamente. Remova o pop-up de aviso do bloqueador de anúncios. +// @description:ru Автоматически пропускайте рекламу YouTube почти мгновенно. Уберите всплывающее предупреждение о блокировщике рекламы. +// @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. +// @description:zh-CN 几乎立即自动跳过 YouTube 广告。删除广告拦截器警告弹出窗口。 +// @description:zh-TW 幾乎立即自動跳過 YouTube 廣告。刪除廣告攔截器警告彈出視窗。 // @author tientq64 // @icon https://cdn-icons-png.flaticon.com/64/2504/2504965.png // @match https://www.youtube.com/* // @match https://m.youtube.com/* +// @match https://music.youtube.com/* // @grant none // @license MIT // @compatible firefox @@ -43,115 +40,263 @@ // @noframes // ==/UserScript== -interface YtdPlayerElement extends HTMLElement { - getPlayer: () => YouTubePlayer -} - /** - * YouTube video player. + * Skip ads. Remove ad elements. */ -interface YouTubePlayer { - getVideoData: () => YouTubeVideoData - getCurrentTime: () => number - loadVideoById: (videoId: string, startTime?: number) => void -} - -interface YouTubeVideoData { - title: string - video_id: string -} - function skipAd(): void { - const isYouTubeShorts: boolean = checkIsYouTubeShorts() - if (isYouTubeShorts) return + removeAdElements() - const ad: HTMLElement | null = getInterruptiveAd() - if (ad === null) return + video = null + fineScrubber = document.querySelector('.ytp-fine-scrubbing') - const player: YouTubePlayer | null = getYouTubePlayer() - if (player === null) return + // Check if the current URL is a YouTube Shorts URL and exit the function if true. + if (window.location.pathname.startsWith('/shorts/')) return - ad.classList.remove('ad-showing') + const moviePlayer = document.querySelector('#movie_player') - const videoData: YouTubeVideoData = player.getVideoData() - const videoId: string = videoData.video_id - const startTime: number = Math.floor(player.getCurrentTime()) - player.loadVideoById(videoId, startTime) + if (moviePlayer) { + hasAd = moviePlayer.classList.contains('ad-showing') + video = moviePlayer.querySelector('video.html5-main-video') + } - console.log('Ad skipped!', videoId, startTime, videoData.title) -} + if (hasAd) { + const skipButton = document.querySelector(` + .ytp-skip-ad-button, + .ytp-ad-skip-button, + .ytp-ad-skip-button-modern, + .ytp-ad-survey-answer-button + `) + // Click the skip ad button if available. + if (skipButton) { + skipButton.click() + skipButton.remove() + } + // Otherwise, fast forward to the end of the ad video. + // Use `9999` instead of `video.duration` to avoid errors when `duration` is not a number. + else if (video && video.src) { + video.currentTime = 9999 + } + } -function getInterruptiveAd(): HTMLElement | null { - // This element appears when a video ad appears. - const adShowing = document.querySelector('.ad-showing') - if (adShowing !== null) return adShowing + if (video) { + video.addEventListener('pause', handleVideoPause) + video.addEventListener('pointerup', allowPauseVideo) + video.addEventListener('timeupdate', handleVideoTimeUpdate) + } // Timed pie countdown ad. const pieCountdown = document.querySelector( '.ytp-ad-timed-pie-countdown-container' ) - if (pieCountdown !== null) return pieCountdown + if (pieCountdown) { + pieCountdown.remove() + replaceCurrentVideo() + } - return null + // Handle when ad blocker warning appears inside video player. + const adBlockerWarningInner = document.querySelector( + '.yt-playability-error-supported-renderers' + ) + if (adBlockerWarningInner) { + adBlockerWarningInner.remove() + document.addEventListener('yt-navigate-finish', handleYouTubeNavigateFinish) + replaceCurrentVideo() + } + + // Video play/pause button. + const playButton = document.querySelector( + 'button.ytp-play-button, button.player-control-play-pause-icon' + ) + if (playButton) { + playButton.addEventListener('click', allowPauseVideo) + } +} + +function queryHasSelector( + selector: string, + hasSelector: string, + element: Document | Element = document +): T | null { + const el = element.querySelector(selector) + if (el === null) return null + const hasEl = el.querySelector(hasSelector) + if (hasEl === null) return null + return el +} + +function queryHasSelectorAll( + selector: string, + hasSelector: string, + element: Document | Element = document +): T[] { + const els = element.querySelectorAll(selector) + const result: T[] = [] + for (const el of els) { + const hasEl = el.querySelector(hasSelector) + if (hasEl === null) continue + result.push(el) + } + return result } -function checkIsYouTubeShorts(): boolean { - return location.pathname.startsWith('/shorts/') +function getCurrentVideoId(): string | null { + const params = new URLSearchParams(location.search) + const videoId: string | null = params.get('v') + return videoId } /** - * Finds and returns the current YouTube video player. - * - * @returns The current YouTube video player, or `null` if not found. + * Check if the user is focused on the input. */ -function getYouTubePlayer(): YouTubePlayer | null { - let player: YouTubePlayer | null - if (isYouTubeMobile) { - const playerEl: unknown = document.querySelector('#movie_player') - player = playerEl as YouTubePlayer +function checkEnteringInput(): boolean { + if (document.activeElement === null) { + return false + } + return document.activeElement.matches('input, textarea, select') +} + +/** + * Temporarily allows the video to be paused, for a short period of time. + */ +function allowPauseVideo(): void { + pausedByUser = true + window.clearTimeout(allowPauseVideoTimeoutId) + allowPauseVideoTimeoutId = window.setTimeout(disallowPauseVideo, 500) +} + +/** + * Pausing the video is not allowed. The purpose is to prevent video from being paused, + * against the behavior of pausing video when YouTube ad blocking warning dialog appears. + * Unless certain conditions, such as pausing by user, etc. + */ +function disallowPauseVideo(): void { + pausedByUser = false + window.clearTimeout(allowPauseVideoTimeoutId) +} + +function handleWindowBlur(): void { + isTabBlurred = true +} + +function handleWindowFocus(): void { + isTabBlurred = false +} + +/** + * Handle when video is paused. If certain conditions are not met, it will continue + * playing. Returning early in this function means the video should be paused as it should + * be. + */ +function handleVideoPause(): void { + if (isYouTubeMusic) return + + // If it was stopped by the user, it's ok, let the video pause as it should, and exit the function. + if (pausedByUser) { + disallowPauseVideo() + return + } + + // 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. + // Note: While this also gives YouTube the opportunity to pause videos to annoy users, it's an acceptable trade-off. + if (document.hidden) return + if (isTabBlurred) return + + if (fineScrubber && fineScrubber.style.display !== 'none') return + if (video === null) return + if (video.duration - video.currentTime < 0.1) return + + // This is YouTube's disruptive behavior towards users, so the video should continue to play as normal. + video.play() +} + +function handleVideoTimeUpdate(): void { + if (hasAd || video === null) return + currentVideoTime = video.currentTime +} + +/** + * Handle both keyboard press or release events. + */ +function handleWindowKeyDownAndKeyUp(event: KeyboardEvent): void { + if (isYouTubeMusic) return + if (checkEnteringInput()) return + const code: string = event.code + if (event.type === 'keydown') { + if (code === 'KeyK' || code === 'MediaPlayPause') { + allowPauseVideo() + } } else { - const playerEl = document.querySelector('#ytd-player') - if (playerEl === null) return null - player = playerEl.getPlayer() + if (code === 'Space') { + allowPauseVideo() + } + } +} + +function handleYouTubeNavigateFinish(): void { + currentVideoTime = 0 + replaceCurrentVideo() +} + +async function replaceCurrentVideo(): Promise { + const start: number = Math.floor(currentVideoTime) + for (let i = 0; i < 16; i++) { + await waitFor(500) + const videoId: string | null = getCurrentVideoId() + if (!videoId || !video || video.src) continue + if (isYouTubeMobile) { + const player = document.querySelector('#movie_player') + if (!player) continue + player.loadVideoByPlayerVars({ videoId, start }) + } else { + const player = document.querySelector('#ytd-player') + if (!player) continue + player.loadVideoWithPlayerVars({ videoId, start }) + } } - return player } +function waitFor(millis: number): Promise { + return new Promise((resolve) => { + window.setTimeout(resolve, millis) + }) +} + +/** + * Add CSS hides some ad elements on the page. + */ function addCss(): void { - const adsSelectors: string[] = [ + const hideCssSelector: string = [ // Ad banner in the upper right corner, above the video playlist. '#player-ads', // Masthead ad on home page. '#masthead-ad', - // Sponsored ad video items on home page. - // 'ytd-ad-slot-renderer', - - // '.ytp-suggested-action', - '.yt-mealbar-promo-renderer', + 'ytd-ad-slot-renderer', - // Featured product ad banner at the bottom left of the video. - '.ytp-featured-product', + // Ad blocker warning inside the player. + 'yt-playability-error-supported-renderers#error-screen', - // Products shelf ad banner below the video description. - 'ytd-merch-shelf-renderer', + '.ytp-suggested-action', + '.yt-mealbar-promo-renderer', // YouTube Music Premium trial promotion dialog, bottom left corner. 'ytmusic-mealbar-promo-renderer', // YouTube Music Premium trial promotion banner on home page. 'ytmusic-statement-banner-renderer' - ] - const adsSelector: string = adsSelectors.join(',') - const css: string = `${adsSelector} { display: none !important; }` - const style = document.createElement('style') + ].join(',') + const css: string = ` + #ytd-player { visibility: visible !important; } + ${hideCssSelector} { display: none !important; } + ` + const style: HTMLStyleElement = document.createElement('style') style.textContent = css document.head.appendChild(style) } /** - * Remove ad elements using JavaScript because these selectors require the use of the CSS + * Remove ad elements using javascript because these selectors require the use of the CSS * `:has` selector which is not supported in older browser versions. */ function removeAdElements(): void { @@ -159,16 +304,19 @@ function removeAdElements(): void { // Ad banner in the upper right corner, above the video playlist. ['#panels', 'ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]'], + // Temporarily comment out this selector to fix issue [#265124](https://greasyfork.org/en/scripts/498197-auto-skip-youtube-ads/discussions/265124). + // ['#panels', 'ytd-ads-engagement-panel-content-renderer'], + // Sponsored ad video items on home page. // ['ytd-rich-item-renderer', '.ytd-ad-slot-renderer'], // ['ytd-rich-section-renderer', '.ytd-statement-banner-renderer'], // Ad videos on YouTube Short. - ['ytd-reel-video-renderer', '.ytd-ad-slot-renderer'] + ['ytd-reel-video-renderer', '.ytd-ad-slot-renderer'], // Ad blocker warning dialog. - // ['tp-yt-paper-dialog', '#feedback.ytd-enforcement-message-view-model'], + ['tp-yt-paper-dialog', '#feedback.ytd-enforcement-message-view-model'] // Survey dialog on home page, located at bottom right. // ['tp-yt-paper-dialog', ':scope > ytd-checkbox-survey-renderer'], @@ -177,19 +325,63 @@ function removeAdElements(): void { // ['tp-yt-paper-dialog', ':scope > ytd-single-option-survey-renderer'] ] for (const adSelector of adSelectors) { - const adEl = document.querySelector(adSelector[0]) - if (adEl === null) continue - const neededEl = adEl.querySelector(adSelector[1]) - if (neededEl === null) continue - adEl.remove() + const adEls = queryHasSelectorAll(adSelector[0], adSelector[1]) + for (const adEl of adEls) { + adEl.remove() + } } } +/** + * Is it YouTube mobile version. + */ const isYouTubeMobile: boolean = location.hostname === 'm.youtube.com' -window.setInterval(skipAd, 500) -window.setInterval(removeAdElements, 1000) +/** + * Is the current page YouTube Music. + */ +const isYouTubeMusic: boolean = location.hostname === 'music.youtube.com' + +/** + * Current video element. + */ +let video: HTMLVideoElement | null = null + +let fineScrubber: HTMLDivElement | null = null +let hasAd: boolean = false +let currentVideoTime: number = 0 + +/** + * Is the video paused by the user, not paused by YouTube's ad blocker warning dialog. + */ +let pausedByUser: boolean = false + +/** + * Is the current tab blurred. + */ +let isTabBlurred: boolean = false + +let allowPauseVideoTimeoutId: number = 0 + +// Observe DOM changes to detect ads. +if (window.MutationObserver) { + const observer: MutationObserver = new MutationObserver(skipAd) + observer.observe(document.body, { + attributes: true, + attributeFilter: ['class', 'src'], + childList: true, + subtree: true + }) +} +// If DOM observation is not supported. Detect ads every 500ms (2 times per second). +else { + window.setInterval(skipAd, 500) +} + +window.addEventListener('blur', handleWindowBlur) +window.addEventListener('focus', handleWindowFocus) +window.addEventListener('keydown', handleWindowKeyDownAndKeyUp) +window.addEventListener('keyup', handleWindowKeyDownAndKeyUp) addCss() -removeAdElements() skipAd() From 5c572b80af60065b85a094d51987e5bd5f18c671 Mon Sep 17 00:00:00 2001 From: tientq64 Date: Mon, 17 Feb 2025 10:44:02 +0700 Subject: [PATCH 2/2] Remove unused `@types/trusted-types` lib --- .resources/global.d.ts | 3 --- package.json | 1 - pnpm-lock.yaml | 8 -------- 3 files changed, 12 deletions(-) diff --git a/.resources/global.d.ts b/.resources/global.d.ts index f3fbb53..50fff57 100644 --- a/.resources/global.d.ts +++ b/.resources/global.d.ts @@ -56,6 +56,3 @@ interface YtdPlayerElement extends HTMLElement { interface YouTubeMoviePlayerElement extends HTMLElement { loadVideoByPlayerVars(options: { videoId: string; start?: number }): void } - -declare type TrustedTypePolicyFactory = import('trusted-types/lib').TrustedTypePolicyFactory -declare const trustedTypes: TrustedTypePolicyFactory diff --git a/package.json b/package.json index 375ed01..d0ea677 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "@types/node": "^20.14.13", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "@types/trusted-types": "2.0.7", "concurrently": "^8.2.2", "eslint": "8.57.0", "eslint-plugin-react": "^7.35.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df8e21c..87cf79a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,9 +45,6 @@ importers: '@types/react-dom': specifier: 18.3.0 version: 18.3.0 - '@types/trusted-types': - specifier: 2.0.7 - version: 2.0.7 concurrently: specifier: ^8.2.2 version: 8.2.2 @@ -357,9 +354,6 @@ packages: '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} - '@types/trusted-types@2.0.7': - resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -1906,8 +1900,6 @@ snapshots: '@types/prop-types': 15.7.13 csstype: 3.1.3 - '@types/trusted-types@2.0.7': {} - '@types/unist@3.0.3': {} '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)':