Fix audio player synchronization between streams and personal pages
- Add global audio manager to coordinate playback between different players - Integrate synchronization into streams-ui.js (streams page player) - Integrate synchronization into app.js (personal stream player) - Remove simultaneous playback issues - only one audio plays at a time - Clean transitions when switching between streams and personal audio Fixes issue where starting audio on one page didn't stop audio on the other page.
This commit is contained in:
@ -3,6 +3,7 @@
|
||||
import { playBeep } from "./sound.js";
|
||||
import { showToast } from "./toast.js";
|
||||
import { injectNavigation } from "./inject-nav.js";
|
||||
import { globalAudioManager } from './global-audio-manager.js';
|
||||
|
||||
// Global audio state
|
||||
let globalAudio = null;
|
||||
@ -963,6 +964,19 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
// Initialize components
|
||||
initNavigation();
|
||||
|
||||
// Register with global audio manager to handle stop requests from other players
|
||||
globalAudioManager.addListener('personal', () => {
|
||||
console.log('[app.js] Received stop request from global audio manager');
|
||||
const audio = getOrCreateAudioElement();
|
||||
if (audio && !audio.paused) {
|
||||
audio.pause();
|
||||
const playButton = document.querySelector('.play-pause-btn');
|
||||
if (playButton) {
|
||||
updatePlayPauseButton(audio, playButton);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize account deletion section visibility
|
||||
handlePageNavigation();
|
||||
|
||||
@ -1011,6 +1025,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify global audio manager that personal player is starting
|
||||
const uid = localStorage.getItem('uid') || 'personal-stream';
|
||||
globalAudioManager.startPlayback('personal', uid);
|
||||
|
||||
// Store the current play promise to handle aborts
|
||||
const playPromise = audio.play();
|
||||
|
||||
@ -1046,6 +1064,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
});
|
||||
} else {
|
||||
audio.pause();
|
||||
|
||||
// Notify global audio manager that personal player has stopped
|
||||
globalAudioManager.stopPlayback('personal');
|
||||
|
||||
if (window.currentlyPlayingAudio === audio) {
|
||||
window.currentlyPlayingAudio = null;
|
||||
window.currentlyPlayingButton = null;
|
||||
|
125
static/global-audio-manager.js
Normal file
125
static/global-audio-manager.js
Normal file
@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Global Audio Manager
|
||||
* Coordinates audio playback between different components to ensure only one audio plays at a time
|
||||
*/
|
||||
|
||||
class GlobalAudioManager {
|
||||
constructor() {
|
||||
this.currentPlayer = null; // 'streams' or 'personal' or null
|
||||
this.currentUid = null;
|
||||
this.listeners = new Set();
|
||||
|
||||
// Bind methods
|
||||
this.startPlayback = this.startPlayback.bind(this);
|
||||
this.stopPlayback = this.stopPlayback.bind(this);
|
||||
this.addListener = this.addListener.bind(this);
|
||||
this.removeListener = this.removeListener.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a player that wants to start playback
|
||||
* @param {string} playerType - 'streams' or 'personal'
|
||||
* @param {string} uid - The UID being played
|
||||
* @param {Object} playerInstance - Reference to the player instance
|
||||
*/
|
||||
startPlayback(playerType, uid, playerInstance = null) {
|
||||
// If the same player is already playing the same UID, allow it
|
||||
if (this.currentPlayer === playerType && this.currentUid === uid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stop any currently playing audio
|
||||
if (this.currentPlayer && this.currentPlayer !== playerType) {
|
||||
this.notifyStop(this.currentPlayer);
|
||||
}
|
||||
|
||||
// Update current state
|
||||
this.currentPlayer = playerType;
|
||||
this.currentUid = uid;
|
||||
|
||||
console.log(`Global Audio Manager: ${playerType} player started playing UID: ${uid}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that playback has stopped
|
||||
* @param {string} playerType - 'streams' or 'personal'
|
||||
*/
|
||||
stopPlayback(playerType) {
|
||||
if (this.currentPlayer === playerType) {
|
||||
console.log(`Global Audio Manager: ${playerType} player stopped`);
|
||||
this.currentPlayer = null;
|
||||
this.currentUid = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current playback state
|
||||
*/
|
||||
getCurrentState() {
|
||||
return {
|
||||
player: this.currentPlayer,
|
||||
uid: this.currentUid
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific player is currently active
|
||||
*/
|
||||
isPlayerActive(playerType) {
|
||||
return this.currentPlayer === playerType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for stop events
|
||||
* @param {string} playerType - 'streams' or 'personal'
|
||||
* @param {Function} callback - Function to call when this player should stop
|
||||
*/
|
||||
addListener(playerType, callback) {
|
||||
const listener = { playerType, callback };
|
||||
this.listeners.add(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener
|
||||
*/
|
||||
removeListener(listener) {
|
||||
this.listeners.delete(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify a specific player type to stop
|
||||
*/
|
||||
notifyStop(playerType) {
|
||||
console.log(`Global Audio Manager: Notifying ${playerType} player to stop`);
|
||||
this.listeners.forEach(listener => {
|
||||
if (listener.playerType === playerType) {
|
||||
try {
|
||||
listener.callback();
|
||||
} catch (error) {
|
||||
console.error(`Error calling stop callback for ${playerType}:`, error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Force stop all playback
|
||||
*/
|
||||
stopAll() {
|
||||
if (this.currentPlayer) {
|
||||
this.notifyStop(this.currentPlayer);
|
||||
this.currentPlayer = null;
|
||||
this.currentUid = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create singleton instance
|
||||
export const globalAudioManager = new GlobalAudioManager();
|
||||
|
||||
// Make it available globally for debugging
|
||||
if (typeof window !== 'undefined') {
|
||||
window.globalAudioManager = globalAudioManager;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
// static/streams-ui.js — public streams loader and profile-link handling
|
||||
import { showOnly } from './router.js';
|
||||
import { globalAudioManager } from './global-audio-manager.js';
|
||||
|
||||
// Global variable to track if we should force refresh the stream list
|
||||
let shouldForceRefresh = false;
|
||||
@ -24,6 +25,12 @@ export function initStreamsUI() {
|
||||
});
|
||||
document.addEventListener('visibilitychange', maybeLoadStreamsOnShow);
|
||||
maybeLoadStreamsOnShow();
|
||||
|
||||
// Register with global audio manager to handle stop requests from other players
|
||||
globalAudioManager.addListener('streams', () => {
|
||||
console.log('[streams-ui] Received stop request from global audio manager');
|
||||
stopPlayback();
|
||||
});
|
||||
}
|
||||
|
||||
function maybeLoadStreamsOnShow() {
|
||||
@ -539,6 +546,9 @@ function stopPlayback() {
|
||||
pauseTime = 0;
|
||||
audioStartTime = 0;
|
||||
|
||||
// Notify global audio manager that streams player has stopped
|
||||
globalAudioManager.stopPlayback('streams');
|
||||
|
||||
// Update UI
|
||||
if (currentlyPlayingButton) {
|
||||
updatePlayPauseButton(currentlyPlayingButton, false);
|
||||
@ -566,6 +576,9 @@ async function loadAndPlayAudio(uid, playPauseBtn) {
|
||||
// Stop any current playback
|
||||
stopPlayback();
|
||||
|
||||
// Notify global audio manager that streams player is starting
|
||||
globalAudioManager.startPlayback('streams', uid);
|
||||
|
||||
// Update UI
|
||||
updatePlayPauseButton(playPauseBtn, true);
|
||||
currentlyPlayingButton = playPauseBtn;
|
||||
|
Reference in New Issue
Block a user