diff --git a/neode-ui/src/components/GlobalAudioPlayer.vue b/neode-ui/src/components/GlobalAudioPlayer.vue index ffb52363..ec6e2443 100644 --- a/neode-ui/src/components/GlobalAudioPlayer.vue +++ b/neode-ui/src/components/GlobalAudioPlayer.vue @@ -25,7 +25,11 @@ class="flex-shrink-0 w-9 h-9 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center transition-colors" @click="togglePlay" > - + + + + + diff --git a/neode-ui/src/composables/useAudioPlayer.ts b/neode-ui/src/composables/useAudioPlayer.ts index 70f5ea03..05d64f68 100644 --- a/neode-ui/src/composables/useAudioPlayer.ts +++ b/neode-ui/src/composables/useAudioPlayer.ts @@ -4,6 +4,7 @@ const audio = ref(null) const currentSrc = ref(null) const currentName = ref('') const playing = ref(false) +const loading = ref(false) const currentTime = ref(0) const duration = ref(0) const error = ref(null) @@ -21,8 +22,22 @@ function init() { duration.value = audio.value?.duration ?? 0 error.value = null }) + // Buffering / connecting over mesh|Tor → show a loader until it can play. + audio.value.addEventListener('loadstart', () => { + loading.value = true + }) + audio.value.addEventListener('waiting', () => { + loading.value = true + }) + audio.value.addEventListener('canplay', () => { + loading.value = false + }) + audio.value.addEventListener('playing', () => { + loading.value = false + }) audio.value.addEventListener('ended', () => { playing.value = false + loading.value = false }) audio.value.addEventListener('pause', () => { playing.value = false @@ -33,7 +48,8 @@ function init() { }) audio.value.addEventListener('error', () => { playing.value = false - error.value = 'Could not play audio. File Browser may not be running.' + loading.value = false + error.value = 'Could not play this audio file. The peer may be offline, or the file may be unavailable.' }) } @@ -47,6 +63,7 @@ function play(src: string, name: string) { } if (currentSrc.value !== src) { + loading.value = true audio.value!.src = src currentSrc.value = src currentName.value = name @@ -87,6 +104,7 @@ export function useAudioPlayer() { seek, stop, playing, + loading, currentName, currentTime, duration, diff --git a/neode-ui/src/views/PeerFiles.vue b/neode-ui/src/views/PeerFiles.vue index 8af3c972..05558b4f 100644 --- a/neode-ui/src/views/PeerFiles.vue +++ b/neode-ui/src/views/PeerFiles.vue @@ -234,12 +234,30 @@ - +
+
@@ -320,6 +338,10 @@ const audioPlayer = useAudioPlayer() const videoPlayerItem = ref(null) const videoPlayerUrl = ref(null) const videoPlayerPaid = ref(false) +// Streaming a peer's file connects over mesh/Tor before the first frame, so +// show a loader until the element can actually play (or errors). +const videoLoading = ref(false) +const videoError = ref(false) const peerDisplayName = computed(() => { if (currentPeer.value?.name) return currentPeer.value.name @@ -604,8 +626,19 @@ function closeVideoPlayer() { videoPlayerItem.value = null videoPlayerUrl.value = null videoPlayerPaid.value = false + videoLoading.value = false + videoError.value = false } +// Show the loader the moment a video opens; the element's playing/canplay/error +// events clear it. +watch(videoPlayerUrl, (url) => { + if (url) { + videoLoading.value = true + videoError.value = false + } +}) + function triggerDownload(base64Data: string, item: CatalogItem) { const blob = new Blob( [Uint8Array.from(atob(base64Data), c => c.charCodeAt(0))],