Skip to content

Commit 31a229e

Browse files
committed
Add adblock and ad speedup
1 parent 1628e4b commit 31a229e

File tree

4 files changed

+169
-3
lines changed

4 files changed

+169
-3
lines changed

electron-builder.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
appId: com.github.th-ch.pear-desktop
2-
productName: Pear Desktop
2+
productName: YouTube Music
33
files:
44
- '!*'
55
- dist

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"name": "pear-music",
33
"desktopName": "com.github.th_ch.pear_music",
4-
"productName": "Pear Desktop",
4+
"productName": "YouTube Music",
55
"version": "3.11.0",
6-
"description": "Pear Desktop App - including custom plugins",
6+
"description": "YouTube Music Desktop App - including custom plugins",
77
"main": "./dist/main/index.js",
88
"type": "module",
99
"license": "MIT",

src/plugins/ad-speedup/index.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { createPlugin } from '@/utils';
2+
import { t } from '@/i18n';
3+
4+
let observer: MutationObserver | null = null;
5+
let lastAdCheck = 0;
6+
let checkInterval: NodeJS.Timeout | null = null;
7+
8+
function checkAndSkipAd() {
9+
// Throttle checks to avoid performance issues
10+
const now = Date.now();
11+
if (now - lastAdCheck < 100) return;
12+
lastAdCheck = now;
13+
14+
const video = document.querySelector<HTMLVideoElement>('video');
15+
if (!video) return;
16+
17+
const adContainer = document.querySelector('.ytp-ad-player-overlay, .video-ads, .ytp-ad-module');
18+
const adText = document.querySelector('.ytp-ad-text, .ytp-ad-preview-text');
19+
20+
// Check if ad is playing
21+
const isAd = adContainer || adText ||
22+
document.querySelector('.ad-showing') ||
23+
document.querySelector('.advertisement');
24+
25+
if (isAd) {
26+
// Mute and speed up the video
27+
if (!video.muted) {
28+
video.muted = true;
29+
}
30+
if (video.playbackRate !== 16) {
31+
video.playbackRate = 16;
32+
}
33+
34+
// Try to click skip button if available
35+
const skipButton = document.querySelector<HTMLElement>(
36+
'.ytp-ad-skip-button, .ytp-ad-skip-button-modern, button.ytp-ad-skip-button'
37+
);
38+
if (skipButton && skipButton.offsetParent !== null) {
39+
skipButton.click();
40+
}
41+
} else {
42+
// Restore normal playback when not an ad
43+
if (video.muted) {
44+
video.muted = false;
45+
}
46+
if (video.playbackRate !== 1) {
47+
video.playbackRate = 1;
48+
}
49+
}
50+
}
51+
52+
export default createPlugin({
53+
name: () => t('plugins.ad-speedup.name'),
54+
description: () => t('plugins.ad-speedup.description'),
55+
restartNeeded: false,
56+
config: {
57+
enabled: true,
58+
},
59+
renderer: {
60+
start() {
61+
// Check for ads periodically
62+
checkInterval = setInterval(() => {
63+
checkAndSkipAd();
64+
}, 500);
65+
66+
// Also watch for DOM changes
67+
observer = new MutationObserver(() => {
68+
checkAndSkipAd();
69+
});
70+
71+
const targetNode = document.body;
72+
if (targetNode) {
73+
observer.observe(targetNode, {
74+
childList: true,
75+
subtree: true,
76+
});
77+
}
78+
},
79+
80+
stop() {
81+
if (observer) {
82+
observer.disconnect();
83+
observer = null;
84+
}
85+
if (checkInterval) {
86+
clearInterval(checkInterval);
87+
checkInterval = null;
88+
}
89+
},
90+
},
91+
});

src/plugins/adblocker/index.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { ElectronBlocker } from '@ghostery/adblocker-electron';
2+
import { session } from 'electron';
3+
4+
import { createPlugin } from '@/utils';
5+
import { t } from '@/i18n';
6+
7+
import type { BackendContext } from '@/types/contexts';
8+
9+
export type AdBlockerPluginConfig = {
10+
enabled: boolean;
11+
cache: boolean;
12+
additionalBlockLists: string[];
13+
};
14+
15+
let blocker: ElectronBlocker | null = null;
16+
17+
const defaultBlockLists = [
18+
'https://easylist.to/easylist/easylist.txt',
19+
'https://easylist.to/easylist/easyprivacy.txt',
20+
'https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt',
21+
'https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/badware.txt',
22+
'https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/privacy.txt',
23+
'https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/resource-abuse.txt',
24+
'https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/unbreak.txt',
25+
'https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/annoyances.txt',
26+
];
27+
28+
export default createPlugin({
29+
name: () => t('plugins.adblocker.name'),
30+
description: () => t('plugins.adblocker.description'),
31+
restartNeeded: false,
32+
config: {
33+
enabled: true,
34+
cache: true,
35+
additionalBlockLists: [],
36+
},
37+
backend: {
38+
async start({ getConfig }: BackendContext<AdBlockerPluginConfig>) {
39+
const config = await getConfig();
40+
const blockLists = [...defaultBlockLists, ...config.additionalBlockLists];
41+
42+
try {
43+
blocker = await ElectronBlocker.fromLists(
44+
fetch,
45+
blockLists,
46+
{
47+
enableCompression: true,
48+
},
49+
);
50+
51+
blocker.enableBlockingInSession(session.defaultSession);
52+
53+
console.log('[AdBlocker] Ad blocker enabled successfully');
54+
} catch (error) {
55+
console.error('[AdBlocker] Failed to initialize ad blocker:', error);
56+
}
57+
},
58+
59+
async onConfigChange(newConfig: AdBlockerPluginConfig) {
60+
if (!newConfig.enabled && blocker) {
61+
blocker.disableBlockingInSession(session.defaultSession);
62+
blocker = null;
63+
console.log('[AdBlocker] Ad blocker disabled');
64+
}
65+
},
66+
67+
stop() {
68+
if (blocker) {
69+
blocker.disableBlockingInSession(session.defaultSession);
70+
blocker = null;
71+
console.log('[AdBlocker] Ad blocker stopped');
72+
}
73+
},
74+
},
75+
});

0 commit comments

Comments
 (0)