Skip to content

Commit

Permalink
Fix:Remove local playback sessions started offline, refresh open play…
Browse files Browse the repository at this point in the history
…er timestamps when device gains focus
  • Loading branch information
advplyr committed Sep 10, 2023
1 parent f9621d2 commit 0ce3c8b
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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)
Expand Down
49 changes: 48 additions & 1 deletion components/app/AudioPlayerContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
},
Expand Down Expand Up @@ -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() {
Expand All @@ -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()
Expand All @@ -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>
19 changes: 13 additions & 6 deletions components/cards/LazyBookCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 -->
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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
Expand Down
25 changes: 24 additions & 1 deletion layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export default {
return {
inittingLibraries: false,
hasMounted: false,
disconnectTime: 0
disconnectTime: 0,
timeLostFocus: 0
}
},
watch: {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
Expand Down

0 comments on commit 0ce3c8b

Please sign in to comment.