mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-10 18:21:47 +00:00
REMOVE adblocker AND no-google-login, and renaming
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
# YouTube Music
|
# YTMD
|
||||||
|
|
||||||
[](https://github.com/th-ch/youtube-music/releases/)
|
[](https://github.com/th-ch/youtube-music/releases/)
|
||||||
[](https://github.com/th-ch/youtube-music/blob/master/license)
|
[](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 ...
|
- And more ...
|
||||||
|
|
||||||
## Available plugins:
|
## 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 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
|
- **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
|
- **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
|
- **Notifications**: Display a notification when a song starts
|
||||||
playing ([interactive notifications](https://user-images.githubusercontent.com/78568641/114102651-63ce0e00-98d0-11eb-9dfe-c5a02bb54f9c.png)
|
playing ([interactive notifications](https://user-images.githubusercontent.com/78568641/114102651-63ce0e00-98d0-11eb-9dfe-c5a02bb54f9c.png)
|
||||||
are available on windows)
|
are available on windows)
|
||||||
|
|||||||
1
src/plugins/adblocker/.gitignore
vendored
1
src/plugins/adblocker/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/ad-blocker-engine.bin
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
function skipAd(target: Element) {
|
|
||||||
const skipButton = target.querySelector<HTMLButtonElement>(
|
|
||||||
'button.ytp-ad-skip-button-modern',
|
|
||||||
);
|
|
||||||
if (skipButton) {
|
|
||||||
skipButton.click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function speedUpAndMute(player: Element, isAdShowing: boolean) {
|
|
||||||
const video = player.querySelector<HTMLVideoElement>('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<HTMLVideoElement>('#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);
|
|
||||||
};
|
|
||||||
@ -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);
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export default async () => {
|
|
||||||
await import('@ghostery/adblocker-electron-preload');
|
|
||||||
};
|
|
||||||
5
src/plugins/adblocker/injectors/inject.d.ts
vendored
5
src/plugins/adblocker/injectors/inject.d.ts
vendored
@ -1,5 +0,0 @@
|
|||||||
import type { ContextBridge } from 'electron';
|
|
||||||
|
|
||||||
export const inject: (contextBridge: ContextBridge) => void;
|
|
||||||
|
|
||||||
export const isInjected: () => boolean;
|
|
||||||
@ -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);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
export const blockers = {
|
|
||||||
WithBlocklists: 'With blocklists',
|
|
||||||
InPlayer: 'In player',
|
|
||||||
AdSpeedup: 'Ad speedup',
|
|
||||||
} as const;
|
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user