122 lines
3.8 KiB
JavaScript
122 lines
3.8 KiB
JavaScript
import { formatDuration } from './utils.js';
|
|
|
|
export class SimplePlayer {
|
|
constructor({ audio, progressEl, currentTimeEl, durationEl }) {
|
|
this.audio = audio;
|
|
this.progressEl = progressEl;
|
|
this.currentTimeEl = currentTimeEl;
|
|
this.durationEl = durationEl;
|
|
|
|
this.queue = [];
|
|
this.currentIndex = -1;
|
|
this.currentStreamUrl = '';
|
|
|
|
this.dashPlayer = window.dashjs.MediaPlayer().create();
|
|
this.usingDash = false;
|
|
|
|
this.onTrackChanged = null;
|
|
this.onTimeUpdate = null;
|
|
this.resolveStreamFn = null;
|
|
|
|
this.bindAudioEvents();
|
|
}
|
|
|
|
bindAudioEvents() {
|
|
this.audio.addEventListener('timeupdate', () => {
|
|
const duration = this.audio.duration || 0;
|
|
const current = this.audio.currentTime || 0;
|
|
const value = duration > 0 ? (current / duration) * 100 : 0;
|
|
this.progressEl.value = String(value);
|
|
this.currentTimeEl.textContent = formatDuration(current);
|
|
|
|
if (typeof this.onTimeUpdate === 'function') {
|
|
this.onTimeUpdate(current, duration, this.getCurrentTrack());
|
|
}
|
|
});
|
|
|
|
this.audio.addEventListener('loadedmetadata', () => {
|
|
this.durationEl.textContent = formatDuration(this.audio.duration || 0);
|
|
});
|
|
|
|
this.audio.addEventListener('ended', async () => {
|
|
await this.playNext();
|
|
});
|
|
|
|
this.progressEl.addEventListener('input', () => {
|
|
const duration = this.audio.duration || 0;
|
|
if (duration <= 0) return;
|
|
const target = (Number(this.progressEl.value) / 100) * duration;
|
|
this.audio.currentTime = target;
|
|
});
|
|
}
|
|
|
|
setResolver(resolveStreamFn) {
|
|
this.resolveStreamFn = resolveStreamFn;
|
|
}
|
|
|
|
setQueue(tracks, startIndex = 0) {
|
|
this.queue = Array.isArray(tracks) ? tracks : [];
|
|
this.currentIndex = Math.max(0, Math.min(startIndex, this.queue.length - 1));
|
|
}
|
|
|
|
getCurrentTrack() {
|
|
if (this.currentIndex < 0 || this.currentIndex >= this.queue.length) return null;
|
|
return this.queue[this.currentIndex];
|
|
}
|
|
|
|
async playStream(streamUrl, isDash = false) {
|
|
if (this.currentStreamUrl?.startsWith('blob:') && this.currentStreamUrl !== streamUrl) {
|
|
URL.revokeObjectURL(this.currentStreamUrl);
|
|
}
|
|
|
|
if (this.usingDash) {
|
|
this.dashPlayer.reset();
|
|
this.usingDash = false;
|
|
}
|
|
|
|
this.currentStreamUrl = streamUrl;
|
|
|
|
if (isDash) {
|
|
this.dashPlayer.initialize(this.audio, streamUrl, true);
|
|
this.usingDash = true;
|
|
return;
|
|
}
|
|
|
|
this.audio.src = streamUrl;
|
|
await this.audio.play();
|
|
}
|
|
|
|
async playCurrent(resolveStreamFn = null) {
|
|
const resolver = resolveStreamFn || this.resolveStreamFn;
|
|
const track = this.getCurrentTrack();
|
|
if (!track || typeof resolver !== 'function') return;
|
|
|
|
const { streamUrl, isDash } = await resolver(track);
|
|
await this.playStream(streamUrl, isDash);
|
|
|
|
if (typeof this.onTrackChanged === 'function') {
|
|
this.onTrackChanged(track);
|
|
}
|
|
}
|
|
|
|
async playNext(resolveStreamFn = null) {
|
|
if (this.queue.length === 0) return;
|
|
this.currentIndex = (this.currentIndex + 1) % this.queue.length;
|
|
await this.playCurrent(resolveStreamFn);
|
|
}
|
|
|
|
async playPrev(resolveStreamFn = null) {
|
|
if (this.queue.length === 0) return;
|
|
this.currentIndex = (this.currentIndex - 1 + this.queue.length) % this.queue.length;
|
|
await this.playCurrent(resolveStreamFn);
|
|
}
|
|
|
|
async togglePlayPause() {
|
|
if (this.audio.paused) {
|
|
await this.audio.play();
|
|
} else {
|
|
this.audio.pause();
|
|
}
|
|
}
|
|
}
|