From f5175a6be7517fe275673d75159915049df946fb Mon Sep 17 00:00:00 2001 From: JellyBrick Date: Wed, 17 Sep 2025 17:48:12 +0900 Subject: [PATCH] REMOVE adblocker AND no-google-login, and renaming --- README.md | 7 +- src/plugins/adblocker/.gitignore | 1 - src/plugins/adblocker/adSpeedup.ts | 58 ---- src/plugins/adblocker/blocker.ts | 81 ------ src/plugins/adblocker/index.ts | 148 ---------- .../injectors/inject-cliqz-preload.ts | 3 - src/plugins/adblocker/injectors/inject.d.ts | 5 - src/plugins/adblocker/injectors/inject.js | 259 ------------------ src/plugins/adblocker/types/index.ts | 5 - src/plugins/no-google-login/index.ts | 26 -- src/plugins/no-google-login/style.css | 6 - 11 files changed, 1 insertion(+), 598 deletions(-) delete mode 100644 src/plugins/adblocker/.gitignore delete mode 100644 src/plugins/adblocker/adSpeedup.ts delete mode 100644 src/plugins/adblocker/blocker.ts delete mode 100644 src/plugins/adblocker/index.ts delete mode 100644 src/plugins/adblocker/injectors/inject-cliqz-preload.ts delete mode 100644 src/plugins/adblocker/injectors/inject.d.ts delete mode 100644 src/plugins/adblocker/injectors/inject.js delete mode 100644 src/plugins/adblocker/types/index.ts delete mode 100644 src/plugins/no-google-login/index.ts delete mode 100644 src/plugins/no-google-login/style.css diff --git a/README.md b/README.md index ea4d347c..e37b6a92 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
-# YouTube Music +# YTMD [![GitHub release](https://img.shields.io/github/release/th-ch/youtube-music.svg?style=for-the-badge&logo=youtube-music)](https://github.com/th-ch/youtube-music/releases/) [![GitHub license](https://img.shields.io/github/license/th-ch/youtube-music.svg?style=for-the-badge)](https://github.com/th-ch/youtube-music/blob/master/license) @@ -65,9 +65,6 @@ Read this in other languages: [한국어](./docs/readme/README-ko.md), [Françai - And more ... ## Available plugins: - -- **Ad Blocker**: Block all ads and tracking out of the box - - **Album Actions**: Adds Undislike, Dislike, Like, and Unlike buttons to apply this to all songs in a playlist or album - **Album Color Theme**: Applies a dynamic theme and visual effects based on the album color palette @@ -116,8 +113,6 @@ Read this in other languages: [한국어](./docs/readme/README-ko.md), [Françai - **Navigation**: Next/Back navigation arrows directly integrated in the interface, like in your favorite browser -- **No Google Login**: Remove Google login buttons and links from the interface - - **Notifications**: Display a notification when a song starts playing ([interactive notifications](https://user-images.githubusercontent.com/78568641/114102651-63ce0e00-98d0-11eb-9dfe-c5a02bb54f9c.png) are available on windows) diff --git a/src/plugins/adblocker/.gitignore b/src/plugins/adblocker/.gitignore deleted file mode 100644 index af053a2b..00000000 --- a/src/plugins/adblocker/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/ad-blocker-engine.bin diff --git a/src/plugins/adblocker/adSpeedup.ts b/src/plugins/adblocker/adSpeedup.ts deleted file mode 100644 index acbfd5ef..00000000 --- a/src/plugins/adblocker/adSpeedup.ts +++ /dev/null @@ -1,58 +0,0 @@ -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 = () => { - 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/blocker.ts b/src/plugins/adblocker/blocker.ts deleted file mode 100644 index 368adfe3..00000000 --- a/src/plugins/adblocker/blocker.ts +++ /dev/null @@ -1,81 +0,0 @@ -// Used for caching -import path from 'node:path'; -import fs, { promises } from 'node:fs'; - -import { ElectronBlocker } from '@ghostery/adblocker-electron'; -import { app, net } from 'electron'; - -const SOURCES = [ - 'https://raw.githubusercontent.com/kbinani/adblock-youtube-ads/master/signed.txt', - // UBlock Origin - 'https://raw.githubusercontent.com/ghostery/adblocker/master/packages/adblocker/assets/ublock-origin/filters.txt', - 'https://raw.githubusercontent.com/ghostery/adblocker/master/packages/adblocker/assets/ublock-origin/quick-fixes.txt', - 'https://raw.githubusercontent.com/ghostery/adblocker/master/packages/adblocker/assets/ublock-origin/unbreak.txt', - 'https://raw.githubusercontent.com/ghostery/adblocker/master/packages/adblocker/assets/ublock-origin/filters-2020.txt', - 'https://raw.githubusercontent.com/ghostery/adblocker/master/packages/adblocker/assets/ublock-origin/filters-2021.txt', - 'https://raw.githubusercontent.com/ghostery/adblocker/master/packages/adblocker/assets/ublock-origin/filters-2022.txt', - 'https://raw.githubusercontent.com/ghostery/adblocker/master/packages/adblocker/assets/ublock-origin/filters-2023.txt', - // Fanboy Annoyances - 'https://secure.fanboy.co.nz/fanboy-annoyance_ubo.txt', - // AdGuard - 'https://filters.adtidy.org/extension/ublock/filters/122_optimized.txt', -]; - -let blocker: ElectronBlocker | undefined; - -export const loadAdBlockerEngine = async ( - session: Electron.Session | undefined = undefined, - cache: boolean = true, - additionalBlockLists: string[] = [], - disableDefaultLists: boolean | unknown[] = false, -) => { - // Only use cache if no additional blocklists are passed - const cacheDirectory = path.join(app.getPath('userData'), 'adblock_cache'); - if (!fs.existsSync(cacheDirectory)) { - fs.mkdirSync(cacheDirectory); - } - const cachingOptions = - cache && additionalBlockLists.length === 0 - ? { - path: path.join(cacheDirectory, 'adblocker-engine.bin'), - read: promises.readFile, - write: promises.writeFile, - } - : undefined; - const lists = [ - ...((disableDefaultLists && !Array.isArray(disableDefaultLists)) || - (Array.isArray(disableDefaultLists) && disableDefaultLists.length > 0) - ? [] - : SOURCES), - ...additionalBlockLists, - ]; - - try { - blocker = await ElectronBlocker.fromLists( - (url: string) => net.fetch(url), - lists, - { - enableCompression: true, - // When generating the engine for caching, do not load network filters - // So that enhancing the session works as expected - // Allowing to define multiple webRequest listeners - loadNetworkFilters: session !== undefined, - }, - cachingOptions, - ); - if (session) { - blocker.enableBlockingInSession(session); - } - } catch (error) { - console.error('Error loading adBlocker engine', error); - } -}; - -export const unloadAdBlockerEngine = (session: Electron.Session) => { - if (blocker) { - blocker.disableBlockingInSession(session); - } -}; - -export const isBlockerEnabled = (session: Electron.Session) => - blocker !== undefined && blocker.isBlockingEnabled(session); diff --git a/src/plugins/adblocker/index.ts b/src/plugins/adblocker/index.ts deleted file mode 100644 index 5cacba7c..00000000 --- a/src/plugins/adblocker/index.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { contextBridge, webFrame } from 'electron'; - -import { blockers } from './types'; -import { createPlugin } from '@/utils'; -import { - isBlockerEnabled, - loadAdBlockerEngine, - unloadAdBlockerEngine, -} from './blocker'; - -import { inject, isInjected } from './injectors/inject'; -import { loadAdSpeedup } from './adSpeedup'; - -import { t } from '@/i18n'; - -import type { BrowserWindow } from 'electron'; - -interface AdblockerConfig { - /** - * Whether to enable the adblocker. - * @default true - */ - enabled: boolean; - /** - * When enabled, the adblocker will cache the blocklists. - * @default true - */ - cache: boolean; - /** - * Which adblocker to use. - * @default blockers.InPlayer - */ - blocker: (typeof blockers)[keyof typeof blockers]; - /** - * Additional list of filters to use. - * @example ["https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt"] - * @default [] - */ - additionalBlockLists: string[]; - /** - * Disable the default blocklists. - * @default false - */ - disableDefaultLists: boolean; -} - -export default createPlugin({ - name: () => t('plugins.adblocker.name'), - description: () => t('plugins.adblocker.description'), - restartNeeded: false, - config: { - enabled: true, - cache: true, - blocker: blockers.InPlayer, - additionalBlockLists: [], - disableDefaultLists: false, - } as AdblockerConfig, - menu: async ({ getConfig, setConfig }) => { - const config = await getConfig(); - - return [ - { - label: t('plugins.adblocker.menu.blocker'), - submenu: Object.values(blockers).map((blocker) => ({ - label: blocker, - type: 'radio', - checked: (config.blocker || blockers.WithBlocklists) === blocker, - click() { - setConfig({ blocker }); - }, - })), - }, - ]; - }, - renderer: { - async onPlayerApiReady(_, { getConfig }) { - const config = await getConfig(); - if (config.blocker === blockers.AdSpeedup) { - loadAdSpeedup(); - } - }, - }, - backend: { - mainWindow: null as BrowserWindow | null, - async start({ getConfig, window }) { - const config = await getConfig(); - this.mainWindow = window; - - if (config.blocker === blockers.WithBlocklists) { - await loadAdBlockerEngine( - window.webContents.session, - config.cache, - config.additionalBlockLists, - config.disableDefaultLists, - ); - } - }, - stop({ window }) { - if (isBlockerEnabled(window.webContents.session)) { - unloadAdBlockerEngine(window.webContents.session); - } - }, - async onConfigChange(newConfig) { - if (this.mainWindow) { - if ( - newConfig.blocker === blockers.WithBlocklists && - !isBlockerEnabled(this.mainWindow.webContents.session) - ) { - await loadAdBlockerEngine( - this.mainWindow.webContents.session, - newConfig.cache, - newConfig.additionalBlockLists, - newConfig.disableDefaultLists, - ); - } - } - }, - }, - preload: { - // see #1478 - script: `const _prunerFn = window._pruner; - window._pruner = undefined; - JSON.parse = new Proxy(JSON.parse, { - apply() { - return _prunerFn(Reflect.apply(...arguments)); - }, - }); - Response.prototype.json = new Proxy(Response.prototype.json, { - apply() { - return Reflect.apply(...arguments).then((o) => _prunerFn(o)); - }, - }); 0`, - async start({ getConfig }) { - const config = await getConfig(); - - if (config.blocker === blockers.InPlayer && !isInjected()) { - inject(contextBridge); - await webFrame.executeJavaScript(this.script); - } - }, - async onConfigChange(newConfig) { - if (newConfig.blocker === blockers.InPlayer && !isInjected()) { - inject(contextBridge); - await webFrame.executeJavaScript(this.script); - } - }, - }, -}); diff --git a/src/plugins/adblocker/injectors/inject-cliqz-preload.ts b/src/plugins/adblocker/injectors/inject-cliqz-preload.ts deleted file mode 100644 index 27463f0b..00000000 --- a/src/plugins/adblocker/injectors/inject-cliqz-preload.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default async () => { - await import('@ghostery/adblocker-electron-preload'); -}; diff --git a/src/plugins/adblocker/injectors/inject.d.ts b/src/plugins/adblocker/injectors/inject.d.ts deleted file mode 100644 index 10062acc..00000000 --- a/src/plugins/adblocker/injectors/inject.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { ContextBridge } from 'electron'; - -export const inject: (contextBridge: ContextBridge) => void; - -export const isInjected: () => boolean; diff --git a/src/plugins/adblocker/injectors/inject.js b/src/plugins/adblocker/injectors/inject.js deleted file mode 100644 index 6e6219fe..00000000 --- a/src/plugins/adblocker/injectors/inject.js +++ /dev/null @@ -1,259 +0,0 @@ -/* eslint-disable */ - -// Source: https://addons.mozilla.org/en-US/firefox/addon/adblock-for-youtube/ -// https://robwu.nl/crxviewer/?crx=https%3A%2F%2Faddons.mozilla.org%2Fen-US%2Ffirefox%2Faddon%2Fadblock-for-youtube%2F - -/* - Parts of this code is derived from set-constant.js: - https://github.com/gorhill/uBlock/blob/5de0ce975753b7565759ac40983d31978d1f84ca/assets/resources/scriptlets.js#L704 - */ - -let injected = false; - -export const isInjected = () => injected; - -/** - * @param {Electron.ContextBridge} contextBridge - * @returns {*} - */ -export const inject = (contextBridge) => { - injected = true; - { - const pruner = function (o) { - delete o.playerAds; - delete o.adPlacements; - delete o.adSlots; - // - if (o.playerResponse) { - delete o.playerResponse.playerAds; - delete o.playerResponse.adPlacements; - delete o.playerResponse.adSlots; - } - if (o.ytInitialPlayerResponse) { - delete o.ytInitialPlayerResponse.playerAds; - delete o.ytInitialPlayerResponse.adPlacements; - delete o.ytInitialPlayerResponse.adSlots; - } - - // - return o; - } - - contextBridge.exposeInMainWorld('_pruner', pruner); - } - - const chains = [ - { - chain: 'playerResponse.adPlacements', - cValue: 'undefined', - }, - { - chain: 'ytInitialPlayerResponse.playerAds', - cValue: 'undefined', - }, - { - chain: 'ytInitialPlayerResponse.adPlacements', - cValue: 'undefined', - }, - { - chain: 'ytInitialPlayerResponse.adSlots', - cValue: 'undefined', - } - ]; - - chains.forEach(function ({ chain, cValue }) { - const thisScript = document.currentScript; - // - switch (cValue) { - case 'null': { - cValue = null; - break; - } - - case "''": { - cValue = ''; - break; - } - - case 'true': { - cValue = true; - break; - } - - case 'false': { - cValue = false; - break; - } - - case 'undefined': { - cValue = undefined; - break; - } - - case 'noopFunc': { - cValue = function () {}; - - break; - } - - case 'trueFunc': { - cValue = function () { - return true; - }; - - break; - } - - case 'falseFunc': { - cValue = function () { - return false; - }; - - break; - } - - default: { - if (/^\d+$/.test(cValue)) { - cValue = Number.parseFloat(cValue); - // - if (isNaN(cValue)) { - return; - } - - if (Math.abs(cValue) > 0x7f_ff) { - return; - } - } else { - return; - } - } - } - - // - let aborted = false; - const mustAbort = function (v) { - if (aborted) { - return true; - } - - aborted = - v !== undefined && - v !== null && - cValue !== undefined && - cValue !== null && - typeof v !== typeof cValue; - return aborted; - }; - - /* - Support multiple trappers for the same property: - https://github.com/uBlockOrigin/uBlock-issues/issues/156 - */ - - const trapProp = function (owner, prop, configurable, handler) { - if (handler.init(owner[prop]) === false) { - return; - } - - // - const odesc = Object.getOwnPropertyDescriptor(owner, prop); - let previousGetter; - let previousSetter; - if (odesc instanceof Object) { - if (odesc.configurable === false) { - return; - } - - if (odesc.get instanceof Function) { - previousGetter = odesc.get; - } - - if (odesc.set instanceof Function) { - previousSetter = odesc.set; - } - } - - // - Object.defineProperty(owner, prop, { - configurable, - get() { - if (previousGetter !== undefined) { - previousGetter(); - } - - // - return handler.getter(); - }, - set(a) { - if (previousSetter !== undefined) { - previousSetter(a); - } - - // - handler.setter(a); - }, - }); - }; - - const trapChain = function (owner, chain) { - const pos = chain.indexOf('.'); - if (pos === -1) { - trapProp(owner, chain, false, { - v: undefined, - getter() { - return document.currentScript === thisScript ? this.v : cValue; - }, - setter(a) { - if (mustAbort(a) === false) { - return; - } - - cValue = a; - }, - init(v) { - if (mustAbort(v)) { - return false; - } - - // - this.v = v; - return true; - }, - }); - // - return; - } - - // - const prop = chain.slice(0, pos); - const v = owner[prop]; - // - chain = chain.slice(pos + 1); - if (v instanceof Object || (typeof v === 'object' && v !== null)) { - trapChain(v, chain); - return; - } - - // - trapProp(owner, prop, true, { - v: undefined, - getter() { - return this.v; - }, - setter(a) { - this.v = a; - if (a instanceof Object) { - trapChain(a, chain); - } - }, - init(v) { - this.v = v; - return true; - }, - }); - }; - - // - trapChain(window, chain); - }); -}; diff --git a/src/plugins/adblocker/types/index.ts b/src/plugins/adblocker/types/index.ts deleted file mode 100644 index d96ce664..00000000 --- a/src/plugins/adblocker/types/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const blockers = { - WithBlocklists: 'With blocklists', - InPlayer: 'In player', - AdSpeedup: 'Ad speedup', -} as const; diff --git a/src/plugins/no-google-login/index.ts b/src/plugins/no-google-login/index.ts deleted file mode 100644 index 5a14e9c6..00000000 --- a/src/plugins/no-google-login/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -import style from './style.css?inline'; -import { createPlugin } from '@/utils'; -import { t } from '@/i18n'; - -export default createPlugin({ - name: () => t('plugins.no-google-login.name'), - description: () => t('plugins.no-google-login.description'), - restartNeeded: true, - config: { - enabled: false, - }, - stylesheets: [style], - renderer() { - const elementsToRemove = [ - '.sign-in-link.ytmusic-nav-bar', - '.ytmusic-pivot-bar-renderer[tab-id="FEmusic_liked"]', - ]; - - for (const selector of elementsToRemove) { - const node = document.querySelector(selector); - if (node) { - node.remove(); - } - } - }, -}); diff --git a/src/plugins/no-google-login/style.css b/src/plugins/no-google-login/style.css deleted file mode 100644 index 874f22b5..00000000 --- a/src/plugins/no-google-login/style.css +++ /dev/null @@ -1,6 +0,0 @@ -.ytmusic-pivot-bar-renderer[tab-id='FEmusic_liked'], -ytmusic-guide-signin-promo-renderer, -a[href='/music_premium'], -.sign-in-link { - display: none !important; -}