mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-01-21 15:52:58 -05:00
236 lines
9.5 KiB
JavaScript
236 lines
9.5 KiB
JavaScript
export class AutoplayHandler {
|
|
constructor(player, mediaData, userPreferences) {
|
|
this.player = player;
|
|
this.mediaData = mediaData;
|
|
this.userPreferences = userPreferences;
|
|
this.isFirefox = this.detectFirefox();
|
|
}
|
|
|
|
detectFirefox() {
|
|
return (
|
|
typeof navigator !== 'undefined' &&
|
|
navigator.userAgent &&
|
|
navigator.userAgent.toLowerCase().indexOf('firefox') > -1
|
|
);
|
|
}
|
|
|
|
hasUserInteracted() {
|
|
// Firefox-specific user interaction detection
|
|
if (this.isFirefox) {
|
|
return (
|
|
// Check if user has explicitly interacted
|
|
sessionStorage.getItem('userInteracted') === 'true' ||
|
|
// Firefox-specific: Check if document has been clicked/touched
|
|
sessionStorage.getItem('firefoxUserGesture') === 'true' ||
|
|
// More reliable focus check for Firefox
|
|
(document.hasFocus() && document.visibilityState === 'visible') ||
|
|
// Check if any user event has been registered
|
|
this.checkFirefoxUserGesture()
|
|
);
|
|
}
|
|
|
|
// Original detection for other browsers
|
|
return (
|
|
document.hasFocus() ||
|
|
document.visibilityState === 'visible' ||
|
|
sessionStorage.getItem('userInteracted') === 'true'
|
|
);
|
|
}
|
|
|
|
checkFirefoxUserGesture() {
|
|
// Firefox requires actual user gesture for autoplay
|
|
// This checks if we've detected any user interaction events
|
|
try {
|
|
const hasGesture = document.createElement('video').play();
|
|
return hasGesture && typeof hasGesture.then === 'function';
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async handleAutoplay() {
|
|
// Don't attempt autoplay if already playing or loading
|
|
if (!this.player.paused() || this.player.seeking()) {
|
|
return;
|
|
}
|
|
|
|
// Firefox-specific delay to ensure player is ready
|
|
if (this.isFirefox) {
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
}
|
|
|
|
// Define variables outside try block so they're accessible in catch
|
|
const userInteracted = this.hasUserInteracted();
|
|
const savedMuteState = this.userPreferences.getPreference('muted');
|
|
|
|
try {
|
|
// Firefox-specific: Always start muted if no user interaction
|
|
if (this.isFirefox && !userInteracted) {
|
|
this.player.muted(true);
|
|
} else if (!this.mediaData.urlMuted && userInteracted && savedMuteState !== true) {
|
|
this.player.muted(false);
|
|
}
|
|
|
|
// First attempt: try to play with current mute state
|
|
const playPromise = this.player.play();
|
|
|
|
// Firefox-specific promise handling
|
|
if (this.isFirefox && playPromise && typeof playPromise.then === 'function') {
|
|
await playPromise;
|
|
} else if (playPromise) {
|
|
await playPromise;
|
|
}
|
|
} catch (error) {
|
|
// Firefox-specific error handling
|
|
if (this.isFirefox) {
|
|
await this.handleFirefoxAutoplayError(error, userInteracted, savedMuteState);
|
|
} else {
|
|
// Fallback to muted autoplay unless user explicitly wants to stay unmuted
|
|
if (!this.player.muted()) {
|
|
try {
|
|
this.player.muted(true);
|
|
await this.player.play();
|
|
|
|
// Only try to restore sound if user hasn't explicitly saved mute=true
|
|
if (savedMuteState !== true) {
|
|
this.restoreSound(userInteracted);
|
|
}
|
|
} catch {
|
|
// console.error('❌ Even muted autoplay was blocked:', mutedError.message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async handleFirefoxAutoplayError(error, userInteracted, savedMuteState) {
|
|
// Firefox requires muted autoplay in most cases
|
|
if (!this.player.muted()) {
|
|
try {
|
|
this.player.muted(true);
|
|
|
|
// Add a small delay for Firefox
|
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
|
|
const mutedPlayPromise = this.player.play();
|
|
if (mutedPlayPromise && typeof mutedPlayPromise.then === 'function') {
|
|
await mutedPlayPromise;
|
|
}
|
|
|
|
// Only try to restore sound if user hasn't explicitly saved mute=true
|
|
if (savedMuteState !== true) {
|
|
this.restoreSound(userInteracted);
|
|
}
|
|
} catch {
|
|
// Even muted autoplay failed - set up user interaction listeners
|
|
this.setupFirefoxInteractionListeners();
|
|
}
|
|
} else {
|
|
// Already muted but still failed - set up interaction listeners
|
|
this.setupFirefoxInteractionListeners();
|
|
}
|
|
}
|
|
|
|
setupFirefoxInteractionListeners() {
|
|
if (!this.isFirefox) return;
|
|
|
|
const enablePlayback = async () => {
|
|
try {
|
|
sessionStorage.setItem('firefoxUserGesture', 'true');
|
|
sessionStorage.setItem('userInteracted', 'true');
|
|
|
|
if (this.player && !this.player.isDisposed() && this.player.paused()) {
|
|
const playPromise = this.player.play();
|
|
if (playPromise && typeof playPromise.then === 'function') {
|
|
await playPromise;
|
|
}
|
|
}
|
|
|
|
// Remove listeners after successful interaction
|
|
document.removeEventListener('click', enablePlayback);
|
|
document.removeEventListener('keydown', enablePlayback);
|
|
document.removeEventListener('touchstart', enablePlayback);
|
|
} catch {
|
|
// Interaction still didn't work, keep listeners active
|
|
}
|
|
};
|
|
|
|
// Set up interaction listeners for Firefox
|
|
document.addEventListener('click', enablePlayback, { once: true });
|
|
document.addEventListener('keydown', enablePlayback, { once: true });
|
|
document.addEventListener('touchstart', enablePlayback, { once: true });
|
|
|
|
// Show Firefox-specific notification
|
|
if (this.player && !this.player.isDisposed()) {
|
|
this.player.trigger('notify', '🦊 Firefox: Click to enable playback');
|
|
}
|
|
}
|
|
|
|
restoreSound(userInteracted) {
|
|
const restoreSound = () => {
|
|
if (this.player && !this.player.isDisposed()) {
|
|
this.player.muted(false);
|
|
this.player.trigger('notify', '🔊 Sound enabled!');
|
|
}
|
|
};
|
|
|
|
// Firefox-specific sound restoration
|
|
if (this.isFirefox) {
|
|
// Firefox needs more time and user interaction verification
|
|
if (userInteracted || sessionStorage.getItem('firefoxUserGesture') === 'true') {
|
|
setTimeout(restoreSound, 200); // Longer delay for Firefox
|
|
} else {
|
|
// Show Firefox-specific notification
|
|
setTimeout(() => {
|
|
if (this.player && !this.player.isDisposed()) {
|
|
this.player.trigger('notify', '🦊 Firefox: Click to enable sound');
|
|
}
|
|
}, 1500); // Longer delay for Firefox notification
|
|
|
|
// Set up Firefox-specific interaction listeners
|
|
const enableSound = () => {
|
|
restoreSound();
|
|
// Mark Firefox user interaction
|
|
sessionStorage.setItem('userInteracted', 'true');
|
|
sessionStorage.setItem('firefoxUserGesture', 'true');
|
|
// Remove listeners
|
|
document.removeEventListener('click', enableSound);
|
|
document.removeEventListener('keydown', enableSound);
|
|
document.removeEventListener('touchstart', enableSound);
|
|
};
|
|
|
|
document.addEventListener('click', enableSound, { once: true });
|
|
document.addEventListener('keydown', enableSound, { once: true });
|
|
document.addEventListener('touchstart', enableSound, { once: true });
|
|
}
|
|
} else {
|
|
// Original behavior for other browsers
|
|
if (userInteracted) {
|
|
setTimeout(restoreSound, 100);
|
|
} else {
|
|
// Show notification for manual interaction
|
|
setTimeout(() => {
|
|
if (this.player && !this.player.isDisposed()) {
|
|
this.player.trigger('notify', '🔇 Click anywhere to enable sound');
|
|
}
|
|
}, 1000);
|
|
|
|
// Set up interaction listeners
|
|
const enableSound = () => {
|
|
restoreSound();
|
|
// Mark user interaction for future videos
|
|
sessionStorage.setItem('userInteracted', 'true');
|
|
// Remove listeners
|
|
document.removeEventListener('click', enableSound);
|
|
document.removeEventListener('keydown', enableSound);
|
|
document.removeEventListener('touchstart', enableSound);
|
|
};
|
|
|
|
document.addEventListener('click', enableSound, { once: true });
|
|
document.addEventListener('keydown', enableSound, { once: true });
|
|
document.addEventListener('touchstart', enableSound, { once: true });
|
|
}
|
|
}
|
|
}
|
|
}
|