Update:Syncing playback time when media item is open in player

This commit is contained in:
advplyr 2023-06-19 12:37:44 -05:00
parent b4bf10d409
commit ff4f8324e7
25 changed files with 149 additions and 177 deletions

View file

@ -305,9 +305,9 @@ data class PodcastEpisode(
.setSubtitle(libraryItemDescription.title) .setSubtitle(libraryItemDescription.title)
.setExtras(extras) .setExtras(extras)
libraryItemDescription.iconBitmap?.let { // libraryItemDescription.iconBitmap?.let {
mediaDescriptionBuilder.setIconBitmap(it) // mediaDescriptionBuilder.setIconBitmap(it)
} // }
return mediaDescriptionBuilder.build() return mediaDescriptionBuilder.build()
} }

View file

@ -110,17 +110,17 @@ class LocalLibraryItem(
override fun getMediaDescription(progress:MediaProgressWrapper?, ctx:Context?): MediaDescriptionCompat { override fun getMediaDescription(progress:MediaProgressWrapper?, ctx:Context?): MediaDescriptionCompat {
val coverUri = getCoverUri() val coverUri = getCoverUri()
var bitmap:Bitmap? = null // var bitmap:Bitmap? = null
if (coverContentUrl != null) { // if (coverContentUrl != null) {
ctx?.let { // ctx?.let {
bitmap = if (Build.VERSION.SDK_INT < 28) { // bitmap = if (Build.VERSION.SDK_INT < 28) {
MediaStore.Images.Media.getBitmap(it.contentResolver, coverUri) // MediaStore.Images.Media.getBitmap(it.contentResolver, coverUri)
} else { // } else {
val source: ImageDecoder.Source = ImageDecoder.createSource(it.contentResolver, coverUri) // val source: ImageDecoder.Source = ImageDecoder.createSource(it.contentResolver, coverUri)
ImageDecoder.decodeBitmap(source) // ImageDecoder.decodeBitmap(source)
} // }
} // }
} // }
val extras = Bundle() val extras = Bundle()
extras.putLong( extras.putLong(
@ -156,9 +156,9 @@ class LocalLibraryItem(
.setSubtitle(authorName) .setSubtitle(authorName)
.setExtras(extras) .setExtras(extras)
bitmap?.let { // bitmap?.let {
mediaDescriptionBuilder.setIconBitmap(bitmap) // mediaDescriptionBuilder.setIconBitmap(bitmap)
} // }
return mediaDescriptionBuilder.build() return mediaDescriptionBuilder.build()
} }

View file

@ -1,10 +1,7 @@
package com.audiobookshelf.app.data package com.audiobookshelf.app.data
import android.content.Context import android.content.Context
import android.graphics.ImageDecoder
import android.net.Uri import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.support.v4.media.MediaMetadataCompat import android.support.v4.media.MediaMetadataCompat
import com.audiobookshelf.app.BuildConfig import com.audiobookshelf.app.BuildConfig
import com.audiobookshelf.app.R import com.audiobookshelf.app.R
@ -168,16 +165,16 @@ class PlaybackSession(
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, getCoverUri().toString()) .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, getCoverUri().toString())
// Local covers get bitmap // Local covers get bitmap
if (localLibraryItem?.coverContentUrl != null) { // if (localLibraryItem?.coverContentUrl != null) {
val bitmap = if (Build.VERSION.SDK_INT < 28) { // val bitmap = if (Build.VERSION.SDK_INT < 28) {
MediaStore.Images.Media.getBitmap(ctx.contentResolver, getCoverUri()) // MediaStore.Images.Media.getBitmap(ctx.contentResolver, getCoverUri())
} else { // } else {
val source: ImageDecoder.Source = ImageDecoder.createSource(ctx.contentResolver, getCoverUri()) // val source: ImageDecoder.Source = ImageDecoder.createSource(ctx.contentResolver, getCoverUri())
ImageDecoder.decodeBitmap(source) // ImageDecoder.decodeBitmap(source)
} // }
metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap) // metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap)
metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmap) // metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmap)
} // }
return metadataBuilder.build() return metadataBuilder.build()
} }

View file

@ -215,7 +215,6 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
val listeningTimeToAdd = diffSinceLastSync / 1000L val listeningTimeToAdd = diffSinceLastSync / 1000L
val syncData = MediaProgressSyncData(listeningTimeToAdd,currentPlaybackDuration,currentTime) val syncData = MediaProgressSyncData(listeningTimeToAdd,currentPlaybackDuration,currentTime)
currentPlaybackSession?.syncData(syncData) currentPlaybackSession?.syncData(syncData)
if (currentPlaybackSession?.progress?.isNaN() == true) { if (currentPlaybackSession?.progress?.isNaN() == true) {
@ -243,11 +242,6 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
// Send sync to server also if connected to this server and local item belongs to this server // Send sync to server also if connected to this server and local item belongs to this server
if (hasNetworkConnection && shouldSyncServer && !it.libraryItemId.isNullOrEmpty() && it.serverConnectionConfigId != null && DeviceManager.serverConnectionConfig?.id == it.serverConnectionConfigId) { if (hasNetworkConnection && shouldSyncServer && !it.libraryItemId.isNullOrEmpty() && it.serverConnectionConfigId != null && DeviceManager.serverConnectionConfig?.id == it.serverConnectionConfigId) {
apiHandler.sendLocalProgressSync(it) { syncSuccess, errorMsg -> apiHandler.sendLocalProgressSync(it) { syncSuccess, errorMsg ->
Log.d(
tag,
"Local progress sync data sent to server $currentDisplayTitle for time $currentTime"
)
if (syncSuccess) { if (syncSuccess) {
failedSyncs = 0 failedSyncs = 0
playerNotificationService.alertSyncSuccess() playerNotificationService.alertSyncSuccess()

View file

@ -4,9 +4,7 @@ import android.annotation.SuppressLint
import android.app.* import android.app.*
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import android.graphics.ImageDecoder
import android.hardware.Sensor import android.hardware.Sensor
import android.hardware.SensorManager import android.hardware.SensorManager
import android.net.ConnectivityManager import android.net.ConnectivityManager
@ -14,7 +12,6 @@ import android.net.Network
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.net.NetworkRequest import android.net.NetworkRequest
import android.os.* import android.os.*
import android.provider.MediaStore
import android.provider.Settings import android.provider.Settings
import android.support.v4.media.MediaBrowserCompat import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaDescriptionCompat import android.support.v4.media.MediaDescriptionCompat
@ -292,18 +289,18 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
return MediaDescriptionCompat.Builder().build() return MediaDescriptionCompat.Builder().build()
} }
val coverUri = currentPlaybackSession!!.getCoverUri() var coverUri = currentPlaybackSession!!.getCoverUri()
var bitmap:Bitmap? = null // var bitmap:Bitmap? = null
// Local covers get bitmap // Local covers get bitmap
if (currentPlaybackSession!!.localLibraryItem?.coverContentUrl != null) { // if (currentPlaybackSession!!.localLibraryItem?.coverContentUrl != null) {
bitmap = if (Build.VERSION.SDK_INT < 28) { // bitmap = if (Build.VERSION.SDK_INT < 28) {
MediaStore.Images.Media.getBitmap(ctx.contentResolver, coverUri) // MediaStore.Images.Media.getBitmap(ctx.contentResolver, coverUri)
} else { // } else {
val source: ImageDecoder.Source = ImageDecoder.createSource(ctx.contentResolver, coverUri) // val source: ImageDecoder.Source = ImageDecoder.createSource(ctx.contentResolver, coverUri)
ImageDecoder.decodeBitmap(source) // ImageDecoder.decodeBitmap(source)
} // }
} // }
// Fix for local images crashing on Android 11 for specific devices // Fix for local images crashing on Android 11 for specific devices
// https://stackoverflow.com/questions/64186578/android-11-mediastyle-notification-crash/64232958#64232958 // https://stackoverflow.com/questions/64186578/android-11-mediastyle-notification-crash/64232958#64232958
@ -325,9 +322,9 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
.setTitle(currentPlaybackSession!!.displayTitle) .setTitle(currentPlaybackSession!!.displayTitle)
.setIconUri(coverUri) .setIconUri(coverUri)
bitmap?.let { // bitmap?.let {
mediaDescriptionBuilder.setIconBitmap(it) // mediaDescriptionBuilder.setIconBitmap(it)
} // }
return mediaDescriptionBuilder.build() return mediaDescriptionBuilder.build()
} }

View file

@ -79,7 +79,7 @@ export default {
}, },
methods: { methods: {
castClick() { castClick() {
if (this.$store.state.playerIsLocal) { if (this.$store.getters['getIsCurrentSessionLocal']) {
this.$eventBus.$emit('cast-local-item') this.$eventBus.$emit('cast-local-item')
return return
} }

View file

@ -574,8 +574,8 @@ export default {
AbsAudioPlayer.seek({ value: Math.floor(time) }) AbsAudioPlayer.seek({ value: Math.floor(time) })
if (this.$refs.playedTrack) { if (this.$refs.playedTrack) {
var perc = time / this.totalDuration const perc = time / this.totalDuration
var ptWidth = Math.round(perc * this.trackWidth) const ptWidth = Math.round(perc * this.trackWidth)
this.$refs.playedTrack.style.width = ptWidth + 'px' this.$refs.playedTrack.style.width = ptWidth + 'px'
this.$refs.playedTrack.classList.remove('bg-gray-200') this.$refs.playedTrack.classList.remove('bg-gray-200')
@ -721,7 +721,7 @@ export default {
AbsAudioPlayer.closePlayback() AbsAudioPlayer.closePlayback()
}, },
endPlayback() { endPlayback() {
this.$store.commit('setPlayerItem', null) this.$store.commit('setPlaybackSession', null)
this.showFullscreen = false this.showFullscreen = false
this.isEnded = false this.isEnded = false
this.isLoading = false this.isLoading = false
@ -767,7 +767,7 @@ export default {
this.isEnded = false this.isEnded = false
this.isLoading = true this.isLoading = true
this.syncStatus = 0 this.syncStatus = 0
this.$store.commit('setPlayerItem', this.playbackSession) this.$store.commit('setPlaybackSession', this.playbackSession)
// Set track width // Set track width
this.$nextTick(() => { this.$nextTick(() => {

View file

@ -110,7 +110,7 @@ export default {
}, },
streamProgress(data) { streamProgress(data) {
if (!data.numSegments) return if (!data.numSegments) return
var chunks = data.chunks const chunks = data.chunks
if (this.$refs.audioPlayer) { if (this.$refs.audioPlayer) {
this.$refs.audioPlayer.setChunksReady(chunks, data.numSegments) this.$refs.audioPlayer.setChunksReady(chunks, data.numSegments)
} }
@ -265,8 +265,7 @@ export default {
this.$store.commit('globals/updateLocalMediaProgress', localMediaProgress) this.$store.commit('globals/updateLocalMediaProgress', localMediaProgress)
}, },
onMediaPlayerChanged(data) { onMediaPlayerChanged(data) {
var mediaPlayer = data.value this.$store.commit('setMediaPlayer', data.value)
this.$store.commit('setMediaPlayer', mediaPlayer)
}, },
onReady() { onReady() {
// The UI is reporting elsewhere we are ready // The UI is reporting elsewhere we are ready
@ -283,6 +282,9 @@ export default {
this.$store.commit('setIsFirstAudioLoad', false) // Only run this once on app launch this.$store.commit('setIsFirstAudioLoad', false) // Only run this once on app launch
AbsAudioPlayer.onReady() AbsAudioPlayer.onReady()
} }
},
playbackTimeUpdate(currentTime) {
this.$refs.audioPlayer?.seek(currentTime)
} }
}, },
mounted() { mounted() {
@ -300,6 +302,7 @@ export default {
this.$eventBus.$on('close-stream', this.closeStreamOnly) this.$eventBus.$on('close-stream', this.closeStreamOnly)
this.$eventBus.$on('cast-local-item', this.castLocalItem) this.$eventBus.$on('cast-local-item', this.castLocalItem)
this.$eventBus.$on('user-settings', this.settingsUpdated) this.$eventBus.$on('user-settings', this.settingsUpdated)
this.$eventBus.$on('playback-time-update', this.playbackTimeUpdate)
}, },
beforeDestroy() { beforeDestroy() {
if (this.onLocalMediaProgressUpdateListener) this.onLocalMediaProgressUpdateListener.remove() if (this.onLocalMediaProgressUpdateListener) this.onLocalMediaProgressUpdateListener.remove()
@ -313,6 +316,7 @@ export default {
this.$eventBus.$off('close-stream', this.closeStreamOnly) this.$eventBus.$off('close-stream', this.closeStreamOnly)
this.$eventBus.$off('cast-local-item', this.castLocalItem) this.$eventBus.$off('cast-local-item', this.castLocalItem)
this.$eventBus.$off('user-settings', this.settingsUpdated) this.$eventBus.$off('user-settings', this.settingsUpdated)
this.$eventBus.$off('playback-time-update', this.playbackTimeUpdate)
} }
} }
</script> </script>

View file

@ -213,7 +213,7 @@ export default {
return this.mediaMetadata.series return this.mediaMetadata.series
}, },
seriesSequence() { seriesSequence() {
return this.series ? this.series.sequence : null return this.series?.sequence || null
}, },
recentEpisode() { recentEpisode() {
// Only added to item when getting currently listening podcasts // Only added to item when getting currently listening podcasts
@ -232,14 +232,14 @@ export default {
}, },
booksInSeries() { booksInSeries() {
// Only added to item object when collapseSeries is enabled // Only added to item object when collapseSeries is enabled
return this.collapsedSeries ? this.collapsedSeries.numBooks : 0 return this.collapsedSeries?.numBooks || 0
}, },
seriesSequenceList() { seriesSequenceList() {
return this.collapsedSeries ? this.collapsedSeries.seriesSequenceList : null return this.collapsedSeries?.seriesSequenceList || null
}, },
libraryItemIdsInSeries() { libraryItemIdsInSeries() {
// Only added to item object when collapseSeries is enabled // Only added to item object when collapseSeries is enabled
return this.collapsedSeries ? this.collapsedSeries.libraryItemIds || [] : [] return this.collapsedSeries?.libraryItemIds || []
}, },
displayTitle() { displayTitle() {
if (this.recentEpisode) return this.recentEpisode.title if (this.recentEpisode) return this.recentEpisode.title
@ -291,20 +291,18 @@ export default {
showError() { showError() {
return this.numMissingParts || this.isMissing || this.isInvalid return this.numMissingParts || this.isMissing || this.isInvalid
}, },
playerIsLocal() {
return !!this.$store.state.playerIsLocal
},
localLibraryItemId() { localLibraryItemId() {
if (this.isLocal) return this.libraryItemId if (this.isLocal) return this.libraryItemId
return this.localLibraryItem ? this.localLibraryItem.id : null return this.localLibraryItem?.id || null
},
localEpisode() {
if (!this.recentEpisode || !this.localLibraryItem) return null
// Current recentEpisode is only implemented server side so this will always be the serverEpisodeId
return this.localLibraryItem.media.episodes.find((ep) => ep.serverEpisodeId === this.recentEpisode.id)
}, },
isStreaming() { isStreaming() {
if (this.isPodcast) { if (this.isPodcast) {
if (this.playerIsLocal) { return this.$store.getters['getIsMediaStreaming'](this.libraryItemId, this.recentEpisode.id)
// Check is streaming local version of this episode
return false // episode cards not implemented for local yet
}
return this.$store.getters['getIsEpisodeStreaming'](this.libraryItemId, this.recentEpisode.id)
} else { } else {
return false // not yet necessary for books return false // not yet necessary for books
} }
@ -380,8 +378,7 @@ export default {
showHasLocalDownload() { showHasLocalDownload() {
if (this.localLibraryItem || this.isLocal) { if (this.localLibraryItem || this.isLocal) {
if (this.recentEpisode && !this.isLocal) { if (this.recentEpisode && !this.isLocal) {
const localEpisode = this.localLibraryItem.media.episodes.find((ep) => ep.serverEpisodeId === this.recentEpisode.id) return !!this.localEpisode
return !!localEpisode
} else { } else {
return true return true
} }
@ -436,19 +433,16 @@ export default {
return return
} }
if (this.localLibraryItem) { if (this.localEpisode) {
const localEpisode = this.localLibraryItem.media.episodes.find((ep) => ep.serverEpisodeId === this.recentEpisode.id)
if (localEpisode) {
// Play episode locally // Play episode locally
eventBus.$emit('play-item', { eventBus.$emit('play-item', {
libraryItemId: this.localLibraryItemId, libraryItemId: this.localLibraryItemId,
episodeId: localEpisode.id, episodeId: this.localEpisode.id,
serverLibraryItemId: this.libraryItemId, serverLibraryItemId: this.libraryItemId,
serverEpisodeId: this.recentEpisode.id serverEpisodeId: this.recentEpisode.id
}) })
return return
} }
}
eventBus.$emit('play-item', { libraryItemId: this.libraryItemId, episodeId: this.recentEpisode.id }) eventBus.$emit('play-item', { libraryItemId: this.libraryItemId, episodeId: this.recentEpisode.id })
}, },

View file

@ -23,7 +23,7 @@
</div> </div>
</div> </div>
<div class="fixed left-0 h-8 w-full bg-primary px-4 flex items-center text-white/80" :style="{ bottom: playerLibraryItemId ? '120px' : '0px' }"> <div class="fixed left-0 h-8 w-full bg-primary px-4 flex items-center text-white/80" :style="{ bottom: isPlayerOpen ? '120px' : '0px' }">
<div class="flex-grow" /> <div class="flex-grow" />
<p class="text-xs">{{ page }} / {{ numPages }}</p> <p class="text-xs">{{ page }} / {{ numPages }}</p>
</div> </div>
@ -148,8 +148,8 @@ export default {
} }
}) })
}, },
playerLibraryItemId() { isPlayerOpen() {
return this.$store.state.playerLibraryItemId return this.$store.getters['getIsPlayerOpen']
} }
}, },
methods: { methods: {

View file

@ -2,7 +2,7 @@
<div id="epub-frame" class="w-full"> <div id="epub-frame" class="w-full">
<div id="viewer" class="h-full w-full"></div> <div id="viewer" class="h-full w-full"></div>
<div class="fixed left-0 h-8 w-full px-4 flex items-center" :class="isLightTheme ? 'bg-white text-black' : 'bg-primary text-white/80'" :style="{ bottom: playerLibraryItemId ? '120px' : '0px' }"> <div class="fixed left-0 h-8 w-full px-4 flex items-center" :class="isLightTheme ? 'bg-white text-black' : 'bg-primary text-white/80'" :style="{ bottom: isPlayerOpen ? '120px' : '0px' }">
<div class="flex-grow" /> <div class="flex-grow" />
<p class="text-xs">{{ progress }}%</p> <p class="text-xs">{{ progress }}%</p>
</div> </div>
@ -37,7 +37,7 @@ export default {
} }
}, },
watch: { watch: {
playerLibraryItemId() { isPlayerOpen() {
this.updateHeight() this.updateHeight()
} }
}, },
@ -65,11 +65,11 @@ export default {
} }
return null return null
}, },
playerLibraryItemId() { isPlayerOpen() {
return this.$store.state.playerLibraryItemId return this.$store.getters['getIsPlayerOpen']
}, },
readerHeightOffset() { readerHeightOffset() {
return this.playerLibraryItemId ? 204 : 84 return this.isPlayerOpen ? 204 : 104
}, },
/** @returns {Array<ePub.NavItem>} */ /** @returns {Array<ePub.NavItem>} */
chapters() { chapters() {

View file

@ -23,9 +23,6 @@ export default {
return {} return {}
}, },
computed: { computed: {
playerLibraryItemId() {
return this.$store.state.playerLibraryItemId
},
userToken() { userToken() {
return this.$store.getters['user/getToken'] return this.$store.getters['user/getToken']
} }

View file

@ -18,7 +18,7 @@
</div> </div>
</div> </div>
<div class="fixed left-0 h-8 w-full bg-primary px-4 flex items-center text-white/80" :style="{ bottom: playerLibraryItemId ? '120px' : '0px' }"> <div class="fixed left-0 h-8 w-full bg-primary px-4 flex items-center text-white/80" :style="{ bottom: isPlayerOpen ? '120px' : '0px' }">
<div class="flex-grow" /> <div class="flex-grow" />
<p class="text-xs">{{ page }} / {{ numPages }}</p> <p class="text-xs">{{ page }} / {{ numPages }}</p>
</div> </div>
@ -114,8 +114,8 @@ export default {
} }
} }
}, },
playerLibraryItemId() { isPlayerOpen() {
return this.$store.state.playerLibraryItemId return this.$store.getters['getIsPlayerOpen']
} }
}, },
methods: { methods: {

View file

@ -1,5 +1,5 @@
<template> <template>
<div v-if="show" :data-theme="ereaderTheme" class="group fixed top-0 left-0 right-0 layout-wrapper w-full z-40 pt-8 data-[theme=dark]:bg-primary data-[theme=dark]:text-white data-[theme=light]:bg-white data-[theme=light]:text-black" :class="{ 'reader-player-open': !!playerLibraryItemId }"> <div v-if="show" :data-theme="ereaderTheme" class="group fixed top-0 left-0 right-0 layout-wrapper w-full z-40 pt-8 data-[theme=dark]:bg-primary data-[theme=dark]:text-white data-[theme=light]:bg-white data-[theme=light]:text-black" :class="{ 'reader-player-open': isPlayerOpen }">
<!-- toolbar --> <!-- toolbar -->
<div class="h-32 pt-10 w-full px-2 fixed top-0 left-0 z-30 transition-transform bg-bg text-white" :class="showingToolbar ? 'translate-y-0' : '-translate-y-32'" @touchstart.stop @mousedown.stop @touchend.stop @mouseup.stop> <div class="h-32 pt-10 w-full px-2 fixed top-0 left-0 z-30 transition-transform bg-bg text-white" :class="showingToolbar ? 'translate-y-0' : '-translate-y-32'" @touchstart.stop @mousedown.stop @touchend.stop @mouseup.stop>
<div class="flex items-center mb-2"> <div class="flex items-center mb-2">
@ -213,8 +213,8 @@ export default {
} }
return `${serverAddress}/api/items/${this.selectedLibraryItem.id}/ebook` return `${serverAddress}/api/items/${this.selectedLibraryItem.id}/ebook`
}, },
playerLibraryItemId() { isPlayerOpen() {
return this.$store.state.playerLibraryItemId return this.$store.getters['getIsPlayerOpen']
}, },
keepProgress() { keepProgress() {
return this.$store.state.ereaderKeepProgress return this.$store.state.ereaderKeepProgress

View file

@ -74,7 +74,7 @@ export default {
return !this.isMissing && !this.isInvalid && this.tracks.length return !this.isMissing && !this.isInvalid && this.tracks.length
}, },
isStreaming() { isStreaming() {
return this.$store.getters['getIsEpisodeStreaming'](this.libraryItemId) return this.$store.getters['getIsMediaStreaming'](this.libraryItemId)
}, },
streamIsPlaying() { streamIsPlaying() {
return this.$store.state.playerIsPlaying && this.isStreaming return this.$store.state.playerIsPlaying && this.isStreaming

View file

@ -102,8 +102,8 @@ export default {
return !this.isMissing && !this.isInvalid && (this.tracks.length || this.episode) return !this.isMissing && !this.isInvalid && (this.tracks.length || this.episode)
}, },
isStreaming() { isStreaming() {
if (this.localLibraryItem && this.$store.getters['getIsEpisodeStreaming'](this.localLibraryItem.id, this.localEpisode?.id)) return true if (this.localLibraryItem && this.localEpisode && this.$store.getters['getIsMediaStreaming'](this.localLibraryItem.id, this.localEpisode.id)) return true
return this.$store.getters['getIsEpisodeStreaming'](this.libraryItem.id, this.episodeId) return this.$store.getters['getIsMediaStreaming'](this.libraryItem.id, this.episodeId)
}, },
streamIsPlaying() { streamIsPlaying() {
return this.$store.state.playerIsPlaying && this.isStreaming return this.$store.state.playerIsPlaying && this.isStreaming

View file

@ -114,14 +114,7 @@ export default {
return this.$secondsToTimestamp(this.episode.duration) return this.$secondsToTimestamp(this.episode.duration)
}, },
isStreaming() { isStreaming() {
if (this.playerIsLocal && this.localLibraryItemId && this.localEpisode) { return this.$store.getters['getIsMediaStreaming'](this.libraryItemId, this.episode.id)
// Check is streaming local version of this episode
return this.$store.getters['getIsEpisodeStreaming'](this.localLibraryItemId, this.localEpisode.id)
}
return this.$store.getters['getIsEpisodeStreaming'](this.libraryItemId, this.episode.id)
},
playerIsLocal() {
return !!this.$store.state.playerIsLocal
}, },
streamIsPlaying() { streamIsPlaying() {
return this.$store.state.playerIsPlaying && this.isStreaming return this.$store.state.playerIsPlaying && this.isStreaming

View file

@ -117,14 +117,7 @@ export default {
return this.$secondsToTimestamp(this.episode.duration) return this.$secondsToTimestamp(this.episode.duration)
}, },
isStreaming() { isStreaming() {
if (this.playerIsLocal && this.localLibraryItemId && this.localEpisode) { return this.$store.getters['getIsMediaStreaming'](this.libraryItemId, this.episode.id)
// Check is streaming local version of this episode
return this.$store.getters['getIsEpisodeStreaming'](this.localLibraryItemId, this.localEpisode.id)
}
return this.$store.getters['getIsEpisodeStreaming'](this.libraryItemId, this.episode.id)
},
playerIsLocal() {
return !!this.$store.state.playerIsLocal
}, },
streamIsPlaying() { streamIsPlaying() {
return this.$store.state.playerIsPlaying && this.isStreaming return this.$store.state.playerIsPlaying && this.isStreaming

View file

@ -1,7 +1,7 @@
<template> <template>
<div class="w-full layout-wrapper bg-bg text-white"> <div class="w-full layout-wrapper bg-bg text-white">
<app-appbar /> <app-appbar />
<div id="content" class="overflow-hidden relative" :class="playerIsOpen ? 'playerOpen' : ''"> <div id="content" class="overflow-hidden relative" :class="isPlayerOpen ? 'playerOpen' : ''">
<Nuxt /> <Nuxt />
<div v-if="attemptingConnection" class="absolute top-0 left-0 z-50 w-full h-full flex items-center justify-center"> <div v-if="attemptingConnection" class="absolute top-0 left-0 z-50 w-full h-full flex items-center justify-center">
@ -57,11 +57,8 @@ export default {
} }
}, },
computed: { computed: {
playerIsOpen() { isPlayerOpen() {
return this.$store.state.playerLibraryItemId return this.$store.getters['getIsPlayerOpen']
},
readerIsOpen() {
return this.$store.state.showReader
}, },
routeName() { routeName() {
return this.$route.name return this.$route.name
@ -86,7 +83,7 @@ export default {
}, },
methods: { methods: {
initialStream(stream) { initialStream(stream) {
if (this.$refs.streamContainer && this.$refs.streamContainer.audioPlayerReady) { if (this.$refs.streamContainer?.audioPlayerReady) {
this.$refs.streamContainer.streamOpen(stream) this.$refs.streamContainer.streamOpen(stream)
} }
}, },
@ -234,8 +231,16 @@ export default {
this.$store.commit('user/setUser', user) this.$store.commit('user/setUser', user)
} }
}, },
async userMediaProgressUpdated(prog) { async userMediaProgressUpdated(payload) {
console.log(`[default] userMediaProgressUpdate checking for local media progress ${prog.id}`) const prog = payload.data // MediaProgress
console.log(`[default] userMediaProgressUpdate checking for local media progress ${payload.id}`)
// Check if this media item is currently open in the player, paused, and this progress update is coming from a different session
const isMediaOpenInPlayer = this.$store.getters['getIsMediaStreaming'](prog.libraryItemId, prog.episodeId)
if (isMediaOpenInPlayer && this.$store.getters['getCurrentPlaybackSessionId'] !== payload.sessionId && !this.$store.state.playerIsPlaying) {
console.log('[default] userMediaProgressUpdated for current open media item', payload.data.currentTime)
this.$eventBus.$emit('playback-time-update', payload.data.currentTime)
}
// Update local media progress if exists // Update local media progress if exists
const localProg = await this.$db.getLocalMediaProgressForServerItem({ libraryItemId: prog.libraryItemId, episodeId: prog.episodeId }) const localProg = await this.$db.getLocalMediaProgressForServerItem({ libraryItemId: prog.libraryItemId, episodeId: prog.episodeId })
@ -287,7 +292,7 @@ export default {
} }
} }
if (newLocalMediaProgress && newLocalMediaProgress.id) { if (newLocalMediaProgress?.id) {
console.log(`[default] local media progress updated for ${newLocalMediaProgress.id}`) console.log(`[default] local media progress updated for ${newLocalMediaProgress.id}`)
this.$store.commit('globals/updateLocalMediaProgress', newLocalMediaProgress) this.$store.commit('globals/updateLocalMediaProgress', newLocalMediaProgress)
} }

View file

@ -75,7 +75,7 @@ export default {
}) })
}, },
streaming() { streaming() {
return !!this.playableBooks.find((b) => this.$store.getters['getIsItemStreaming'](b.id)) return !!this.playableBooks.find((b) => this.$store.getters['getIsMediaStreaming'](b.id))
}, },
showPlayButton() { showPlayButton() {
return this.playableBooks.length return this.playableBooks.length

View file

@ -209,14 +209,7 @@ export default {
return this.$store.getters['globals/getLibraryItemCoverSrcById'](this.libraryItemId) return this.$store.getters['globals/getLibraryItemCoverSrcById'](this.libraryItemId)
}, },
isPlaying() { isPlaying() {
if (this.playerIsLocal && this.localLibraryItemId && this.localEpisodeId) { return this.$store.getters['getIsMediaStreaming'](this.libraryItemId, this.episode.id)
// Check is streaming local version of this episode
return this.$store.getters['getIsEpisodeStreaming'](this.localLibraryItemId, this.localEpisodeId)
}
return this.$store.getters['getIsEpisodeStreaming'](this.libraryItemId, this.episode.id)
},
playerIsLocal() {
return !!this.$store.state.playerIsLocal
}, },
playerIsPlaying() { playerIsPlaying() {
return this.$store.state.playerIsPlaying && this.isPlaying return this.$store.state.playerIsPlaying && this.isPlaying

View file

@ -326,11 +326,11 @@ export default {
return this.userItemProgress ? this.userItemProgress.finishedAt : 0 return this.userItemProgress ? this.userItemProgress.finishedAt : 0
}, },
isStreaming() { isStreaming() {
return this.isPlaying && !this.$store.state.playerIsLocal return this.isPlaying && !this.$store.getters['getIsCurrentSessionLocal']
}, },
isPlaying() { isPlaying() {
if (this.localLibraryItemId && this.$store.getters['getIsItemStreaming'](this.localLibraryItemId)) return true if (this.localLibraryItemId && this.$store.getters['getIsMediaStreaming'](this.localLibraryItemId)) return true
return this.$store.getters['getIsItemStreaming'](this.libraryItemId) return this.$store.getters['getIsMediaStreaming'](this.libraryItemId)
}, },
playerIsPlaying() { playerIsPlaying() {
return this.$store.state.playerIsPlaying && (this.isStreaming || this.isPlaying) return this.$store.state.playerIsPlaying && (this.isStreaming || this.isPlaying)

View file

@ -108,7 +108,7 @@
<p class="text-lg text-center px-8">{{ failed ? 'Failed to get local library item ' + localLibraryItemId : 'Loading..' }}</p> <p class="text-lg text-center px-8">{{ failed ? 'Failed to get local library item ' + localLibraryItemId : 'Loading..' }}</p>
</div> </div>
<div v-if="orderChanged" class="fixed left-0 w-full py-4 px-4 bg-bg box-shadow-book flex items-center" :style="{ bottom: playerLibraryItemId ? '120px' : '0px' }"> <div v-if="orderChanged" class="fixed left-0 w-full py-4 px-4 bg-bg box-shadow-book flex items-center" :style="{ bottom: isPlayerOpen ? '120px' : '0px' }">
<div class="flex-grow" /> <div class="flex-grow" />
<ui-btn small color="success" @click="saveTrackOrder">Save Order</ui-btn> <ui-btn small color="success" @click="saveTrackOrder">Save Order</ui-btn>
</div> </div>
@ -156,8 +156,8 @@ export default {
} }
}, },
computed: { computed: {
playerLibraryItemId() { isPlayerOpen() {
return this.$store.state.playerLibraryItemId return this.$store.getters['getIsPlayerOpen']
}, },
isIos() { isIos() {
return this.$platform === 'ios' return this.$platform === 'ios'

View file

@ -88,11 +88,10 @@ class ServerSocket extends EventEmitter {
this.emit('user_updated', data) this.emit('user_updated', data)
} }
onUserItemProgressUpdated(data) { onUserItemProgressUpdated(payload) {
console.log('[SOCKET] User Item Progress Updated', JSON.stringify(data)) console.log('[SOCKET] User Item Progress Updated', JSON.stringify(payload))
var progress = data.data this.$store.commit('user/updateUserMediaProgress', payload.data)
this.$store.commit('user/updateUserMediaProgress', progress) this.emit('user_media_progress_updated', payload)
this.emit('user_media_progress_updated', progress)
} }
} }

View file

@ -3,9 +3,7 @@ import { AbsAudioPlayer } from '@/plugins/capacitor'
export const state = () => ({ export const state = () => ({
deviceData: null, deviceData: null,
playerLibraryItemId: null, currentPlaybackSession: null,
playerEpisodeId: null,
playerIsLocal: false,
playerIsPlaying: false, playerIsPlaying: false,
playerIsFullscreen: false, playerIsFullscreen: false,
isCasting: false, isCasting: false,
@ -30,16 +28,33 @@ export const state = () => ({
}) })
export const getters = { export const getters = {
getCurrentPlaybackSessionId: state => {
return state.currentPlaybackSession?.id || null
},
getIsPlayerOpen: state => {
return !!state.currentPlaybackSession
},
getIsCurrentSessionLocal: state => {
return state.currentPlaybackSession?.playMethod == this.$constants.PlayMethod.LOCAL
},
getIsMediaStreaming: state => (libraryItemId, episodeId) => { getIsMediaStreaming: state => (libraryItemId, episodeId) => {
if (!state.playerLibraryItemId) return null if (!state.currentPlaybackSession || !libraryItemId) return false
if (!episodeId) return state.playerLibraryItemId == libraryItemId
return state.playerLibraryItemId == libraryItemId && state.playerEpisodeId == episodeId // Check using local library item id and local episode id
}, const isLocalLibraryItemId = libraryItemId.startsWith('local_')
getIsItemStreaming: state => libraryItemId => { if (isLocalLibraryItemId) {
return state.playerLibraryItemId == libraryItemId if (state.currentPlaybackSession.localLibraryItem?.id !== libraryItemId) {
}, return false
getIsEpisodeStreaming: state => (libraryItemId, episodeId) => { }
return state.playerLibraryItemId == libraryItemId && state.playerEpisodeId == episodeId if (!episodeId) return true
return state.currentPlaybackSession.localEpisodeId === episodeId
}
if (state.currentPlaybackSession.libraryItemId !== libraryItemId) {
return false
}
if (!episodeId) return true
return state.currentPlaybackSession.episodeId === episodeId
}, },
getServerSetting: state => key => { getServerSetting: state => key => {
if (!state.serverSettings) return null if (!state.serverSettings) return null
@ -96,19 +111,10 @@ export const mutations = {
setLastItemScrollData(state, data) { setLastItemScrollData(state, data) {
state.lastItemScrollData = data state.lastItemScrollData = data
}, },
setPlayerItem(state, playbackSession) { setPlaybackSession(state, playbackSession) {
state.playerIsLocal = playbackSession ? playbackSession.playMethod == this.$constants.PlayMethod.LOCAL : false state.currentPlaybackSession = playbackSession
if (state.playerIsLocal) { state.isCasting = playbackSession?.mediaPlayer === "cast-player"
state.playerLibraryItemId = playbackSession ? playbackSession.localLibraryItem.id || null : null
state.playerEpisodeId = playbackSession ? playbackSession.localEpisodeId || null : null
} else {
state.playerLibraryItemId = playbackSession ? playbackSession.libraryItemId || null : null
state.playerEpisodeId = playbackSession ? playbackSession.episodeId || null : null
}
var mediaPlayer = playbackSession ? playbackSession.mediaPlayer : null
state.isCasting = mediaPlayer === "cast-player"
}, },
setMediaPlayer(state, mediaPlayer) { setMediaPlayer(state, mediaPlayer) {
state.isCasting = mediaPlayer === 'cast-player' state.isCasting = mediaPlayer === 'cast-player'