diff --git a/src/i18n/resources/en.json b/src/i18n/resources/en.json index 2d75479d3f..7cee2e74b2 100644 --- a/src/i18n/resources/en.json +++ b/src/i18n/resources/en.json @@ -214,6 +214,10 @@ }, "name": "Ad Blocker" }, + "ad-speedup": { + "name": "Ad Speedup", + "description": "If an ad play it mutes the audio and sets playback speed to 16x" + }, "album-actions": { "description": "Adds Undislike, Dislike, Like, and Unlike buttons to apply this to all songs in a playlist or album", "name": "Album Actions" diff --git a/src/plugins/adblocker/adSpeedup.ts b/src/plugins/adblocker/adSpeedup.ts new file mode 100644 index 0000000000..dbe2ea350e --- /dev/null +++ b/src/plugins/adblocker/adSpeedup.ts @@ -0,0 +1,56 @@ +function skipAd(target: Element) { + const skipButton = target.querySelector('button.ytp-ad-skip-button-modern'); + if (skipButton) { + skipButton.click(); + } +} + +function speedUpAndMute(player: Element, isAdShowing: boolean) { + const video = player.querySelector('video'); + if (!video) return; + if (isAdShowing) { + video.playbackRate = 16; + video.muted = true; + } else if (!isAdShowing) { + video.playbackRate = 1; + video.muted = false; + } +} + +export const loadAdSpeedup = async () => { + const player = document.querySelector('#movie_player'); + if (!player) return; + + new MutationObserver((mutations) => { + for (const mutation of mutations) { + if ( + mutation.type === 'attributes' && + mutation.attributeName === 'class' + ) { + const target = mutation.target as HTMLElement; + + const isAdShowing = + target.classList.contains('ad-showing') || + target.classList.contains('ad-interrupting'); + speedUpAndMute(target, isAdShowing); + } + if ( + mutation.type === 'childList' && + mutation.addedNodes.length && + mutation.target instanceof HTMLElement + ) { + skipAd(mutation.target); + } + } + }).observe(player, { + attributes: true, + childList: true, + subtree: true, + }); + + const isAdShowing = + player.classList.contains('ad-showing') || + player.classList.contains('ad-interrupting'); + speedUpAndMute(player, isAdShowing); + skipAd(player); +} diff --git a/src/plugins/adblocker/index.ts b/src/plugins/adblocker/index.ts index bfc35f93a2..9deb74c869 100644 --- a/src/plugins/adblocker/index.ts +++ b/src/plugins/adblocker/index.ts @@ -14,6 +14,7 @@ import { inject, isInjected } from './injectors/inject'; import { t } from '@/i18n'; import type { BrowserWindow } from 'electron'; +import { loadAdSpeedup } from './adSpeedup'; interface AdblockerConfig { /** @@ -72,6 +73,14 @@ export default createPlugin({ }, ]; }, + renderer: { + async onPlayerApiReady(_, {getConfig}) { + const config = await getConfig(); + if (config.blocker === blockers.AdSpeedup) { + await loadAdSpeedup(); + } + } + }, backend: { mainWindow: null as BrowserWindow | null, async start({ getConfig, window }) { diff --git a/src/plugins/adblocker/types/index.ts b/src/plugins/adblocker/types/index.ts index 74b7c20be0..d96ce6647c 100644 --- a/src/plugins/adblocker/types/index.ts +++ b/src/plugins/adblocker/types/index.ts @@ -1,4 +1,5 @@ export const blockers = { WithBlocklists: 'With blocklists', InPlayer: 'In player', + AdSpeedup: 'Ad speedup', } as const;