Files
client/www/js/greyscale-player.mjs
T
2026-04-17 16:52:51 -04:00

105 lines
3.1 KiB
JavaScript

function formatTime(seconds) {
if (!Number.isFinite(seconds) || seconds < 0) return '0:00';
const m = Math.floor(seconds / 60);
const s = Math.floor(seconds % 60);
return `${m}:${String(s).padStart(2, '0')}`;
}
export class SimplePlayer {
constructor({ audio, progressEl, currentTimeEl, durationEl }) {
this.audio = audio;
this.progressEl = progressEl;
this.currentTimeEl = currentTimeEl;
this.durationEl = durationEl;
this.currentStreamUrl = '';
this.dashPlayer = window.dashjs.MediaPlayer().create();
this.usingDash = false;
this.onTrackChanged = null;
this.onEnded = 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 = formatTime(current);
if (typeof this.onTimeUpdate === 'function') {
this.onTimeUpdate(current);
}
});
this.audio.addEventListener('loadedmetadata', () => {
this.durationEl.textContent = formatTime(this.audio.duration || 0);
});
this.audio.addEventListener('ended', async () => {
if (typeof this.onEnded === 'function') {
await this.onEnded();
}
});
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;
}
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 playTrack(track, resolveStreamFn = null) {
const resolver = resolveStreamFn || this.resolveStreamFn;
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 togglePlayPause() {
if (this.audio.paused) {
await this.audio.play();
} else {
this.audio.pause();
}
}
}