-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathskipper.ts
121 lines (102 loc) · 3.22 KB
/
skipper.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
(async () => {
const classNameList = ['ytp-ad-skip-button ytp-button', 'ytp-ad-overlay-close-button'];
let observedSkipBtn: Element | undefined;
let skipBtnObserver: MutationObserver | undefined;
let ytdPlayer: HTMLElement | null;
function getElements(classNames: string[]): Element[] {
return classNames
.map(name => Array.from(ytdPlayer!.getElementsByClassName(name)))
.reduce((acc, elems) => acc.concat(elems), []);
}
function getAndClickButtons() {
getElements(classNameList).forEach(button => {
if (!isVisible(button as HTMLElement)) {
observeButton(button);
} else {
triggerClick(button);
}
});
}
function triggerClick(el: Element) {
if (typeof el.dispatchEvent !== 'function') {
return;
}
const evt = new MouseEvent('click', {bubbles: true, cancelable: false});
el.dispatchEvent(evt);
}
/**
* Check the `display: none`
*
* @param el - The element you want to check
*
* @return `true` if it is invisible on the screen; otherwise, `false`
*/
function isVisible(el: HTMLElement): boolean {
return el.offsetParent !== null;
}
/**
* Create a new MutationObserver to monitor the button.
* Once it is visible, it will be clicked.
*
* @param button - The element need to be observed
*/
function observeButton(button: Element) {
if (button === observedSkipBtn) {
return;
}
let parentWithDisplayStyle: HTMLElement | undefined;
for (let curParent: HTMLElement | null = button as HTMLElement; curParent !== null; curParent = curParent.parentElement) {
if (curParent.style.display === 'none') {
parentWithDisplayStyle = curParent;
break;
}
}
if (parentWithDisplayStyle === undefined) {
return;
}
if (skipBtnObserver && observedSkipBtn) {
skipBtnObserver.disconnect();
triggerClick(observedSkipBtn);
} else if (!skipBtnObserver) {
skipBtnObserver = new MutationObserver(() => {
if (observedSkipBtn !== undefined && !isVisible(observedSkipBtn as HTMLElement)) {
return;
}
if (observedSkipBtn !== undefined) {
triggerClick(observedSkipBtn);
}
observedSkipBtn = undefined;
if (skipBtnObserver !== undefined) {
skipBtnObserver.disconnect();
}
});
}
observedSkipBtn = button;
skipBtnObserver.observe(parentWithDisplayStyle, {attributes: true});
}
/**
* Get *ytd-player* element and create a MutationObserver to listen to it.
*
* @return `true` if succeed; otherwise, `false`
*/
function initObserver(): boolean {
ytdPlayer = document.getElementById('ytd-player');
if (ytdPlayer === null) {
return false;
}
const mutObserver = new MutationObserver(() => getAndClickButtons());
mutObserver.observe(ytdPlayer, {childList: true, subtree: true});
return true;
}
function sleep(ms: number): Promise<unknown> {
return new Promise(r => setTimeout(r, ms));
}
const maxRetryCount = 5;
for (let failCount = 0; failCount < maxRetryCount; failCount++) {
if (initObserver()) {
console.debug('MutationObserver initialized successfully.');
break;
}
await sleep(1000);
}
})();