initiation librodrome
This commit is contained in:
153
app/composables/useAudioPlayer.ts
Normal file
153
app/composables/useAudioPlayer.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import type { Song } from '~/types/song'
|
||||
|
||||
let audio: HTMLAudioElement | null = null
|
||||
let animationFrameId: number | null = null
|
||||
|
||||
export function useAudioPlayer() {
|
||||
const store = usePlayerStore()
|
||||
|
||||
function getAudio(): HTMLAudioElement {
|
||||
if (!audio) {
|
||||
audio = new Audio()
|
||||
audio.preload = 'metadata'
|
||||
audio.volume = store.volume
|
||||
|
||||
audio.addEventListener('loadedmetadata', () => {
|
||||
store.setDuration(audio!.duration)
|
||||
})
|
||||
|
||||
audio.addEventListener('ended', () => {
|
||||
const next = store.nextSong()
|
||||
if (next) {
|
||||
loadAndPlay(next)
|
||||
}
|
||||
})
|
||||
|
||||
audio.addEventListener('error', (e) => {
|
||||
console.error('Audio error:', e)
|
||||
store.pause()
|
||||
})
|
||||
}
|
||||
return audio
|
||||
}
|
||||
|
||||
function startTimeUpdate() {
|
||||
if (animationFrameId) return
|
||||
const update = () => {
|
||||
if (audio && !audio.paused) {
|
||||
store.setCurrentTime(audio.currentTime)
|
||||
}
|
||||
animationFrameId = requestAnimationFrame(update)
|
||||
}
|
||||
animationFrameId = requestAnimationFrame(update)
|
||||
}
|
||||
|
||||
function stopTimeUpdate() {
|
||||
if (animationFrameId) {
|
||||
cancelAnimationFrame(animationFrameId)
|
||||
animationFrameId = null
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAndPlay(song: Song) {
|
||||
const el = getAudio()
|
||||
store.setSong(song)
|
||||
|
||||
// Try OGG first, fall back to MP3
|
||||
const oggPath = song.file.replace(/\.mp3$/, '.ogg')
|
||||
const canOgg = el.canPlayType('audio/ogg; codecs=vorbis')
|
||||
|
||||
el.src = canOgg ? oggPath : song.file
|
||||
el.volume = store.volume
|
||||
|
||||
try {
|
||||
await el.play()
|
||||
store.play()
|
||||
startTimeUpdate()
|
||||
}
|
||||
catch {
|
||||
// If OGG failed, try MP3
|
||||
if (el.src !== song.file) {
|
||||
el.src = song.file
|
||||
try {
|
||||
await el.play()
|
||||
store.play()
|
||||
startTimeUpdate()
|
||||
}
|
||||
catch (err) {
|
||||
console.error('Playback failed:', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pause() {
|
||||
getAudio().pause()
|
||||
store.pause()
|
||||
stopTimeUpdate()
|
||||
}
|
||||
|
||||
function resume() {
|
||||
const el = getAudio()
|
||||
if (el.src) {
|
||||
el.play()
|
||||
store.play()
|
||||
startTimeUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
function togglePlayPause() {
|
||||
if (store.isPlaying) {
|
||||
pause()
|
||||
}
|
||||
else {
|
||||
resume()
|
||||
}
|
||||
}
|
||||
|
||||
function seek(time: number) {
|
||||
const el = getAudio()
|
||||
el.currentTime = time
|
||||
store.setCurrentTime(time)
|
||||
}
|
||||
|
||||
function setVolume(vol: number) {
|
||||
store.setVolume(vol)
|
||||
getAudio().volume = store.volume
|
||||
}
|
||||
|
||||
function playNext() {
|
||||
const song = store.nextSong()
|
||||
if (song) loadAndPlay(song)
|
||||
}
|
||||
|
||||
function playPrev() {
|
||||
const song = store.prevSong()
|
||||
if (song) {
|
||||
if (song === store.currentSong && store.currentTime <= 3) {
|
||||
// prevSong already reset time
|
||||
seek(0)
|
||||
}
|
||||
else {
|
||||
loadAndPlay(song)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Watch volume changes from store
|
||||
watch(() => store.volume, (vol) => {
|
||||
if (audio) audio.volume = vol
|
||||
})
|
||||
|
||||
return {
|
||||
loadAndPlay,
|
||||
pause,
|
||||
resume,
|
||||
togglePlayPause,
|
||||
seek,
|
||||
setVolume,
|
||||
playNext,
|
||||
playPrev,
|
||||
getAudio,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user