diff --git a/client/src/lib/Apps/MusicBrowser/Song.svelte b/client/src/lib/Apps/MusicBrowser/Song.svelte index 00cee78..7b101e0 100644 --- a/client/src/lib/Apps/MusicBrowser/Song.svelte +++ b/client/src/lib/Apps/MusicBrowser/Song.svelte @@ -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 @@ diff --git a/client/src/lib/Dashboard/Dashboard.svelte b/client/src/lib/Dashboard/Dashboard.svelte index dc825d1..0fa97d8 100644 --- a/client/src/lib/Dashboard/Dashboard.svelte +++ b/client/src/lib/Dashboard/Dashboard.svelte @@ -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' - +
diff --git a/client/src/lib/Dashboard/MediaPlayer/Controls.svelte b/client/src/lib/Dashboard/MediaPlayer/Controls.svelte index 92e72d5..8593140 100644 --- a/client/src/lib/Dashboard/MediaPlayer/Controls.svelte +++ b/client/src/lib/Dashboard/MediaPlayer/Controls.svelte @@ -1,9 +1,16 @@
diff --git a/client/src/lib/Dashboard/MediaPlayer/MediaDisplay.svelte b/client/src/lib/Dashboard/MediaPlayer/MediaDisplay.svelte index 47e50f7..c72cd66 100644 --- a/client/src/lib/Dashboard/MediaPlayer/MediaDisplay.svelte +++ b/client/src/lib/Dashboard/MediaPlayer/MediaDisplay.svelte @@ -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() + }) + }) {#if songData} -
+
- 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) - } - diff --git a/client/src/lib/Dashboard/MediaPlayer/audioManager.ts b/client/src/lib/Dashboard/MediaPlayer/audioManager.ts deleted file mode 100644 index c5522e1..0000000 --- a/client/src/lib/Dashboard/MediaPlayer/audioManager.ts +++ /dev/null @@ -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 { - 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) diff --git a/client/src/lib/stores/audioManager.ts b/client/src/lib/stores/audioManager.ts new file mode 100644 index 0000000..ce826c0 --- /dev/null +++ b/client/src/lib/stores/audioManager.ts @@ -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 + } + } +} diff --git a/client/src/lib/stores/musicStore.ts b/client/src/lib/stores/musicStore.ts index f990414..1f5a2bb 100644 --- a/client/src/lib/stores/musicStore.ts +++ b/client/src/lib/stores/musicStore.ts @@ -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({ 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, + }), } }