Spaces:
Build error
Build error
File size: 1,809 Bytes
87a665c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | type AudioQueueEvent = 'stop' | 'empty-queue' | 'id-change';
interface AudioQueueStopDetail {
event: AudioQueueEvent;
id: string | null;
}
export type OnStoppedCallback = (detail: AudioQueueStopDetail) => void;
export class AudioQueue {
private audio: HTMLAudioElement;
private queue: string[] = [];
private current: string | null = null;
private readonly _onEnded = () => this.next();
id: string | null = null;
onStopped: OnStoppedCallback | null = null;
constructor(audioElement: HTMLAudioElement) {
this.audio = audioElement;
this.audio.addEventListener('ended', this._onEnded);
}
setId(newId: string) {
if (this.id === newId) return;
this.#halt();
this.id = newId;
this.onStopped?.({ event: 'id-change', id: newId });
}
setPlaybackRate(rate: number) {
this.audio.playbackRate = rate;
}
enqueue(url: string) {
this.queue.push(url);
// Auto-play if nothing is currently playing or loaded
if (this.audio.paused && !this.current) {
this.next();
}
}
play() {
if (!this.current && this.queue.length > 0) {
this.next();
} else {
this.audio.play();
}
}
next() {
this.current = this.queue.shift() ?? null;
if (this.current) {
this.audio.src = this.current;
this.audio.play();
} else {
this.#halt();
this.onStopped?.({ event: 'empty-queue', id: this.id });
}
}
stop() {
this.#halt();
this.onStopped?.({ event: 'stop', id: this.id });
}
destroy() {
this.audio.removeEventListener('ended', this._onEnded);
this.#halt();
this.onStopped = null;
}
/**
* Pause audio and clear queue without firing onStopped.
* Callers that need the callback should invoke it themselves.
*/
#halt() {
this.audio.pause();
this.audio.currentTime = 0;
this.audio.src = '';
this.queue = [];
this.current = null;
}
}
|