188 lines
4.1 KiB
TypeScript
188 lines
4.1 KiB
TypeScript
import type { Song } from '~/types/song'
|
|
import type { PlayerMode, RepeatMode } from '~/types/player'
|
|
|
|
export const usePlayerStore = defineStore('player', () => {
|
|
// State
|
|
const isPlaying = ref(false)
|
|
const currentSong = ref<Song | null>(null)
|
|
const currentTime = ref(0)
|
|
const duration = ref(0)
|
|
const volume = ref(0.8)
|
|
const mode = ref<PlayerMode>('guided')
|
|
const repeatMode = ref<RepeatMode>('none')
|
|
const isShuffled = ref(false)
|
|
const playlist = ref<Song[]>([])
|
|
const queue = ref<Song[]>([])
|
|
const isExpanded = ref(false)
|
|
|
|
// Computed
|
|
const progress = computed(() => {
|
|
if (duration.value === 0) return 0
|
|
return (currentTime.value / duration.value) * 100
|
|
})
|
|
|
|
const formattedCurrentTime = computed(() => formatTime(currentTime.value))
|
|
const formattedDuration = computed(() => formatTime(duration.value))
|
|
const isGuidedMode = computed(() => mode.value === 'guided')
|
|
|
|
const currentIndex = computed(() => {
|
|
if (!currentSong.value) return -1
|
|
return playlist.value.findIndex(s => s.id === currentSong.value!.id)
|
|
})
|
|
|
|
const hasNext = computed(() => {
|
|
if (repeatMode.value === 'all') return playlist.value.length > 0
|
|
return currentIndex.value < playlist.value.length - 1
|
|
})
|
|
|
|
const hasPrev = computed(() => {
|
|
return currentIndex.value > 0
|
|
})
|
|
|
|
// Actions
|
|
function setSong(song: Song) {
|
|
currentSong.value = song
|
|
currentTime.value = 0
|
|
duration.value = song.duration
|
|
}
|
|
|
|
function setPlaylist(songs: Song[]) {
|
|
playlist.value = songs
|
|
}
|
|
|
|
function setMode(newMode: PlayerMode) {
|
|
mode.value = newMode
|
|
}
|
|
|
|
function togglePlay() {
|
|
isPlaying.value = !isPlaying.value
|
|
}
|
|
|
|
function play() {
|
|
isPlaying.value = true
|
|
}
|
|
|
|
function pause() {
|
|
isPlaying.value = false
|
|
}
|
|
|
|
function setCurrentTime(time: number) {
|
|
currentTime.value = time
|
|
}
|
|
|
|
function setDuration(dur: number) {
|
|
duration.value = dur
|
|
}
|
|
|
|
function setVolume(vol: number) {
|
|
volume.value = Math.max(0, Math.min(1, vol))
|
|
}
|
|
|
|
function toggleRepeat() {
|
|
const modes: RepeatMode[] = ['none', 'all', 'one']
|
|
const idx = modes.indexOf(repeatMode.value)
|
|
repeatMode.value = modes[(idx + 1) % modes.length]
|
|
}
|
|
|
|
function toggleShuffle() {
|
|
isShuffled.value = !isShuffled.value
|
|
}
|
|
|
|
function toggleExpanded() {
|
|
isExpanded.value = !isExpanded.value
|
|
}
|
|
|
|
function nextSong(): Song | null {
|
|
if (playlist.value.length === 0) return null
|
|
|
|
if (repeatMode.value === 'one') {
|
|
currentTime.value = 0
|
|
return currentSong.value
|
|
}
|
|
|
|
let nextIdx = currentIndex.value + 1
|
|
if (nextIdx >= playlist.value.length) {
|
|
if (repeatMode.value === 'all') {
|
|
nextIdx = 0
|
|
}
|
|
else {
|
|
pause()
|
|
return null
|
|
}
|
|
}
|
|
|
|
const song = playlist.value[nextIdx]
|
|
setSong(song)
|
|
return song
|
|
}
|
|
|
|
function prevSong(): Song | null {
|
|
if (playlist.value.length === 0) return null
|
|
|
|
// If more than 3 seconds in, restart current song
|
|
if (currentTime.value > 3) {
|
|
currentTime.value = 0
|
|
return currentSong.value
|
|
}
|
|
|
|
let prevIdx = currentIndex.value - 1
|
|
if (prevIdx < 0) {
|
|
if (repeatMode.value === 'all') {
|
|
prevIdx = playlist.value.length - 1
|
|
}
|
|
else {
|
|
currentTime.value = 0
|
|
return currentSong.value
|
|
}
|
|
}
|
|
|
|
const song = playlist.value[prevIdx]
|
|
setSong(song)
|
|
return song
|
|
}
|
|
|
|
return {
|
|
// State
|
|
isPlaying,
|
|
currentSong,
|
|
currentTime,
|
|
duration,
|
|
volume,
|
|
mode,
|
|
repeatMode,
|
|
isShuffled,
|
|
playlist,
|
|
queue,
|
|
isExpanded,
|
|
// Computed
|
|
progress,
|
|
formattedCurrentTime,
|
|
formattedDuration,
|
|
isGuidedMode,
|
|
currentIndex,
|
|
hasNext,
|
|
hasPrev,
|
|
// Actions
|
|
setSong,
|
|
setPlaylist,
|
|
setMode,
|
|
togglePlay,
|
|
play,
|
|
pause,
|
|
setCurrentTime,
|
|
setDuration,
|
|
setVolume,
|
|
toggleRepeat,
|
|
toggleShuffle,
|
|
toggleExpanded,
|
|
nextSong,
|
|
prevSong,
|
|
}
|
|
})
|
|
|
|
function formatTime(seconds: number): string {
|
|
const mins = Math.floor(seconds / 60)
|
|
const secs = Math.floor(seconds % 60)
|
|
return `${mins}:${secs.toString().padStart(2, '0')}`
|
|
}
|