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
[](https://github.com/th-ch/youtube-music/releases/)
[](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;
-}