Fix audio playback
This commit is contained in:
parent
cec34d49fa
commit
54a33cd14e
|
@ -14,7 +14,7 @@ import { useIntervalFn, useToggle, useWindowSize } from '@vueuse/core'
|
||||||
import { computed, nextTick, onMounted, ref, watchEffect } from 'vue'
|
import { computed, nextTick, onMounted, ref, watchEffect } from 'vue'
|
||||||
import { Track } from '~/types'
|
import { Track } from '~/types'
|
||||||
import onKeyboardShortcut from '~/composables/onKeyboardShortcut'
|
import onKeyboardShortcut from '~/composables/onKeyboardShortcut'
|
||||||
import useQueue from '~/composables/useQueue'
|
import useQueue from '~/composables/audio/useQueue'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
|
|
@ -9,8 +9,8 @@ import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
|
||||||
import Draggable, { } from 'vuedraggable'
|
import Draggable, { } from 'vuedraggable'
|
||||||
import { whenever, useTimeoutFn, useWindowScroll, useWindowSize } from '@vueuse/core'
|
import { whenever, useTimeoutFn, useWindowScroll, useWindowSize } from '@vueuse/core'
|
||||||
import { useGettext } from "vue3-gettext"
|
import { useGettext } from "vue3-gettext"
|
||||||
import useQueue from '~/composables/useQueue'
|
import useQueue from '~/composables/audio/useQueue'
|
||||||
import usePlayer from '~/composables/usePlayer'
|
import usePlayer from '~/composables/audio/usePlayer'
|
||||||
|
|
||||||
const queueModal = ref()
|
const queueModal = ref()
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ import onKeyboardShortcut from '~/composables/onKeyboardShortcut'
|
||||||
import { ref, computed, watch, onMounted, onBeforeUnmount, watchEffect } from 'vue'
|
import { ref, computed, watch, onMounted, onBeforeUnmount, watchEffect } from 'vue'
|
||||||
import { useTimeoutFn, useIntervalFn } from '@vueuse/core'
|
import { useTimeoutFn, useIntervalFn } from '@vueuse/core'
|
||||||
import { useGettext } from 'vue3-gettext'
|
import { useGettext } from 'vue3-gettext'
|
||||||
import useQueue from '~/composables/useQueue'
|
import useQueue from '~/composables/audio/useQueue'
|
||||||
import usePlayer from '~/composables/usePlayer'
|
import usePlayer from '~/composables/audio/usePlayer'
|
||||||
import useTrackSources, { TrackSource } from '~/composables/audio/useTrackSources'
|
import useTrackSources, { TrackSource } from '~/composables/audio/useTrackSources'
|
||||||
import useSoundCache from '~/composables/audio/useSoundCache'
|
import useSoundCache from '~/composables/audio/useSoundCache'
|
||||||
|
|
||||||
|
@ -146,11 +146,7 @@ watch(currentTrack, (track, oldValue) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const observeProgress = ref(false)
|
const observeProgress = ref(false)
|
||||||
useIntervalFn(() => {
|
useIntervalFn(() => observeProgress.value && updateProgress(), 1000)
|
||||||
if (observeProgress.value) {
|
|
||||||
updateProgress()
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
|
|
||||||
watch(playing, async (isPlaying) => {
|
watch(playing, async (isPlaying) => {
|
||||||
if (currentSound.value) {
|
if (currentSound.value) {
|
||||||
|
@ -207,6 +203,7 @@ onMounted(() => {
|
||||||
// TODO (wvffle): Check if it is needed
|
// TODO (wvffle): Check if it is needed
|
||||||
nextTrackPreloaded.value = false
|
nextTrackPreloaded.value = false
|
||||||
|
|
||||||
|
// Cache sound if we have currentTrack available
|
||||||
if (currentTrack.value) {
|
if (currentTrack.value) {
|
||||||
getSound(currentTrack.value)
|
getSound(currentTrack.value)
|
||||||
}
|
}
|
||||||
|
@ -249,7 +246,7 @@ const getSound = (trackData: Track) => {
|
||||||
},
|
},
|
||||||
|
|
||||||
onload () {
|
onload () {
|
||||||
const node = (sound as any)._sounds[0].node as HTMLAudioElement
|
const node = (sound as any)._sounds[0]._node as HTMLAudioElement
|
||||||
|
|
||||||
node.addEventListener('progress', () => {
|
node.addEventListener('progress', () => {
|
||||||
if (sound !== currentSound.value) {
|
if (sound !== currentSound.value) {
|
||||||
|
@ -261,8 +258,8 @@ const getSound = (trackData: Track) => {
|
||||||
},
|
},
|
||||||
|
|
||||||
onplay () {
|
onplay () {
|
||||||
if (sound !== currentSound.value) {
|
if (this !== currentSound.value) {
|
||||||
return sound.stop()
|
return (this as any).stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
const time = currentSound.value.seek()
|
const time = currentSound.value.seek()
|
||||||
|
@ -277,11 +274,16 @@ const getSound = (trackData: Track) => {
|
||||||
store.commit('player/duration', sound.duration())
|
store.commit('player/duration', sound.duration())
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onplayerror (soundId, error) {
|
||||||
|
console.log('play error', soundId, error)
|
||||||
|
},
|
||||||
|
|
||||||
onloaderror (soundId, error) {
|
onloaderror (soundId, error) {
|
||||||
|
console.log('load error', soundId, error)
|
||||||
soundCache.delete(trackData.id)
|
soundCache.delete(trackData.id)
|
||||||
sound.unload()
|
sound.unload()
|
||||||
|
|
||||||
if (sound !== currentSound.value) {
|
if (this !== currentSound.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,6 +300,7 @@ const getSound = (trackData: Track) => {
|
||||||
|
|
||||||
return sound
|
return sound
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTrack = async (trackData: Track) => {
|
const getTrack = async (trackData: Track) => {
|
||||||
// use previously fetched trackData
|
// use previously fetched trackData
|
||||||
if (trackData.uploads.length) {
|
if (trackData.uploads.length) {
|
||||||
|
@ -372,7 +375,8 @@ const loadSound = async (trackData: Track, oldValue?: Track) => {
|
||||||
handleError()
|
handleError()
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSound.value = getSound(trackData as Track)
|
currentSound.value = getSound(trackData)
|
||||||
|
|
||||||
// TODO (wvffle): #1777
|
// TODO (wvffle): #1777
|
||||||
soundId.value = currentSound.value.play()
|
soundId.value = currentSound.value.play()
|
||||||
store.commit('player/isLoadingAudio', true)
|
store.commit('player/isLoadingAudio', true)
|
||||||
|
@ -576,8 +580,11 @@ const switchTab = () => {
|
||||||
<span
|
<span
|
||||||
class="start"
|
class="start"
|
||||||
@click.stop.prevent="setCurrentTime(0)"
|
@click.stop.prevent="setCurrentTime(0)"
|
||||||
>{{ currentTimeFormatted }}</span>
|
>
|
||||||
| <span class="total">{{ durationFormatted }}</span>
|
{{ currentTimeFormatted }}
|
||||||
|
</span>
|
||||||
|
|
|
||||||
|
<span class="total">{{ durationFormatted }}</span>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,3 +1,41 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { useStore } from '~/store'
|
||||||
|
import { useGettext } from 'vue3-gettext'
|
||||||
|
import { useTimeoutFn } from '@vueuse/core'
|
||||||
|
import usePlayer from '~/composables/audio/usePlayer'
|
||||||
|
|
||||||
|
const store = useStore()
|
||||||
|
const { volume, mute, unmute } = usePlayer()
|
||||||
|
|
||||||
|
const expanded = ref(false)
|
||||||
|
const volumeSteps = 100
|
||||||
|
|
||||||
|
const sliderVolume = computed({
|
||||||
|
get: () => volume.value * volumeSteps,
|
||||||
|
set: (value) => store.commit('player/volume', value / volumeSteps)
|
||||||
|
})
|
||||||
|
|
||||||
|
const { $pgettext } = useGettext()
|
||||||
|
const labels = computed(() => ({
|
||||||
|
unmute: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Unmute'),
|
||||||
|
mute: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Mute'),
|
||||||
|
slider: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Adjust volume')
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { start, stop } = useTimeoutFn(() => (expanded.value = false), 500, { immediate: false })
|
||||||
|
|
||||||
|
const handleOver = () => {
|
||||||
|
stop()
|
||||||
|
expanded.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleLeave = () => {
|
||||||
|
stop()
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
class="circular control button"
|
class="circular control button"
|
||||||
|
@ -49,52 +87,3 @@
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
|
||||||
import { mapActions } from 'vuex'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
expanded: false,
|
|
||||||
timeout: null,
|
|
||||||
volumeSteps: 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
sliderVolume: {
|
|
||||||
get () {
|
|
||||||
return this.$store.state.player.volume * this.volumeSteps
|
|
||||||
},
|
|
||||||
set (v) {
|
|
||||||
this.$store.commit('player/volume', v / this.volumeSteps)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
labels () {
|
|
||||||
return {
|
|
||||||
unmute: this.$pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Unmute'),
|
|
||||||
mute: this.$pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Mute'),
|
|
||||||
slider: this.$pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Adjust volume')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions({
|
|
||||||
mute: 'player/mute',
|
|
||||||
unmute: 'player/unmute',
|
|
||||||
toggleMute: 'player/toggleMute'
|
|
||||||
}),
|
|
||||||
handleOver () {
|
|
||||||
if (this.timeout) {
|
|
||||||
clearTimeout(this.timeout)
|
|
||||||
}
|
|
||||||
this.expanded = true
|
|
||||||
},
|
|
||||||
handleLeave () {
|
|
||||||
if (this.timeout) {
|
|
||||||
clearTimeout(this.timeout)
|
|
||||||
}
|
|
||||||
this.timeout = setTimeout(() => { this.expanded = false }, 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { computed, watchEffect } from "vue"
|
import { computed, watchEffect } from "vue"
|
||||||
import { Howler } from 'howler'
|
import { Howler } from 'howler'
|
||||||
import useQueue from '~/composables/useQueue'
|
import useQueue from '~/composables/audio/useQueue'
|
||||||
import toLinearVolumeScale from '~/composables/audio/toLinearVolumeScale'
|
import toLinearVolumeScale from '~/composables/audio/toLinearVolumeScale'
|
||||||
import store from "~/store"
|
import store from "~/store"
|
||||||
|
|
||||||
|
@ -19,6 +19,10 @@ export default () => {
|
||||||
|
|
||||||
watchEffect(() => Howler.volume(toLinearVolumeScale(volume.value)))
|
watchEffect(() => Howler.volume(toLinearVolumeScale(volume.value)))
|
||||||
|
|
||||||
|
const mute = () => store.dispatch('player/mute')
|
||||||
|
const unmute = () => store.dispatch('player/unmute')
|
||||||
|
const toggleMute = () => store.dispatch('player/toggleMute')
|
||||||
|
|
||||||
// Time and duration
|
// Time and duration
|
||||||
const duration = computed(() => store.state.player.duration)
|
const duration = computed(() => store.state.player.duration)
|
||||||
const currentTime = computed(() => store.state.player.currentTime)
|
const currentTime = computed(() => store.state.player.currentTime)
|
||||||
|
@ -65,6 +69,9 @@ export default () => {
|
||||||
focused,
|
focused,
|
||||||
|
|
||||||
volume,
|
volume,
|
||||||
|
mute,
|
||||||
|
unmute,
|
||||||
|
toggleMute,
|
||||||
|
|
||||||
duration,
|
duration,
|
||||||
currentTime,
|
currentTime,
|
|
@ -19,11 +19,11 @@ export default (maxPreloaded: MaybeRef<number>) => {
|
||||||
if (toRemove > 0 && !cleaningCache.value) {
|
if (toRemove > 0 && !cleaningCache.value) {
|
||||||
cleaningCache.value = true
|
cleaningCache.value = true
|
||||||
|
|
||||||
const excess = sortBy(soundCache.values(), [(cached: CachedSound) => cached.date])
|
const excess = sortBy([...soundCache.values()], [(cached: CachedSound) => cached.date])
|
||||||
// TODO (wvffle): Check if works
|
.slice(0, toRemove)
|
||||||
.slice(0, toRemove) as unknown as CachedSound[]
|
|
||||||
|
|
||||||
for (const cached of excess) {
|
for (const cached of excess) {
|
||||||
|
console.log('Removing cached element:', cached)
|
||||||
soundCache.delete(cached.id)
|
soundCache.delete(cached.id)
|
||||||
cached.sound.unload()
|
cached.sound.unload()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Track } from "~/types"
|
import { Track } from "~/types"
|
||||||
import { useStore } from '~/store'
|
import store from '~/store'
|
||||||
import updateQueryString from '~/composables/updateQueryString'
|
import updateQueryString from '~/composables/updateQueryString'
|
||||||
|
|
||||||
export interface TrackSource {
|
export interface TrackSource {
|
||||||
|
@ -8,7 +8,6 @@ export interface TrackSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (trackData: Track): TrackSource[] => {
|
export default (trackData: Track): TrackSource[] => {
|
||||||
const store = useStore()
|
|
||||||
const audio = document.createElement('audio')
|
const audio = document.createElement('audio')
|
||||||
|
|
||||||
const allowed = ['probably', 'maybe']
|
const allowed = ['probably', 'maybe']
|
||||||
|
|
|
@ -7,11 +7,11 @@ export const install: InitModule = ({ app }) => {
|
||||||
// this is needed to unlock audio playing under some browsers,
|
// this is needed to unlock audio playing under some browsers,
|
||||||
// cf https://github.com/goldfire/howler.js#mobilechrome-playback
|
// cf https://github.com/goldfire/howler.js#mobilechrome-playback
|
||||||
// but we never actually load those audio files
|
// but we never actually load those audio files
|
||||||
const dummyAudio = new Howl({
|
// const dummyAudio = new Howl({
|
||||||
preload: false,
|
// preload: false,
|
||||||
autoplay: false,
|
// autoplay: false,
|
||||||
src: ['noop.webm', 'noop.mp3']
|
// src: ['noop.webm', 'noop.mp3']
|
||||||
})
|
// })
|
||||||
|
|
||||||
return dummyAudio
|
// return dummyAudio
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { InitModule } from '~/types'
|
import { InitModule } from '~/types'
|
||||||
import { whenever } from '@vueuse/core'
|
import { whenever } from '@vueuse/core'
|
||||||
import useQueue from '~/composables/useQueue'
|
import useQueue from '~/composables/audio/useQueue'
|
||||||
import usePlayer from '~/composables/usePlayer'
|
import usePlayer from '~/composables/audio/usePlayer'
|
||||||
|
|
||||||
export const install: InitModule = ({ app }) => {
|
export const install: InitModule = ({ app }) => {
|
||||||
const { currentTrack, next, previous } = useQueue()
|
const { currentTrack, next, previous } = useQueue()
|
||||||
|
|
|
@ -95,10 +95,6 @@ const store: Module<State, RootState> = {
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
durationFormatted: state => {
|
durationFormatted: state => {
|
||||||
if (state.duration % 1 !== 0) {
|
|
||||||
return time.parse(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return time.parse(Math.round(state.duration))
|
return time.parse(Math.round(state.duration))
|
||||||
},
|
},
|
||||||
currentTimeFormatted: state => {
|
currentTimeFormatted: state => {
|
||||||
|
|
Loading…
Reference in New Issue