Skip to content

Commit 3101b42

Browse files
committed
Media control params and other tweaks
1 parent 2b941c7 commit 3101b42

File tree

5 files changed

+146
-124
lines changed

5 files changed

+146
-124
lines changed

injected/src/features/duck-player-native.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { initDuckPlayerNative } from './duckplayer-native/duckplayer-native.js';
1111
export class DuckPlayerNativeFeature extends ContentFeature {
1212
init(args) {
1313
console.log('LOADING DUCK PLAYER NATIVE SCRIPTS', args);
14+
console.log('Duck Player Native Feature', args?.bundledConfig?.features?.duckPlayerNative);
1415

1516
/**
1617
* This feature never operates in a frame

injected/src/features/duckplayer-native/duckplayer-native.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ export async function initDuckPlayerNative(messages) {
3232
// getCurrentTimestamp();
3333
// });
3434

35-
messages.onMediaControl(() => {
36-
console.log('MEDIA CONTROL');
37-
mediaControl();
35+
messages.onMediaControl(({ pause }) => {
36+
console.log('MEDIA CONTROL', pause);
37+
mediaControl(pause);
3838
});
3939

4040
messages.onMuteAudio(({ mute }) => {

injected/src/features/duckplayer-native/error-detection.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,10 @@ export class ErrorDetection {
6262
mutation.addedNodes.forEach((node) => {
6363
if (this.checkForError(node)) {
6464
console.log('A node with an error has been added to the document:', node);
65-
const error = this.getErrorType();
66-
this.messages.onYoutubeError(error);
65+
setTimeout(() => {
66+
const error = this.getErrorType();
67+
this.messages.onYoutubeError(error);
68+
}, 4000);
6769
}
6870
});
6971
}
@@ -78,8 +80,17 @@ export class ErrorDetection {
7880
const currentWindow = /** @type {Window & typeof globalThis & { ytcfg: object }} */ (window);
7981
let playerResponse;
8082

83+
while (!currentWindow.ytcfg) {
84+
console.log('Waiting for ytcfg');
85+
}
86+
87+
console.log('Got ytcfg', currentWindow.ytcfg);
88+
8189
try {
82-
playerResponse = JSON.parse(currentWindow.ytcfg?.get('PLAYER_VARS')?.embedded_player_response);
90+
const playerResponseJSON = currentWindow.ytcfg?.get('PLAYER_VARS')?.embedded_player_response;
91+
console.log("Player response", playerResponseJSON);
92+
93+
playerResponse = JSON.parse(playerResponseJSON);
8394
} catch (e) {
8495
console.log('Could not parse player response', e);
8596
}
@@ -93,16 +104,19 @@ export class ErrorDetection {
93104
if (status === 'UNPLAYABLE') {
94105
// 1.1. Check for presence of desktopLegacyAgeGateReason
95106
if (desktopLegacyAgeGateReason === 1) {
107+
console.log('AGE RESTRICTED ERROR');
96108
return YOUTUBE_ERRORS.ageRestricted;
97109
}
98110

99111
// 1.2. Fall back to embed not allowed error
112+
console.log('NO EMBED ERROR');
100113
return YOUTUBE_ERRORS.noEmbed;
101114
}
102115

103116
// 2. Check for sign-in support link
104117
try {
105118
if (this.settings?.signInRequiredSelector && !!document.querySelector(this.settings.signInRequiredSelector)) {
119+
console.log('SIGN-IN ERROR');
106120
return YOUTUBE_ERRORS.signInRequired;
107121
}
108122
} catch (e) {
@@ -111,6 +125,7 @@ export class ErrorDetection {
111125
}
112126

113127
// 3. Fall back to unknown error
128+
console.log('UNKNOWN ERROR');
114129
return YOUTUBE_ERRORS.unknown;
115130
}
116131

Lines changed: 117 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
22
// @ts-nocheck - Typing will be fixed in the future
33

4-
export function mediaControl() {
4+
/**
5+
*
6+
* @param {boolean} pause
7+
*/
8+
export function mediaControl(pause) {
59
// Initialize state if not exists
610
if (!window._mediaControlState) {
11+
console.log('Creating control state');
712
window._mediaControlState = {
813
observer: null,
914
userInitiated: false,
@@ -16,132 +21,128 @@ export function mediaControl() {
1621

1722
// Block playback handler
1823
const blockPlayback = function (event) {
24+
console.log('Blocking playback handler');
1925
event.preventDefault();
2026
event.stopPropagation();
2127
return false;
2228
};
2329

24-
// The actual media control function
25-
function mediaControl(pause) {
26-
state.isPaused = pause;
27-
28-
if (pause) {
29-
// Capture play events at the earliest possible moment
30-
document.addEventListener('play', blockPlayback, true);
31-
document.addEventListener('playing', blockPlayback, true);
32-
33-
// Block HTML5 video/audio playback methods
34-
HTMLMediaElement.prototype.play = function () {
35-
this.pause();
36-
return Promise.reject(new Error('Playback blocked'));
37-
};
38-
39-
// Override load to ensure media starts paused
40-
HTMLMediaElement.prototype.load = function () {
41-
this.autoplay = false;
42-
this.pause();
43-
return state.originalLoad.apply(this, arguments);
44-
};
45-
46-
// Listen for user interactions that may lead to playback
47-
document.addEventListener(
48-
'touchstart',
49-
() => {
50-
state.userInitiated = true;
51-
52-
// Remove the early blocking listeners
53-
document.removeEventListener('play', blockPlayback, true);
54-
document.removeEventListener('playing', blockPlayback, true);
55-
56-
// Reset HTMLMediaElement.prototype.play
57-
HTMLMediaElement.prototype.play = state.originalPlay;
58-
59-
// Unmute all media elements when user interacts
60-
document.querySelectorAll('audio, video').forEach((media) => {
61-
media.muted = false;
62-
});
63-
64-
// Reset after a short delay
65-
setTimeout(() => {
66-
state.userInitiated = false;
67-
68-
// Re-add blocking if still in paused state
69-
if (state.isPaused) {
70-
document.addEventListener('play', blockPlayback, true);
71-
document.addEventListener('playing', blockPlayback, true);
72-
HTMLMediaElement.prototype.play = function () {
73-
this.pause();
74-
return Promise.reject(new Error('Playback blocked'));
75-
};
76-
}
77-
}, 500);
78-
},
79-
true,
80-
);
81-
82-
// Initial pause of all media
83-
document.querySelectorAll('audio, video').forEach((media) => {
84-
media.pause();
85-
media.muted = true;
86-
media.autoplay = false;
87-
});
30+
console.log('Running media control with pause: ', pause);
31+
state.isPaused = pause;
32+
33+
if (pause) {
34+
// Capture play events at the earliest possible moment
35+
document.addEventListener('play', blockPlayback, true);
36+
document.addEventListener('playing', blockPlayback, true);
37+
38+
// Block HTML5 video/audio playback methods
39+
HTMLMediaElement.prototype.play = function () {
40+
this.pause();
41+
return Promise.reject(new Error('Playback blocked'));
42+
};
43+
44+
// Override load to ensure media starts paused
45+
HTMLMediaElement.prototype.load = function () {
46+
this.autoplay = false;
47+
this.pause();
48+
return state.originalLoad.apply(this, arguments);
49+
};
50+
51+
// Listen for user interactions that may lead to playback
52+
document.addEventListener(
53+
'touchstart',
54+
() => {
55+
state.userInitiated = true;
56+
57+
// Remove the early blocking listeners
58+
document.removeEventListener('play', blockPlayback, true);
59+
document.removeEventListener('playing', blockPlayback, true);
60+
61+
// Reset HTMLMediaElement.prototype.play
62+
HTMLMediaElement.prototype.play = state.originalPlay;
8863

89-
// Monitor DOM for newly added media elements
90-
if (state.observer) {
91-
state.observer.disconnect();
92-
}
64+
// Unmute all media elements when user interacts
65+
document.querySelectorAll('audio, video').forEach((media) => {
66+
media.muted = false;
67+
});
68+
69+
// Reset after a short delay
70+
setTimeout(() => {
71+
state.userInitiated = false;
72+
73+
// Re-add blocking if still in paused state
74+
if (state.isPaused) {
75+
document.addEventListener('play', blockPlayback, true);
76+
document.addEventListener('playing', blockPlayback, true);
77+
HTMLMediaElement.prototype.play = function () {
78+
this.pause();
79+
return Promise.reject(new Error('Playback blocked'));
80+
};
81+
}
82+
}, 500);
83+
},
84+
true,
85+
);
86+
87+
// Initial pause of all media
88+
document.querySelectorAll('audio, video').forEach((media) => {
89+
media.pause();
90+
media.muted = true;
91+
media.autoplay = false;
92+
});
93+
94+
// Monitor DOM for newly added media elements
95+
if (state.observer) {
96+
state.observer.disconnect();
97+
}
9398

94-
state.observer = new MutationObserver((mutations) => {
95-
mutations.forEach((mutation) => {
96-
// Check for added nodes
97-
mutation.addedNodes.forEach((node) => {
98-
if (node.tagName === 'VIDEO' || node.tagName === 'AUDIO') {
99+
state.observer = new MutationObserver((mutations) => {
100+
mutations.forEach((mutation) => {
101+
// Check for added nodes
102+
mutation.addedNodes.forEach((node) => {
103+
if (node.tagName === 'VIDEO' || node.tagName === 'AUDIO') {
104+
if (!state.userInitiated) {
105+
node.pause();
106+
node.muted = true;
107+
node.autoplay = false;
108+
}
109+
} else if (node.querySelectorAll) {
110+
node.querySelectorAll('audio, video').forEach((media) => {
99111
if (!state.userInitiated) {
100-
node.pause();
101-
node.muted = true;
102-
node.autoplay = false;
112+
media.pause();
113+
media.muted = true;
114+
media.autoplay = false;
103115
}
104-
} else if (node.querySelectorAll) {
105-
node.querySelectorAll('audio, video').forEach((media) => {
106-
if (!state.userInitiated) {
107-
media.pause();
108-
media.muted = true;
109-
media.autoplay = false;
110-
}
111-
});
112-
}
113-
});
116+
});
117+
}
114118
});
115119
});
116-
117-
state.observer.observe(document.documentElement || document.body, {
118-
childList: true,
119-
subtree: true,
120-
attributes: true,
121-
attributeFilter: ['autoplay', 'src', 'playing'],
122-
});
123-
} else {
124-
// Restore original methods
125-
HTMLMediaElement.prototype.play = state.originalPlay;
126-
HTMLMediaElement.prototype.load = state.originalLoad;
127-
128-
// Remove listeners
129-
document.removeEventListener('play', blockPlayback, true);
130-
document.removeEventListener('playing', blockPlayback, true);
131-
132-
// Clean up observer
133-
if (state.observer) {
134-
state.observer.disconnect();
135-
state.observer = null;
136-
}
137-
138-
// Unmute all media
139-
document.querySelectorAll('audio, video').forEach((media) => {
140-
media.muted = false;
141-
});
120+
});
121+
122+
state.observer.observe(document.documentElement || document.body, {
123+
childList: true,
124+
subtree: true,
125+
attributes: true,
126+
attributeFilter: ['autoplay', 'src', 'playing'],
127+
});
128+
} else {
129+
// Restore original methods
130+
HTMLMediaElement.prototype.play = state.originalPlay;
131+
HTMLMediaElement.prototype.load = state.originalLoad;
132+
133+
// Remove listeners
134+
document.removeEventListener('play', blockPlayback, true);
135+
document.removeEventListener('playing', blockPlayback, true);
136+
137+
// Clean up observer
138+
if (state.observer) {
139+
state.observer.disconnect();
140+
state.observer = null;
142141
}
143-
}
144142

145-
// Export function
146-
window.mediaControl = mediaControl;
143+
// Unmute all media
144+
document.querySelectorAll('audio, video').forEach((media) => {
145+
media.muted = false;
146+
});
147+
}
147148
}

injected/src/features/duckplayer-native/native-messages.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import * as constants from './constants.js';
22

33
/**
4-
* @typedef {object} muteSettings - Settings passing to the onMute callback
4+
* @typedef {object} muteSettings - Settings passed to the onMute callback
55
* @property {boolean} mute - Set to true to mute the video, false to unmute
66
*/
77

8+
/**
9+
* @typedef {object} mediaControlSettings - Settings passed to the onMediaControll callback
10+
* @property {boolean} pause - Set to true to pause the video, false to play
11+
*/
12+
813
/**
914
* @typedef {import("@duckduckgo/messaging").Messaging} Messaging
1015
*
@@ -52,7 +57,7 @@ export class DuckPlayerNativeMessages {
5257

5358
/**
5459
* Subscribe to media control events
55-
* @param {() => void} callback
60+
* @param {(mediaControlSettings: mediaControlSettings) => void} callback
5661
*/
5762
onMediaControl(callback) {
5863
console.log('Subscribing to onMediaControl');

0 commit comments

Comments
 (0)