Skip to content

Commit 30058de

Browse files
committed
fix: prevent initial autoplay when disabled
1 parent 0a0afb1 commit 30058de

2 files changed

Lines changed: 124 additions & 0 deletions

File tree

js&css/web-accessible/core.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,28 @@ var ImprovedTube = {
9393
defaultApiKey: 'AIzaSyCXRRCFwKAXOiF1JkUBmibzxJF1cPuKNwA'
9494
};
9595

96+
ImprovedTube.syncAutoplayDisableLocalStorage = function () {
97+
if (ImprovedTube.storage.player_autoplay_disable === true) {
98+
localStorage['it-player-autoplay-disable'] = 'true';
99+
} else {
100+
localStorage.removeItem('it-player-autoplay-disable');
101+
}
102+
};
103+
104+
ImprovedTube.shouldPreventInitialAutoplay = function (video) {
105+
if (ImprovedTube.user_interacted || !location.href.includes('/watch?') || location.href.includes('list=')) {
106+
return false;
107+
}
108+
109+
if (localStorage['it-player-autoplay-disable'] !== 'true' && ImprovedTube.storage.player_autoplay_disable !== true) {
110+
return false;
111+
}
112+
113+
var player = video.closest && (video.closest('.html5-video-player') || video.closest('#movie_player'));
114+
115+
return !player || !player.classList || !player.classList.contains('ad-showing');
116+
};
117+
96118
/*--------------------------------------------------------------
97119
CODEC || 30FPS
98120
----------------------------------------------------------------
@@ -125,6 +147,29 @@ if (localStorage['it-codec'] || localStorage['it-player30fps']) {
125147
}
126148
};
127149

150+
HTMLMediaElement.prototype.play = (function (original) {
151+
if (original.improvedTubeInitialAutoplayGuard) {
152+
return original;
153+
}
154+
155+
function play() {
156+
if (ImprovedTube.shouldPreventInitialAutoplay(this)) {
157+
try {
158+
this.pause();
159+
} catch (error) {
160+
}
161+
162+
return Promise.resolve();
163+
}
164+
165+
return original.apply(this, arguments);
166+
}
167+
168+
play.improvedTubeInitialAutoplayGuard = true;
169+
170+
return play;
171+
})(HTMLMediaElement.prototype.play);
172+
128173
/*--------------------------------------------------------------
129174
# MESSAGES
130175
----------------------------------------------------------------
@@ -182,6 +227,7 @@ document.addEventListener('it-message-from-extension', function () {
182227

183228
if (message.action === 'storage-loaded') {
184229
ImprovedTube.storage = message.storage;
230+
ImprovedTube.syncAutoplayDisableLocalStorage();
185231

186232
if (ImprovedTube.storage.block_vp9 || ImprovedTube.storage.block_av1 || ImprovedTube.storage.block_h264) {
187233
let atlas = { block_vp9: 'vp9|vp09', block_h264: 'avc1', block_av1: 'av01' },
@@ -233,6 +279,9 @@ document.addEventListener('it-message-from-extension', function () {
233279
localStorage.removeItem('it-player30fps');
234280
}
235281
}
282+
if (message.key === 'player_autoplay_disable') {
283+
ImprovedTube.syncAutoplayDisableLocalStorage();
284+
}
236285
switch (camelized_key) {
237286
case 'blocklist':
238287
case 'blocklistActivate':
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
describe('initial autoplay guard', () => {
2+
let originalPlay;
3+
let video;
4+
5+
function loadCore() {
6+
jest.resetModules();
7+
8+
const storage = {};
9+
storage.removeItem = key => {
10+
delete storage[key];
11+
};
12+
13+
global.localStorage = storage;
14+
global.location = {
15+
href: 'https://www.youtube.com/watch?v=abcdefghijk'
16+
};
17+
global.window = {};
18+
global.CustomEvent = function CustomEvent(type) {
19+
this.type = type;
20+
};
21+
global.document = {
22+
addEventListener: jest.fn(),
23+
createElement: jest.fn(() => ({style: {}})),
24+
dispatchEvent: jest.fn(),
25+
documentElement: {
26+
appendChild: jest.fn()
27+
},
28+
querySelector: jest.fn()
29+
};
30+
31+
originalPlay = jest.fn(() => 'played');
32+
global.HTMLMediaElement = function HTMLMediaElement() {};
33+
global.HTMLMediaElement.prototype.play = originalPlay;
34+
35+
require('../../js&css/web-accessible/core.js');
36+
37+
video = {
38+
closest: jest.fn(() => ({
39+
classList: {
40+
contains: jest.fn(() => false)
41+
}
42+
})),
43+
pause: jest.fn()
44+
};
45+
}
46+
47+
beforeEach(() => {
48+
loadCore();
49+
});
50+
51+
afterEach(() => {
52+
delete global.CustomEvent;
53+
delete global.document;
54+
delete global.HTMLMediaElement;
55+
delete global.localStorage;
56+
delete global.location;
57+
delete global.window;
58+
});
59+
60+
test('prevents the first direct watch-page play when autoplay is disabled', async () => {
61+
localStorage['it-player-autoplay-disable'] = 'true';
62+
63+
await expect(HTMLMediaElement.prototype.play.call(video)).resolves.toBeUndefined();
64+
65+
expect(video.pause).toHaveBeenCalledTimes(1);
66+
expect(originalPlay).not.toHaveBeenCalled();
67+
});
68+
69+
test('allows playback when autoplay is not disabled', () => {
70+
expect(HTMLMediaElement.prototype.play.call(video)).toBe('played');
71+
72+
expect(video.pause).not.toHaveBeenCalled();
73+
expect(originalPlay).toHaveBeenCalledTimes(1);
74+
});
75+
});

0 commit comments

Comments
 (0)