Skip to content

Commit 69f560c

Browse files
committed
Implement both blocklists and in-player blocking
1 parent c488c30 commit 69f560c

File tree

5 files changed

+338
-10
lines changed

5 files changed

+338
-10
lines changed

plugins/adblocker/back.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
const { loadAdBlockerEngine } = require("./blocker");
2-
module.exports = (win, options) =>
3-
loadAdBlockerEngine(
4-
win.webContents.session,
5-
options.cache,
6-
options.additionalBlockLists,
7-
options.disableDefaultLists
8-
);
2+
const config = require("./config");
3+
4+
module.exports = async (win, options) => {
5+
if (await config.shouldUseBlocklists()) {
6+
loadAdBlockerEngine(
7+
win.webContents.session,
8+
options.cache,
9+
options.additionalBlockLists,
10+
options.disableDefaultLists,
11+
);
12+
}
13+
};

plugins/adblocker/config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const { PluginConfig } = require("../../config/dynamic");
2+
3+
const config = new PluginConfig("adblocker", { enableFront: true });
4+
5+
const blockers = {
6+
WithBlocklists: "With blocklists",
7+
InPlayer: "In player",
8+
};
9+
10+
const shouldUseBlocklists = async () =>
11+
(await config.get("blocker")) !== blockers.InPlayer;
12+
13+
module.exports = { shouldUseBlocklists, blockers, ...config };

plugins/adblocker/inject.js

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
// Source: https://addons.mozilla.org/en-US/firefox/addon/adblock-for-youtube/
2+
// https://robwu.nl/crxviewer/?crx=https%3A%2F%2Faddons.mozilla.org%2Fen-US%2Ffirefox%2Faddon%2Fadblock-for-youtube%2F
3+
4+
/*
5+
Parts of this code is derived from set-constant.js:
6+
https://github.com/gorhill/uBlock/blob/5de0ce975753b7565759ac40983d31978d1f84ca/assets/resources/scriptlets.js#L704
7+
*/
8+
9+
{
10+
let pruner = function (o) {
11+
delete o.playerAds;
12+
delete o.adPlacements;
13+
//
14+
if (o.playerResponse) {
15+
delete o.playerResponse.playerAds;
16+
delete o.playerResponse.adPlacements;
17+
}
18+
//
19+
return o;
20+
};
21+
22+
JSON.parse = new Proxy(JSON.parse, {
23+
apply: function () {
24+
return pruner(Reflect.apply(...arguments));
25+
},
26+
});
27+
28+
Response.prototype.json = new Proxy(Response.prototype.json, {
29+
apply: function () {
30+
return Reflect.apply(...arguments).then((o) => pruner(o));
31+
},
32+
});
33+
}
34+
35+
(function () {
36+
let cValue = "undefined";
37+
const chain = "playerResponse.adPlacements";
38+
const thisScript = document.currentScript;
39+
//
40+
if (cValue === "null") cValue = null;
41+
else if (cValue === "''") cValue = "";
42+
else if (cValue === "true") cValue = true;
43+
else if (cValue === "false") cValue = false;
44+
else if (cValue === "undefined") cValue = undefined;
45+
else if (cValue === "noopFunc") cValue = function () {};
46+
else if (cValue === "trueFunc")
47+
cValue = function () {
48+
return true;
49+
};
50+
else if (cValue === "falseFunc")
51+
cValue = function () {
52+
return false;
53+
};
54+
else if (/^\d+$/.test(cValue)) {
55+
cValue = parseFloat(cValue);
56+
//
57+
if (isNaN(cValue)) return;
58+
if (Math.abs(cValue) > 0x7fff) return;
59+
} else {
60+
return;
61+
}
62+
//
63+
let aborted = false;
64+
const mustAbort = function (v) {
65+
if (aborted) return true;
66+
aborted =
67+
v !== undefined &&
68+
v !== null &&
69+
cValue !== undefined &&
70+
cValue !== null &&
71+
typeof v !== typeof cValue;
72+
return aborted;
73+
};
74+
75+
/*
76+
Support multiple trappers for the same property:
77+
https://github.com/uBlockOrigin/uBlock-issues/issues/156
78+
*/
79+
80+
const trapProp = function (owner, prop, configurable, handler) {
81+
if (handler.init(owner[prop]) === false) {
82+
return;
83+
}
84+
//
85+
const odesc = Object.getOwnPropertyDescriptor(owner, prop);
86+
let prevGetter, prevSetter;
87+
if (odesc instanceof Object) {
88+
if (odesc.configurable === false) return;
89+
if (odesc.get instanceof Function) prevGetter = odesc.get;
90+
if (odesc.set instanceof Function) prevSetter = odesc.set;
91+
}
92+
//
93+
Object.defineProperty(owner, prop, {
94+
configurable,
95+
get() {
96+
if (prevGetter !== undefined) {
97+
prevGetter();
98+
}
99+
//
100+
return handler.getter();
101+
},
102+
set(a) {
103+
if (prevSetter !== undefined) {
104+
prevSetter(a);
105+
}
106+
//
107+
handler.setter(a);
108+
},
109+
});
110+
};
111+
112+
const trapChain = function (owner, chain) {
113+
const pos = chain.indexOf(".");
114+
if (pos === -1) {
115+
trapProp(owner, chain, false, {
116+
v: undefined,
117+
getter: function () {
118+
return document.currentScript === thisScript ? this.v : cValue;
119+
},
120+
setter: function (a) {
121+
if (mustAbort(a) === false) return;
122+
cValue = a;
123+
},
124+
init: function (v) {
125+
if (mustAbort(v)) return false;
126+
//
127+
this.v = v;
128+
return true;
129+
},
130+
});
131+
//
132+
return;
133+
}
134+
//
135+
const prop = chain.slice(0, pos);
136+
const v = owner[prop];
137+
//
138+
chain = chain.slice(pos + 1);
139+
if (v instanceof Object || (typeof v === "object" && v !== null)) {
140+
trapChain(v, chain);
141+
return;
142+
}
143+
//
144+
trapProp(owner, prop, true, {
145+
v: undefined,
146+
getter: function () {
147+
return this.v;
148+
},
149+
setter: function (a) {
150+
this.v = a;
151+
if (a instanceof Object) trapChain(a, chain);
152+
},
153+
init: function (v) {
154+
this.v = v;
155+
return true;
156+
},
157+
});
158+
};
159+
//
160+
trapChain(window, chain);
161+
})();
162+
163+
(function () {
164+
let cValue = "undefined";
165+
const thisScript = document.currentScript;
166+
const chain = "ytInitialPlayerResponse.adPlacements";
167+
//
168+
if (cValue === "null") cValue = null;
169+
else if (cValue === "''") cValue = "";
170+
else if (cValue === "true") cValue = true;
171+
else if (cValue === "false") cValue = false;
172+
else if (cValue === "undefined") cValue = undefined;
173+
else if (cValue === "noopFunc") cValue = function () {};
174+
else if (cValue === "trueFunc")
175+
cValue = function () {
176+
return true;
177+
};
178+
else if (cValue === "falseFunc")
179+
cValue = function () {
180+
return false;
181+
};
182+
else if (/^\d+$/.test(cValue)) {
183+
cValue = parseFloat(cValue);
184+
//
185+
if (isNaN(cValue)) return;
186+
if (Math.abs(cValue) > 0x7fff) return;
187+
} else {
188+
return;
189+
}
190+
//
191+
let aborted = false;
192+
const mustAbort = function (v) {
193+
if (aborted) return true;
194+
aborted =
195+
v !== undefined &&
196+
v !== null &&
197+
cValue !== undefined &&
198+
cValue !== null &&
199+
typeof v !== typeof cValue;
200+
return aborted;
201+
};
202+
203+
/*
204+
Support multiple trappers for the same property:
205+
https://github.com/uBlockOrigin/uBlock-issues/issues/156
206+
*/
207+
208+
const trapProp = function (owner, prop, configurable, handler) {
209+
if (handler.init(owner[prop]) === false) {
210+
return;
211+
}
212+
//
213+
const odesc = Object.getOwnPropertyDescriptor(owner, prop);
214+
let prevGetter, prevSetter;
215+
if (odesc instanceof Object) {
216+
if (odesc.configurable === false) return;
217+
if (odesc.get instanceof Function) prevGetter = odesc.get;
218+
if (odesc.set instanceof Function) prevSetter = odesc.set;
219+
}
220+
//
221+
Object.defineProperty(owner, prop, {
222+
configurable,
223+
get() {
224+
if (prevGetter !== undefined) {
225+
prevGetter();
226+
}
227+
//
228+
return handler.getter();
229+
},
230+
set(a) {
231+
if (prevSetter !== undefined) {
232+
prevSetter(a);
233+
}
234+
//
235+
handler.setter(a);
236+
},
237+
});
238+
};
239+
240+
const trapChain = function (owner, chain) {
241+
const pos = chain.indexOf(".");
242+
if (pos === -1) {
243+
trapProp(owner, chain, false, {
244+
v: undefined,
245+
getter: function () {
246+
return document.currentScript === thisScript ? this.v : cValue;
247+
},
248+
setter: function (a) {
249+
if (mustAbort(a) === false) return;
250+
cValue = a;
251+
},
252+
init: function (v) {
253+
if (mustAbort(v)) return false;
254+
//
255+
this.v = v;
256+
return true;
257+
},
258+
});
259+
//
260+
return;
261+
}
262+
//
263+
const prop = chain.slice(0, pos);
264+
const v = owner[prop];
265+
//
266+
chain = chain.slice(pos + 1);
267+
if (v instanceof Object || (typeof v === "object" && v !== null)) {
268+
trapChain(v, chain);
269+
return;
270+
}
271+
//
272+
trapProp(owner, prop, true, {
273+
v: undefined,
274+
getter: function () {
275+
return this.v;
276+
},
277+
setter: function (a) {
278+
this.v = a;
279+
if (a instanceof Object) trapChain(a, chain);
280+
},
281+
init: function (v) {
282+
this.v = v;
283+
return true;
284+
},
285+
});
286+
};
287+
//
288+
trapChain(window, chain);
289+
})();

plugins/adblocker/menu.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const config = require("./config");
2+
3+
module.exports = () => [
4+
{
5+
label: "Blocker",
6+
submenu: Object.values(config.blockers).map((blocker) => ({
7+
label: blocker,
8+
type: "radio",
9+
checked: config.get("blocker") === blocker,
10+
click: () => {
11+
config.set("blocker", blocker);
12+
},
13+
})),
14+
},
15+
];

plugins/adblocker/preload.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
module.exports = () => {
2-
// Preload adblocker to inject scripts/styles
3-
require("@cliqz/adblocker-electron-preload");
1+
const config = require("./config");
2+
3+
module.exports = async () => {
4+
if (await config.shouldUseBlocklists()) {
5+
// Preload adblocker to inject scripts/styles
6+
require("@cliqz/adblocker-electron-preload");
7+
} else if ((await config.get("blocker")) === config.blockers.InPlayer) {
8+
require("./inject");
9+
}
410
};

0 commit comments

Comments
 (0)