fix music player
This commit is contained in:
parent
f5f7371595
commit
1ed960d8e8
8 changed files with 123 additions and 139 deletions
|
@ -7,8 +7,7 @@
|
|||
let { title, artist, coverImg } = song
|
||||
|
||||
const handlePlay = () => {
|
||||
musicStore.setCurrent(slug)
|
||||
!$musicStore.playing && musicStore.toggle()
|
||||
musicStore.play(slug)
|
||||
}
|
||||
|
||||
const handleQueueNext = () => {
|
||||
|
@ -33,7 +32,6 @@
|
|||
<button
|
||||
class="mt-2 hover:brightness-75"
|
||||
on:click={!nowPlaying ? handlePlay : () => {}}
|
||||
class:invisible={nowPlaying}
|
||||
>
|
||||
<span class="material-symbols-outlined icon fill">play_arrow</span>
|
||||
</button>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
import Speedometer from './Speedometer.svelte'
|
||||
import SpeedLimit from './SpeedLimit.svelte'
|
||||
import MediaDisplay from './MediaPlayer/MediaDisplay.svelte'
|
||||
import Player from './MediaPlayer/Player.svelte'
|
||||
// import Player from './MediaPlayer/Player.svelte'
|
||||
</script>
|
||||
|
||||
<Player />
|
||||
<!-- <Player /> -->
|
||||
|
||||
<div class="mt-2">
|
||||
<div class="px-5">
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { musicStore } from '../../stores/musicStore'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let playing = false
|
||||
|
||||
let startTime = Date.now()
|
||||
|
||||
$: if (playing) {
|
||||
startTime = Date.now()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="my-auto flex gap-4 mr-4">
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
import Controls from './Controls.svelte'
|
||||
import { musicStore } from '../../stores/musicStore'
|
||||
import { songList } from './songList'
|
||||
import { fly } from 'svelte/transition'
|
||||
import { quintInOut } from 'svelte/easing'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
$: currentSong = $musicStore.queue[$musicStore.currentIndex]
|
||||
$: songData = songList[currentSong]
|
||||
|
@ -17,10 +20,19 @@
|
|||
const toggle = () => {
|
||||
musicStore.toggle()
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
document.addEventListener('ended', () => {
|
||||
musicStore.skip()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if songData}
|
||||
<div class="rounded-t-lg bg-neutral-800 px-4 py-2 h-24 flex justify-between">
|
||||
<div
|
||||
class="rounded-t-lg bg-neutral-800 px-4 py-2 h-24 flex justify-between"
|
||||
transition:fly={{ y: 100, duration: 300, easing: quintInOut }}
|
||||
>
|
||||
<div class="flex gap-6">
|
||||
<div class="aspect-square">
|
||||
<img
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { AudioManager } from './audioManager'
|
||||
import { musicStore } from '../../stores/musicStore'
|
||||
import { songList } from './songList'
|
||||
|
||||
const audioManager = new AudioManager()
|
||||
$: currentSong = songList[$musicStore.queue[$musicStore.currentIndex]]
|
||||
|
||||
let src: string = ''
|
||||
$: {
|
||||
if (currentSong) src = currentSong.src
|
||||
console.log(currentSong)
|
||||
}
|
||||
|
||||
$: {
|
||||
if (src !== '' && $musicStore.playing) {
|
||||
audioManager.playAudio(src)
|
||||
console.log(src)
|
||||
} else if (!$musicStore.playing) {
|
||||
console.log('stopping')
|
||||
audioManager.stopAudio()
|
||||
}
|
||||
console.log($musicStore.queue)
|
||||
console.log($musicStore.currentIndex)
|
||||
}
|
||||
</script>
|
|
@ -1,88 +0,0 @@
|
|||
export class AudioManager {
|
||||
private audioContext: AudioContext | null = null
|
||||
private currentSource: AudioBufferSourceNode | null = null
|
||||
private currentToken: number = 0 // Unique token for each play request
|
||||
private isPlaying: boolean = false // Track whether audio is playing
|
||||
|
||||
constructor() {
|
||||
this.initAudioContext()
|
||||
}
|
||||
|
||||
private async initAudioContext() {
|
||||
if (!this.audioContext) {
|
||||
this.audioContext = new AudioContext()
|
||||
}
|
||||
}
|
||||
|
||||
public async playAudio(url: string): Promise<void> {
|
||||
const playToken = ++this.currentToken // Update the token for this request
|
||||
await this.initAudioContext()
|
||||
|
||||
if (this.audioContext) {
|
||||
this.stopAudio() // Stop any currently playing audio
|
||||
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const arrayBuffer = await response.arrayBuffer()
|
||||
// Before decoding, check if the token has changed
|
||||
if (this.currentToken !== playToken) {
|
||||
return // Abort this operation if a new play request has been made
|
||||
}
|
||||
const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer)
|
||||
|
||||
const source = this.audioContext.createBufferSource()
|
||||
source.buffer = audioBuffer
|
||||
source.connect(this.audioContext.destination)
|
||||
|
||||
// Again check the token before starting playback
|
||||
if (this.currentToken !== playToken) {
|
||||
return // Abort if a newer request has been made
|
||||
}
|
||||
|
||||
source.start(0)
|
||||
this.currentSource = source
|
||||
this.isPlaying = true // Update the playing status
|
||||
|
||||
// Set the playing status to false when the audio ends
|
||||
source.onended = () => {
|
||||
if (this.currentToken === playToken) {
|
||||
// Check to avoid race conditions
|
||||
this.isPlaying = false
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error playing audio:', error)
|
||||
this.isPlaying = false // Ensure status is accurate in case of error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public stopAudio() {
|
||||
if (this.currentSource) {
|
||||
this.currentSource.stop()
|
||||
this.currentSource = null
|
||||
this.isPlaying = false // Update the playing status
|
||||
}
|
||||
}
|
||||
|
||||
// Method to check if audio is currently playing
|
||||
public isAudioPlaying(): boolean {
|
||||
return this.isPlaying
|
||||
}
|
||||
}
|
||||
|
||||
// Usage example
|
||||
const audioManager = new AudioManager()
|
||||
console.log(audioManager.isAudioPlaying()) // False, initially
|
||||
|
||||
// To play audio
|
||||
audioManager
|
||||
.playAudio('https://example.com/path/to/your/audio/file.mp3')
|
||||
.then(() => {
|
||||
console.log(audioManager.isAudioPlaying()) // Should log true when audio starts playing
|
||||
})
|
||||
|
||||
// Later, you can check if audio is still playing
|
||||
setTimeout(() => {
|
||||
console.log(audioManager.isAudioPlaying()) // The result depends on the audio length and timing
|
||||
}, 1000)
|
51
client/src/lib/stores/audioManager.ts
Normal file
51
client/src/lib/stores/audioManager.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
export class AudioPlayer {
|
||||
private audio: HTMLAudioElement
|
||||
private currentUrl: string | null = null
|
||||
private isPlaying: boolean = false
|
||||
public length: number = 0
|
||||
|
||||
constructor() {
|
||||
this.audio = new Audio()
|
||||
this.audio.addEventListener('ended', () => {
|
||||
this.isPlaying = false
|
||||
const ended = new CustomEvent('ended', {
|
||||
detail: this.currentUrl,
|
||||
bubbles: true,
|
||||
})
|
||||
document.dispatchEvent(ended)
|
||||
})
|
||||
|
||||
this.audio.onloadedmetadata = () => {
|
||||
this.length = this.audio.duration
|
||||
}
|
||||
}
|
||||
|
||||
// Getter for playing state
|
||||
get playing(): boolean {
|
||||
return this.isPlaying
|
||||
}
|
||||
|
||||
// Method to play audio from a URL
|
||||
play(url: string): void {
|
||||
this.audio.src = url
|
||||
this.currentUrl = url
|
||||
this.audio.play().catch(e => console.error('Error playing audio:', e))
|
||||
this.isPlaying = true
|
||||
}
|
||||
|
||||
// Method to pause audio playback
|
||||
pause(): void {
|
||||
if (this.isPlaying) {
|
||||
this.audio.pause()
|
||||
this.isPlaying = false
|
||||
}
|
||||
}
|
||||
|
||||
// Method to unpause (resume) audio playback
|
||||
unpause(): void {
|
||||
if (!this.isPlaying && this.currentUrl) {
|
||||
this.audio.play().catch(e => console.error('Error playing audio:', e))
|
||||
this.isPlaying = true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
import { writable } from 'svelte/store'
|
||||
import { songList } from '../Dashboard/MediaPlayer/songList'
|
||||
import { AudioPlayer } from './audioManager'
|
||||
|
||||
interface MusicQueue {
|
||||
queue: string[]
|
||||
currentIndex: number
|
||||
player: AudioPlayer
|
||||
playing: boolean
|
||||
}
|
||||
|
||||
|
@ -10,11 +13,27 @@ function createMusicStore() {
|
|||
const { subscribe, set, update } = writable<MusicQueue>({
|
||||
queue: [],
|
||||
currentIndex: 0,
|
||||
player: new AudioPlayer(),
|
||||
playing: false,
|
||||
})
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
play: (songSlug: string) =>
|
||||
update(store => {
|
||||
if (store.queue.length >= 1) {
|
||||
store.queue.splice(store.currentIndex + 1, 0, songSlug)
|
||||
store.currentIndex++
|
||||
store.player.play(songList[songSlug].src)
|
||||
} else {
|
||||
store.queue[0] = songSlug
|
||||
store.currentIndex = 0
|
||||
store.player.play(songList[songSlug].src)
|
||||
}
|
||||
store.playing = true
|
||||
|
||||
return store
|
||||
}),
|
||||
push: (songSlug: string) =>
|
||||
update(store => {
|
||||
store.queue.push(songSlug)
|
||||
|
@ -22,43 +41,54 @@ function createMusicStore() {
|
|||
}),
|
||||
skip: () =>
|
||||
update(store => {
|
||||
let next = store.queue[store.currentIndex + 1]
|
||||
|
||||
if (next !== undefined) {
|
||||
if (store.currentIndex < store.queue.length - 1) {
|
||||
let next = store.queue[store.currentIndex + 1]
|
||||
store.currentIndex++
|
||||
store.playing = true
|
||||
store.player.play(songList[next].src)
|
||||
}
|
||||
return store
|
||||
}),
|
||||
setCurrent: (songSlug: string) =>
|
||||
update(store => {
|
||||
store.currentIndex = store.queue.length
|
||||
store.queue.push(songSlug)
|
||||
return store
|
||||
}),
|
||||
queueNext: (songSlug: string) =>
|
||||
update(store => {
|
||||
store.queue.splice(store.currentIndex + 1, 0, songSlug)
|
||||
return store
|
||||
}),
|
||||
pause: update(store => {
|
||||
store.playing = false
|
||||
return store
|
||||
}),
|
||||
play: update(store => {
|
||||
store.playing = true
|
||||
return store
|
||||
}),
|
||||
toggle: () =>
|
||||
update(store => {
|
||||
store.playing = !store.playing
|
||||
if (store.player.playing) {
|
||||
store.playing = false
|
||||
store.player.pause()
|
||||
} else {
|
||||
store.playing = true
|
||||
store.player.unpause()
|
||||
}
|
||||
return store
|
||||
}),
|
||||
pause: update(store => {
|
||||
store.player.pause()
|
||||
|
||||
return store
|
||||
}),
|
||||
unpause: update(store => {
|
||||
store.player.unpause()
|
||||
|
||||
return store
|
||||
}),
|
||||
rewind: () =>
|
||||
update(store => {
|
||||
store.currentIndex--
|
||||
store.player.play(songList[store.queue[store.currentIndex]].src)
|
||||
store.playing = true
|
||||
return store
|
||||
}),
|
||||
reset: () => set({ queue: [], currentIndex: 0, playing: false }),
|
||||
reset: () =>
|
||||
set({
|
||||
queue: [],
|
||||
currentIndex: 0,
|
||||
player: new AudioPlayer(),
|
||||
playing: false,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue