Spaces:
Runtime error
Runtime error
export class PlayerControls { | |
constructor(audioService, playerState) { | |
this.audioService = audioService; | |
this.playerState = playerState; | |
this.elements = { | |
playPauseBtn: document.getElementById('play-pause-btn'), | |
seekSlider: document.getElementById('seek-slider'), | |
volumeSlider: document.getElementById('volume-slider'), | |
speedSlider: document.getElementById('speed-slider'), | |
speedValue: document.getElementById('speed-value'), | |
timeDisplay: document.getElementById('time-display'), | |
cancelBtn: document.getElementById('cancel-btn') | |
}; | |
this.setupEventListeners(); | |
this.setupAudioEvents(); | |
this.setupStateSubscription(); | |
this.timeUpdateInterval = null; | |
} | |
formatTime(secs) { | |
const minutes = Math.floor(secs / 60); | |
const seconds = Math.floor(secs % 60); | |
return `${minutes}:${seconds.toString().padStart(2, '0')}`; | |
} | |
startTimeUpdate() { | |
this.stopTimeUpdate(); // Clear any existing interval | |
this.timeUpdateInterval = setInterval(() => { | |
this.updateTimeDisplay(); | |
}, 100); // Update every 100ms for smooth tracking | |
} | |
stopTimeUpdate() { | |
if (this.timeUpdateInterval) { | |
clearInterval(this.timeUpdateInterval); | |
this.timeUpdateInterval = null; | |
} | |
} | |
updateTimeDisplay() { | |
const currentTime = this.audioService.getCurrentTime(); | |
const duration = this.audioService.getDuration(); | |
// Update time display | |
this.elements.timeDisplay.textContent = | |
`${this.formatTime(currentTime)} / ${this.formatTime(duration || 0)}`; | |
// Update seek slider | |
if (duration > 0 && !this.elements.seekSlider.dragging) { | |
this.elements.seekSlider.value = (currentTime / duration) * 100; | |
} | |
// Update state | |
this.playerState.setTime(currentTime, duration); | |
} | |
setupEventListeners() { | |
// Play/Pause button | |
this.elements.playPauseBtn.addEventListener('click', () => { | |
if (this.audioService.isPlaying()) { | |
this.audioService.pause(); | |
} else { | |
this.audioService.play(); | |
} | |
}); | |
// Seek slider | |
this.elements.seekSlider.addEventListener('mousedown', () => { | |
this.elements.seekSlider.dragging = true; | |
}); | |
this.elements.seekSlider.addEventListener('mouseup', () => { | |
this.elements.seekSlider.dragging = false; | |
}); | |
this.elements.seekSlider.addEventListener('input', (e) => { | |
const duration = this.audioService.getDuration(); | |
const seekTime = (duration * e.target.value) / 100; | |
this.audioService.seek(seekTime); | |
this.updateTimeDisplay(); | |
}); | |
// Volume slider | |
this.elements.volumeSlider.addEventListener('input', (e) => { | |
const volume = e.target.value / 100; | |
this.audioService.setVolume(volume); | |
this.playerState.setVolume(volume); | |
}); | |
// Speed slider | |
this.elements.speedSlider.addEventListener('input', (e) => { | |
const speed = parseFloat(e.target.value); | |
this.elements.speedValue.textContent = speed.toFixed(1); | |
this.playerState.setSpeed(speed); | |
}); | |
// Cancel button | |
this.elements.cancelBtn.addEventListener('click', () => { | |
this.audioService.cancel(); | |
this.playerState.reset(); | |
this.updateControls({ isGenerating: false }); | |
this.stopTimeUpdate(); | |
}); | |
} | |
setupAudioEvents() { | |
this.audioService.addEventListener('play', () => { | |
this.elements.playPauseBtn.textContent = 'Pause'; | |
this.playerState.setPlaying(true); | |
this.startTimeUpdate(); | |
}); | |
this.audioService.addEventListener('pause', () => { | |
this.elements.playPauseBtn.textContent = 'Play'; | |
this.playerState.setPlaying(false); | |
this.stopTimeUpdate(); | |
}); | |
this.audioService.addEventListener('ended', () => { | |
this.elements.playPauseBtn.textContent = 'Play'; | |
this.playerState.setPlaying(false); | |
this.stopTimeUpdate(); | |
}); | |
// Initial time display | |
this.updateTimeDisplay(); | |
} | |
setupStateSubscription() { | |
this.playerState.subscribe(state => this.updateControls(state)); | |
} | |
updateControls(state) { | |
// Update button states | |
this.elements.playPauseBtn.disabled = !state.duration && !state.isGenerating; | |
this.elements.seekSlider.disabled = !state.duration; | |
this.elements.cancelBtn.style.display = state.isGenerating ? 'block' : 'none'; | |
// Update volume and speed if changed externally | |
if (this.elements.volumeSlider.value !== state.volume * 100) { | |
this.elements.volumeSlider.value = state.volume * 100; | |
} | |
if (this.elements.speedSlider.value !== state.speed.toString()) { | |
this.elements.speedSlider.value = state.speed; | |
this.elements.speedValue.textContent = state.speed.toFixed(1); | |
} | |
} | |
cleanup() { | |
this.stopTimeUpdate(); | |
if (this.audioService) { | |
this.audioService.pause(); | |
} | |
if (this.playerState) { | |
this.playerState.reset(); | |
} | |
// Reset UI elements | |
this.elements.playPauseBtn.textContent = 'Play'; | |
this.elements.playPauseBtn.disabled = true; | |
this.elements.seekSlider.value = 0; | |
this.elements.seekSlider.disabled = true; | |
this.elements.timeDisplay.textContent = '0:00 / 0:00'; | |
} | |
} | |
export default PlayerControls; |