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 = () => {
|
const handlePlay = () => {
|
||||||
musicStore.setCurrent(slug)
|
musicStore.setCurrent(slug)
|
||||||
|
!$musicStore.playing && musicStore.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleQueueNext = () => {
|
const handleQueueNext = () => {
|
||||||
|
@ -17,6 +18,8 @@
|
||||||
const handleQueueLast = () => {
|
const handleQueueLast = () => {
|
||||||
musicStore.push(slug)
|
musicStore.push(slug)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: nowPlaying = slug === $musicStore.queue[$musicStore.currentIndex]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -27,7 +30,11 @@
|
||||||
<p class="text-xl text-slate-400">{artist}</p>
|
<p class="text-xl text-slate-400">{artist}</p>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div class="my-auto flex gap-4">
|
<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>
|
<span class="material-symbols-outlined icon fill">play_arrow</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="mt-2 hover:brightness-75" on:click={handleQueueNext}>
|
<button class="mt-2 hover:brightness-75" on:click={handleQueueNext}>
|
||||||
|
|
|
@ -7,10 +7,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="my-auto flex gap-4 mr-4">
|
<div class="my-auto flex gap-4 mr-4">
|
||||||
<button
|
<button class="mt-2 hover:brightness-75" on:click={() => dispatch('rewind')}>
|
||||||
class="mt-2 hover:brightness-75"
|
|
||||||
on:click={() => dispatch('previous')}
|
|
||||||
>
|
|
||||||
<span class="material-symbols-outlined icon">skip_previous</span>
|
<span class="material-symbols-outlined icon">skip_previous</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="mt-2 hover:brightness-75" on:click={() => dispatch('toggle')}>
|
<button class="mt-2 hover:brightness-75" on:click={() => dispatch('toggle')}>
|
||||||
|
|
|
@ -10,7 +10,9 @@
|
||||||
musicStore.skip()
|
musicStore.skip()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRewind = () => {}
|
const rewind = () => {
|
||||||
|
musicStore.rewind()
|
||||||
|
}
|
||||||
|
|
||||||
const toggle = () => {
|
const toggle = () => {
|
||||||
musicStore.toggle()
|
musicStore.toggle()
|
||||||
|
@ -32,6 +34,11 @@
|
||||||
<p class="text-lg text-slate-400">{songData.artist}</p>
|
<p class="text-lg text-slate-400">{songData.artist}</p>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
export class AudioManager {
|
export class AudioManager {
|
||||||
private audioContext: AudioContext | null = null
|
private audioContext: AudioContext | null = null
|
||||||
private currentSource: AudioBufferSourceNode | 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 currentToken: number = 0 // Unique token for each play request
|
||||||
private isPaused: boolean = false
|
private isPlaying: boolean = false // Track whether audio is playing
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.initAudioContext()
|
this.initAudioContext()
|
||||||
|
@ -18,13 +15,6 @@ export class AudioManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async playAudio(url: string): Promise<void> {
|
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
|
const playToken = ++this.currentToken // Update the token for this request
|
||||||
await this.initAudioContext()
|
await this.initAudioContext()
|
||||||
|
|
||||||
|
@ -34,69 +24,65 @@ export class AudioManager {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
const arrayBuffer = await response.arrayBuffer()
|
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) {
|
if (this.currentToken !== playToken) {
|
||||||
return // Abort if a newer request has been made
|
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) {
|
} catch (error) {
|
||||||
console.error('Error playing audio:', 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() {
|
public stopAudio() {
|
||||||
if (this.currentSource) {
|
if (this.currentSource) {
|
||||||
this.currentSource.stop()
|
this.currentSource.stop()
|
||||||
this.currentSource = null
|
this.currentSource = null
|
||||||
this.currentBuffer = null // Clear the current buffer
|
this.isPlaying = false // Update the playing status
|
||||||
this.isPaused = false
|
|
||||||
this.pauseTime = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usage example:
|
// Method to check if audio is currently playing
|
||||||
|
public isAudioPlaying(): boolean {
|
||||||
|
return this.isPlaying
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage example
|
||||||
const audioManager = new AudioManager()
|
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
|
// To play audio
|
||||||
audioManager.playAudio(audioUrl)
|
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
|
// Later, you can check if audio is still playing
|
||||||
audioManager.pause()
|
setTimeout(() => {
|
||||||
|
console.log(audioManager.isAudioPlaying()) // The result depends on the audio length and timing
|
||||||
// To resume the audio
|
}, 1000)
|
||||||
audioManager.resume()
|
|
||||||
|
|
|
@ -5,10 +5,16 @@ export const songList: { [key: string]: SongData } = {
|
||||||
src: '/static/songs/danger-zone/audio.mp3',
|
src: '/static/songs/danger-zone/audio.mp3',
|
||||||
coverImg: '/static/songs/danger-zone/cover.png',
|
coverImg: '/static/songs/danger-zone/cover.png',
|
||||||
},
|
},
|
||||||
'monko-zone': {
|
'deja-vu': {
|
||||||
title: 'Danger Zone',
|
title: 'Deja Vu',
|
||||||
artist: 'Kenny Loggins',
|
artist: 'Initial D',
|
||||||
src: '/static/songs/danger-zone/audio.mp3',
|
src: '/static/songs/deja-vu/audio.m4a',
|
||||||
coverImg: '/static/songs/danger-zone/cover.png',
|
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
|
store.playing = !store.playing
|
||||||
return store
|
return store
|
||||||
}),
|
}),
|
||||||
|
rewind: () =>
|
||||||
|
update(store => {
|
||||||
|
store.currentIndex--
|
||||||
|
return store
|
||||||
|
}),
|
||||||
reset: () => set({ queue: [], currentIndex: 0, playing: false }),
|
reset: () => set({ queue: [], currentIndex: 0, playing: false }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue