plebsaber.stream/samples/songPlayer.js
2025-08-09 00:16:28 -07:00

111 lines
2.5 KiB
JavaScript

import {writable} from 'svelte/store';
import {tweened} from 'svelte/motion';
import {cubicOut} from 'svelte/easing';
export let songPlayerStore = null;
export let currentTimeStore = null;
const DEFAULT_STATE = {
currentHash: null,
playing: false,
duration: 0,
currentTime: 0,
volume: 0.3,
};
export default () => {
if (songPlayerStore) return songPlayerStore;
let currentState = {...DEFAULT_STATE};
let audio = null;
currentTimeStore = tweened(0, {duration: 10, easing: cubicOut});
const {subscribe, set, update} = writable(currentState);
function cleanup() {
if (audio) {
audio.removeEventListener('timeupdate', handleTimeUpdate);
audio.removeEventListener('loadedmetadata', handleMetadata);
audio.removeEventListener('ended', handleEnded);
audio.removeEventListener('pause', handleEnded);
audio.pause();
audio.currentTime = 0;
audio = null;
}
}
function handleTimeUpdate() {
currentTimeStore.set(audio.currentTime);
}
function handleMetadata() {
update(state => ({...state, duration: audio.duration}));
}
function handleEnded() {
update(state => ({...state, playing: false, currentHash: null}));
}
function togglePlay(hash, local = false) {
update(state => {
const isPlaying = hash == state.currentHash ? !state.playing : true;
let url = local ? `https://cdn.songs.beatleader.com/${hash}.mp3` : `https://eu.cdn.beatsaver.com/${hash.toLowerCase()}.mp3`;
if (hash !== state.currentHash) {
cleanup();
currentTimeStore.set(0);
audio = new Audio(url);
audio.volume = state.volume;
audio.addEventListener('timeupdate', handleTimeUpdate);
audio.addEventListener('loadedmetadata', handleMetadata);
audio.addEventListener('ended', handleEnded);
audio.addEventListener('pause', handleEnded);
} else if (!audio) {
audio = new Audio(url);
audio.volume = state.volume;
audio.addEventListener('timeupdate', handleTimeUpdate);
audio.addEventListener('loadedmetadata', handleMetadata);
audio.addEventListener('ended', handleEnded);
audio.addEventListener('pause', handleEnded);
}
if (isPlaying) {
audio?.play();
} else {
audio?.pause();
}
return {
...state,
currentHash: hash,
playing: isPlaying,
};
});
}
function setVolume(volume) {
update(state => {
if (audio) {
audio.volume = volume;
}
return {...state, volume};
});
}
const reset = () => {
cleanup();
set(DEFAULT_STATE);
};
songPlayerStore = {
subscribe,
togglePlay,
setVolume,
reset,
};
return songPlayerStore;
};