feat: upgrade media player
This commit is contained in:
parent
6cb1c9d29c
commit
f5f7371595
13 changed files with 89 additions and 68 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
app/static/songs/** filter=lfs diff=lfs merge=lfs -text
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 130 B |
3
app/static/songs/deja-vu/audio.m4a
Normal file
3
app/static/songs/deja-vu/audio.m4a
Normal file
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1ffed6b030e6efac7b842aa1d04fe142ae67cc42676ef0863fd98c986e45167c
|
||||
size 4272439
|
3
app/static/songs/deja-vu/cover.jpg
Normal file
3
app/static/songs/deja-vu/cover.jpg
Normal file
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:577320e227fedd883db36d7e1fd0399b4f7bae898a9c927de4b347c59f54917f
|
||||
size 36462
|
3
app/static/songs/xenogenesis/audio.m4a
Normal file
3
app/static/songs/xenogenesis/audio.m4a
Normal file
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ed6986a60bd281410b2e47a21928280ef1b4ef8ea0015f046886e0c1ca38f1f8
|
||||
size 3780869
|
3
app/static/songs/xenogenesis/cover.jpg
Normal file
3
app/static/songs/xenogenesis/cover.jpg
Normal file
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:60bb7c64e60ec007b423dcd427cb7c7cb8a5e075b504e3168b5a63a0d4b6cc7e
|
||||
size 79875
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
const handlePlay = () => {
|
||||
musicStore.setCurrent(slug)
|
||||
!$musicStore.playing && musicStore.toggle()
|
||||
}
|
||||
|
||||
const handleQueueNext = () => {
|
||||
|
@ -17,6 +18,8 @@
|
|||
const handleQueueLast = () => {
|
||||
musicStore.push(slug)
|
||||
}
|
||||
|
||||
$: nowPlaying = slug === $musicStore.queue[$musicStore.currentIndex]
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
@ -27,7 +30,11 @@
|
|||
<p class="text-xl text-slate-400">{artist}</p>
|
||||
<div class="flex justify-center">
|
||||
<div class="my-auto flex gap-4">
|
||||
<button class="mt-2 hover:brightness-75" on:click={handlePlay}>
|
||||
<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>
|
||||
<button class="mt-2 hover:brightness-75" on:click={handleQueueNext}>
|
||||
|
|
|
@ -7,10 +7,7 @@
|
|||
</script>
|
||||
|
||||
<div class="my-auto flex gap-4 mr-4">
|
||||
<button
|
||||
class="mt-2 hover:brightness-75"
|
||||
on:click={() => dispatch('previous')}
|
||||
>
|
||||
<button class="mt-2 hover:brightness-75" on:click={() => dispatch('rewind')}>
|
||||
<span class="material-symbols-outlined icon">skip_previous</span>
|
||||
</button>
|
||||
<button class="mt-2 hover:brightness-75" on:click={() => dispatch('toggle')}>
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
musicStore.skip()
|
||||
}
|
||||
|
||||
const handleRewind = () => {}
|
||||
const rewind = () => {
|
||||
musicStore.rewind()
|
||||
}
|
||||
|
||||
const toggle = () => {
|
||||
musicStore.toggle()
|
||||
|
@ -32,6 +34,11 @@
|
|||
<p class="text-lg text-slate-400">{songData.artist}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Controls on:skip={skip} on:toggle={toggle} playing={$musicStore.playing} />
|
||||
<Controls
|
||||
on:skip={skip}
|
||||
on:toggle={toggle}
|
||||
on:rewind={rewind}
|
||||
playing={$musicStore.playing}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
export class AudioManager {
|
||||
private audioContext: AudioContext | null = null
|
||||
private currentSource: AudioBufferSourceNode | null = null
|
||||
private currentBuffer: AudioBuffer | null = null // Stores the current audio buffer
|
||||
private startTime: number = 0 // When the current playback started
|
||||
private pauseTime: number = 0 // Track where we paused
|
||||
private currentToken: number = 0 // Unique token for each play request
|
||||
private isPaused: boolean = false
|
||||
private isPlaying: boolean = false // Track whether audio is playing
|
||||
|
||||
constructor() {
|
||||
this.initAudioContext()
|
||||
|
@ -18,13 +15,6 @@ export class AudioManager {
|
|||
}
|
||||
|
||||
public async playAudio(url: string): Promise<void> {
|
||||
if (this.isPaused && this.currentBuffer) {
|
||||
// If paused, resume instead of reloading
|
||||
this.resume()
|
||||
return
|
||||
}
|
||||
|
||||
this.pauseTime = 0 // Reset pause time for a new track
|
||||
const playToken = ++this.currentToken // Update the token for this request
|
||||
await this.initAudioContext()
|
||||
|
||||
|
@ -34,69 +24,65 @@ export class AudioManager {
|
|||
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
|
||||
}
|
||||
const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer)
|
||||
this.currentBuffer = audioBuffer // Save the buffer for potential pausing/resuming
|
||||
|
||||
this.startPlayback(audioBuffer, 0) // Start playback from the beginning
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private startPlayback(buffer: AudioBuffer, offset: number) {
|
||||
const source = this.audioContext!.createBufferSource()
|
||||
source.buffer = buffer
|
||||
source.connect(this.audioContext!.destination)
|
||||
source.start(0, offset)
|
||||
this.startTime = this.audioContext!.currentTime - offset
|
||||
this.currentSource = source
|
||||
this.isPaused = false
|
||||
|
||||
source.onended = () => {
|
||||
if (!this.isPaused) {
|
||||
this.currentBuffer = null // Clear the buffer if playback finishes normally
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public pause() {
|
||||
if (!this.isPaused && this.currentSource && this.audioContext) {
|
||||
this.pauseTime = this.audioContext.currentTime - this.startTime
|
||||
this.currentSource.stop()
|
||||
this.isPaused = true
|
||||
}
|
||||
}
|
||||
|
||||
public resume() {
|
||||
if (this.isPaused && this.currentBuffer) {
|
||||
this.startPlayback(this.currentBuffer, this.pauseTime)
|
||||
}
|
||||
}
|
||||
|
||||
public stopAudio() {
|
||||
if (this.currentSource) {
|
||||
this.currentSource.stop()
|
||||
this.currentSource = null
|
||||
this.currentBuffer = null // Clear the current buffer
|
||||
this.isPaused = false
|
||||
this.pauseTime = 0
|
||||
}
|
||||
this.isPlaying = false // Update the playing status
|
||||
}
|
||||
}
|
||||
|
||||
// Usage example:
|
||||
// Method to check if audio is currently playing
|
||||
public isAudioPlaying(): boolean {
|
||||
return this.isPlaying
|
||||
}
|
||||
}
|
||||
|
||||
// Usage example
|
||||
const audioManager = new AudioManager()
|
||||
const audioUrl = 'https://example.com/path/to/your/audio/file.mp3'
|
||||
console.log(audioManager.isAudioPlaying()) // False, initially
|
||||
|
||||
// To play the audio
|
||||
audioManager.playAudio(audioUrl)
|
||||
// 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
|
||||
})
|
||||
|
||||
// To pause the audio
|
||||
audioManager.pause()
|
||||
|
||||
// To resume the audio
|
||||
audioManager.resume()
|
||||
// Later, you can check if audio is still playing
|
||||
setTimeout(() => {
|
||||
console.log(audioManager.isAudioPlaying()) // The result depends on the audio length and timing
|
||||
}, 1000)
|
||||
|
|
|
@ -5,10 +5,16 @@ export const songList: { [key: string]: SongData } = {
|
|||
src: '/static/songs/danger-zone/audio.mp3',
|
||||
coverImg: '/static/songs/danger-zone/cover.png',
|
||||
},
|
||||
'monko-zone': {
|
||||
title: 'Danger Zone',
|
||||
artist: 'Kenny Loggins',
|
||||
src: '/static/songs/danger-zone/audio.mp3',
|
||||
coverImg: '/static/songs/danger-zone/cover.png',
|
||||
'deja-vu': {
|
||||
title: 'Deja Vu',
|
||||
artist: 'Initial D',
|
||||
src: '/static/songs/deja-vu/audio.m4a',
|
||||
coverImg: '/static/songs/deja-vu/cover.jpg',
|
||||
},
|
||||
'Xenogenesis': {
|
||||
title: 'Xenogenesis',
|
||||
artist: 'TheFatRat',
|
||||
src: '/static/songs/xenogenesis/audio.m4a',
|
||||
coverImg: '/static/songs/xenogenesis/cover.jpg',
|
||||
},
|
||||
}
|
||||
|
|
|
@ -53,6 +53,11 @@ function createMusicStore() {
|
|||
store.playing = !store.playing
|
||||
return store
|
||||
}),
|
||||
rewind: () =>
|
||||
update(store => {
|
||||
store.currentIndex--
|
||||
return store
|
||||
}),
|
||||
reset: () => set({ queue: [], currentIndex: 0, playing: false }),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue