mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-29 06:18:51 +02:00
Fix:Remove local playback sessions started offline, refresh open player timestamps when device gains focus
This commit is contained in:
parent
f9621d2f60
commit
0ce3c8bcbd
5 changed files with 92 additions and 10 deletions
|
@ -231,6 +231,10 @@ class AbsDatabase : Plugin() {
|
|||
if (!success) {
|
||||
call.resolve(JSObject("{\"error\":\"$errorMsg\"}"))
|
||||
} else {
|
||||
// Remove all local sessions
|
||||
savedSessions.forEach {
|
||||
DeviceManager.dbManager.removePlaybackSession(it.id)
|
||||
}
|
||||
call.resolve()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -323,13 +323,15 @@ class ApiHandler(var ctx:Context) {
|
|||
|
||||
fun sendSyncLocalSessions(playbackSessions:List<PlaybackSession>, cb: (Boolean, String?) -> Unit) {
|
||||
val payload = JSObject(jacksonMapper.writeValueAsString(LocalSessionsSyncRequestPayload(playbackSessions)))
|
||||
|
||||
Log.d(tag, "Sending ${playbackSessions.size} saved local playback sessions to server")
|
||||
postRequest("/api/session/local-all", payload, null) {
|
||||
if (!it.getString("error").isNullOrEmpty()) {
|
||||
Log.e(tag, "Failed to sync local sessions")
|
||||
cb(false, it.getString("error"))
|
||||
} else {
|
||||
val response = jacksonMapper.readValue<LocalSessionsSyncResponsePayload>(it.toString())
|
||||
response.results.forEach { localSessionSyncResult ->
|
||||
Log.d(tag, "Synced session result ${localSessionSyncResult.id}|${localSessionSyncResult.progressSynced}|${localSessionSyncResult.success}")
|
||||
playbackSessions.find { ps -> ps.id == localSessionSyncResult.id }?.let { session ->
|
||||
if (localSessionSyncResult.progressSynced == true) {
|
||||
val syncResult = SyncResult(true, true, "Progress synced on server")
|
||||
|
@ -338,7 +340,6 @@ class ApiHandler(var ctx:Context) {
|
|||
} else if (!localSessionSyncResult.success) {
|
||||
Log.e(tag, "Failed to sync session ${session.displayTitle} with server. Error: ${localSessionSyncResult.error}")
|
||||
}
|
||||
DeviceManager.dbManager.removePlaybackSession(session.id)
|
||||
}
|
||||
}
|
||||
cb(true, null)
|
||||
|
|
|
@ -256,7 +256,7 @@ export default {
|
|||
})
|
||||
},
|
||||
pauseItem() {
|
||||
if (this.$refs.audioPlayer && !this.$refs.audioPlayer.isPaused) {
|
||||
if (this.$refs.audioPlayer && this.$refs.audioPlayer.isPlaying) {
|
||||
this.$refs.audioPlayer.pause()
|
||||
}
|
||||
},
|
||||
|
@ -285,6 +285,51 @@ export default {
|
|||
},
|
||||
playbackTimeUpdate(currentTime) {
|
||||
this.$refs.audioPlayer?.seek(currentTime)
|
||||
},
|
||||
/**
|
||||
* When device gains focus then refresh the timestamps in the audio player
|
||||
*/
|
||||
deviceFocused(hasFocus) {
|
||||
if (hasFocus) {
|
||||
if (!this.$refs.audioPlayer?.isPlaying) {
|
||||
const playbackSession = this.$store.state.currentPlaybackSession
|
||||
if (this.$refs.audioPlayer.isLocalPlayMethod) {
|
||||
const localLibraryItemId = playbackSession.localLibraryItem?.id
|
||||
const localEpisodeId = playbackSession.localEpisodeId
|
||||
if (!localLibraryItemId) {
|
||||
console.error('[AudioPlayerContainer] device visibility: no local library item for session', JSON.stringify(playbackSession))
|
||||
return
|
||||
}
|
||||
const localMediaProgress = this.$store.state.globals.localMediaProgress.find((mp) => {
|
||||
if (localEpisodeId) return mp.localEpisodeId === localEpisodeId
|
||||
return mp.localLibraryItemId === localLibraryItemId
|
||||
})
|
||||
if (localMediaProgress) {
|
||||
console.log('[AudioPlayerContainer] device visibility: found local media progress', localMediaProgress.currentTime, 'last time in player is', this.currentTime)
|
||||
this.$refs.audioPlayer.currentTime = localMediaProgress.currentTime
|
||||
this.$refs.audioPlayer.timeupdate()
|
||||
} else {
|
||||
console.error('[AudioPlayerContainer] device visibility: Local media progress not found')
|
||||
}
|
||||
} else {
|
||||
const libraryItemId = playbackSession.libraryItemId
|
||||
const episodeId = playbackSession.episodeId
|
||||
const url = episodeId ? `/api/me/progress/${libraryItemId}/${episodeId}` : `/api/me/progress/${libraryItemId}`
|
||||
this.$axios
|
||||
.$get(url)
|
||||
.then((data) => {
|
||||
if (!this.$refs.audioPlayer?.isPlaying && data.libraryItemId === libraryItemId) {
|
||||
console.log('[AudioPlayerContainer] device visibility: got server media progress', data.currentTime, 'last time in player is', this.currentTime)
|
||||
this.$refs.audioPlayer.currentTime = data.currentTime
|
||||
this.$refs.audioPlayer.timeupdate()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('[AudioPlayerContainer] device visibility: Failed to get progress', error)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -303,6 +348,7 @@ export default {
|
|||
this.$eventBus.$on('cast-local-item', this.castLocalItem)
|
||||
this.$eventBus.$on('user-settings', this.settingsUpdated)
|
||||
this.$eventBus.$on('playback-time-update', this.playbackTimeUpdate)
|
||||
this.$eventBus.$on('device-focus-update', this.deviceFocused)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.onLocalMediaProgressUpdateListener) this.onLocalMediaProgressUpdateListener.remove()
|
||||
|
@ -317,6 +363,7 @@ export default {
|
|||
this.$eventBus.$off('cast-local-item', this.castLocalItem)
|
||||
this.$eventBus.$off('user-settings', this.settingsUpdated)
|
||||
this.$eventBus.$off('playback-time-update', this.playbackTimeUpdate)
|
||||
this.$eventBus.$off('device-focus-update', this.deviceFocused)
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -37,6 +37,12 @@
|
|||
<div v-if="!hasCover" class="absolute left-0 right-0 w-full flex items-center justify-center" :style="{ padding: placeholderCoverPadding + 'rem', bottom: authorBottom + 'rem' }">
|
||||
<p class="text-center" style="color: rgb(247 223 187); opacity: 0.75" :style="{ fontSize: authorFontSize + 'rem' }">{{ authorCleaned }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="showPlayButton" class="absolute -bottom-16 -right-16 rotate-45 w-32 h-32 p-2 bg-gradient-to-r from-transparent to-black to-40% inline-flex justify-start items-center">
|
||||
<div class="hover:text-white text-gray-200 hover:scale-110 transform duration-200 pointer-events-auto -rotate-45" @click.stop.prevent="play">
|
||||
<span class="material-icons" :style="{ fontSize: playIconFontSize + 'rem' }">{{ streamIsPlaying ? 'pause_circle' : 'play_circle_filled' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Play/pause button for podcast episode -->
|
||||
|
@ -306,14 +312,10 @@ export default {
|
|||
return this.localLibraryItem.media.episodes.find((ep) => ep.serverEpisodeId === this.recentEpisode.id)
|
||||
},
|
||||
isStreaming() {
|
||||
if (this.isPodcast) {
|
||||
return this.$store.getters['getIsMediaStreaming'](this.libraryItemId, this.recentEpisode.id)
|
||||
} else {
|
||||
return false // not yet necessary for books
|
||||
}
|
||||
return this.store.getters['getIsMediaStreaming'](this.libraryItemId, this.recentEpisode?.id)
|
||||
},
|
||||
streamIsPlaying() {
|
||||
return this.$store.state.playerIsPlaying && this.isStreaming
|
||||
return this.store.state.playerIsPlaying && this.isStreaming
|
||||
},
|
||||
isMissing() {
|
||||
return this._libraryItem.isMissing
|
||||
|
@ -393,6 +395,10 @@ export default {
|
|||
rssFeed() {
|
||||
if (this.booksInSeries) return null
|
||||
return this._libraryItem.rssFeed || null
|
||||
},
|
||||
showPlayButton() {
|
||||
return false
|
||||
// return !this.isMissing && !this.isInvalid && !this.isStreaming && (this.numTracks || this.recentEpisode)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -434,6 +440,7 @@ export default {
|
|||
// Server books may have a local library item
|
||||
this.localLibraryItem = localLibraryItem
|
||||
},
|
||||
async play() {},
|
||||
async playEpisode() {
|
||||
await this.$hapticsImpact()
|
||||
const eventBus = this.$eventBus || this.$nuxt.$eventBus
|
||||
|
|
|
@ -20,7 +20,8 @@ export default {
|
|||
return {
|
||||
inittingLibraries: false,
|
||||
hasMounted: false,
|
||||
disconnectTime: 0
|
||||
disconnectTime: 0,
|
||||
timeLostFocus: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -299,9 +300,30 @@ export default {
|
|||
console.log(`[default] local media progress updated for ${newLocalMediaProgress.id}`)
|
||||
this.$store.commit('globals/updateLocalMediaProgress', newLocalMediaProgress)
|
||||
}
|
||||
},
|
||||
async visibilityChanged() {
|
||||
if (document.visibilityState === 'visible') {
|
||||
const elapsedTimeOutOfFocus = Date.now() - this.timeLostFocus
|
||||
console.log(`✅ [default] device visibility: has focus (${elapsedTimeOutOfFocus}ms out of focus)`)
|
||||
// If device out of focus for more than 30s then reload local media progress
|
||||
if (elapsedTimeOutOfFocus > 30000) {
|
||||
console.log(`✅ [default] device visibility: reloading local media progress`)
|
||||
// Reload local media progresses
|
||||
await this.$store.dispatch('globals/loadLocalMediaProgress')
|
||||
}
|
||||
if (document.visibilityState === 'visible') {
|
||||
this.$eventBus.$emit('device-focus-update', true)
|
||||
}
|
||||
} else {
|
||||
console.log('⛔️ [default] device visibility: does NOT have focus')
|
||||
this.timeLostFocus = Date.now()
|
||||
this.$eventBus.$emit('device-focus-update', false)
|
||||
}
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
document.addEventListener('visibilitychange', this.visibilityChanged)
|
||||
|
||||
this.$socket.on('user_updated', this.userUpdated)
|
||||
this.$socket.on('user_media_progress_updated', this.userMediaProgressUpdated)
|
||||
|
||||
|
@ -339,6 +361,7 @@ export default {
|
|||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('visibilitychange', this.visibilityChanged)
|
||||
this.$socket.off('user_updated', this.userUpdated)
|
||||
this.$socket.off('user_media_progress_updated', this.userMediaProgressUpdated)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue