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