diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/MediaProgressSyncer.kt b/android/app/src/main/java/com/audiobookshelf/app/player/MediaProgressSyncer.kt index 25bea081..2f774a1d 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/MediaProgressSyncer.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/MediaProgressSyncer.kt @@ -46,7 +46,10 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic } else { return } + } else if (playerNotificationService.getCurrentPlaybackSessionId() != currentSessionId) { + currentLocalMediaProgress = null } + listeningTimerRunning = true lastSyncTime = System.currentTimeMillis() currentPlaybackSession = playerNotificationService.getCurrentPlaybackSessionCopy() @@ -63,13 +66,30 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic } } - fun stop() { + fun stop(cb: () -> Unit) { if (!listeningTimerRunning) return Log.d(tag, "stop: Stopping listening for $currentDisplayTitle") val currentTime = playerNotificationService.getCurrentTimeSeconds() sync(currentTime) { reset() + cb() + } + } + + fun pause(cb: () -> Unit) { + if (!listeningTimerRunning) return + Log.d(tag, "pause: Pausing progress syncer for $currentDisplayTitle") + + val currentTime = playerNotificationService.getCurrentTimeSeconds() + sync(currentTime) { + listeningTimerTask?.cancel() + listeningTimerTask = null + listeningTimerRunning = false + lastSyncTime = 0L + failedSyncs = 0 + + cb() } } @@ -77,7 +97,6 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic currentPlaybackSession?.let { it.updatedAt = mediaProgress.lastUpdate it.currentTime = mediaProgress.currentTime - DeviceManager.dbManager.saveLocalPlaybackSession(it) saveLocalProgress(it) } diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerListener.kt b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerListener.kt index cc9f1e3c..302abf33 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerListener.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerListener.kt @@ -71,32 +71,36 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) : if (player.isPlaying) { Log.d(tag, "SeekBackTime: Player is playing") if (lastPauseTime > 0 && DeviceManager.deviceData.deviceSettings?.disableAutoRewind != true) { + var seekBackTime = 0L if (onSeekBack) onSeekBack = false else { Log.d(tag, "SeekBackTime: playing started now set seek back time $lastPauseTime") - var backTime = calcPauseSeekBackTime() - if (backTime > 0) { + seekBackTime = calcPauseSeekBackTime() + if (seekBackTime > 0) { // Current chapter is used so that seek back does not go back to the previous chapter val currentChapter = playerNotificationService.getCurrentBookChapter() val minSeekBackTime = currentChapter?.startMs ?: 0 val currentTime = playerNotificationService.getCurrentTime() - val newTime = currentTime - backTime + val newTime = currentTime - seekBackTime if (newTime < minSeekBackTime) { - backTime = currentTime - minSeekBackTime + seekBackTime = currentTime - minSeekBackTime } - Log.d(tag, "SeekBackTime $backTime") + Log.d(tag, "SeekBackTime $seekBackTime") onSeekBack = true - playerNotificationService.seekBackward(backTime) } } // Check if playback session still exists or sync media progress if updated val pauseLength: Long = System.currentTimeMillis() - lastPauseTime if (pauseLength > PAUSE_LEN_BEFORE_RECHECK) { - val shouldCarryOn = playerNotificationService.checkCurrentSessionProgress() + val shouldCarryOn = playerNotificationService.checkCurrentSessionProgress(seekBackTime) if (!shouldCarryOn) return } + + if (seekBackTime > 0L) { + playerNotificationService.seekBackward(seekBackTime) + } } } else { Log.d(tag, "SeekBackTime: Player not playing set last pause time") @@ -104,12 +108,13 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) : } // Start/stop progress sync interval - Log.d(tag, "Playing ${playerNotificationService.getCurrentBookTitle()}") if (player.isPlaying) { player.volume = 1F // Volume on sleep timer might have decreased this playerNotificationService.mediaProgressSyncer.start() } else { - playerNotificationService.mediaProgressSyncer.stop() + playerNotificationService.mediaProgressSyncer.pause { + Log.d(tag, "Media Progress Syncer paused and synced") + } } playerNotificationService.clientEventEmitter?.onPlayingUpdate(player.isPlaying) diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt index 8b0c09cf..df082706 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt @@ -550,10 +550,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { // Called from PlayerListener play event // check with server if progress has updated since last play and sync progress update - fun checkCurrentSessionProgress():Boolean { + fun checkCurrentSessionProgress(seekBackTime:Long):Boolean { if (currentPlaybackSession == null) return true - currentPlaybackSession?.let { playbackSession -> + mediaProgressSyncer.currentPlaybackSession?.let { playbackSession -> if (!apiHandler.isOnline() || playbackSession.isLocalLibraryItemOnly) { return true // carry on } @@ -575,16 +575,28 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { Log.d(tag, "checkCurrentSessionProgress: Media progress was updated since last play time updating from ${playbackSession.currentTime} to ${mediaProgress.currentTime}") mediaProgressSyncer.syncFromServerProgress(mediaProgress) + // Update current playback session stored in PNS since MediaProgressSyncer version is a copy + mediaProgressSyncer.currentPlaybackSession?.let { updatedPlaybackSession -> + currentPlaybackSession = updatedPlaybackSession + } + Handler(Looper.getMainLooper()).post { seekPlayer(playbackSession.currentTimeMs) + // Should already be playing + currentPlayer.volume = 1F // Volume on sleep timer might have decreased this + mediaProgressSyncer.start() + clientEventEmitter?.onPlayingUpdate(true) + } + } else { + Handler(Looper.getMainLooper()).post { + if (seekBackTime > 0L) { + seekBackward(seekBackTime) + } + // Should already be playing + currentPlayer.volume = 1F // Volume on sleep timer might have decreased this + mediaProgressSyncer.start() + clientEventEmitter?.onPlayingUpdate(true) } - } - - Handler(Looper.getMainLooper()).post { - // Should already be playing - currentPlayer.volume = 1F // Volume on sleep timer might have decreased this - mediaProgressSyncer.start() - clientEventEmitter?.onPlayingUpdate(true) } } } else { @@ -607,6 +619,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { } else { Log.d(tag, "checkCurrentSessionProgress: Playback session still available on server") Handler(Looper.getMainLooper()).post { + if (seekBackTime > 0L) { + seekBackward(seekBackTime) + } + currentPlayer.volume = 1F // Volume on sleep timer might have decreased this mediaProgressSyncer.start() clientEventEmitter?.onPlayingUpdate(true) @@ -673,7 +689,9 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { Log.d(tag, "closePlayback") if (mediaProgressSyncer.listeningTimerRunning) { Log.i(tag, "About to close playback so stopping media progress syncer first") - mediaProgressSyncer.stop() + mediaProgressSyncer.stop { + Log.d(tag, "Media Progress syncer stopped and synced") + } } try { diff --git a/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsAudioPlayer.kt b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsAudioPlayer.kt index b0d82226..2a36a584 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsAudioPlayer.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsAudioPlayer.kt @@ -177,7 +177,21 @@ class AbsAudioPlayer : Plugin() { Handler(Looper.getMainLooper()).post { Log.d(tag, "prepareLibraryItem: Preparing Local Media item ${jacksonMapper.writeValueAsString(it)}") val playbackSession = it.getPlaybackSession(episode) - playerNotificationService.preparePlayer(playbackSession, playWhenReady, playbackRate) + + if (playerNotificationService.mediaProgressSyncer.listeningTimerRunning) { // If progress syncing then first stop before preparing next + playerNotificationService.mediaProgressSyncer.stop { + Log.d(tag, "Media progress syncer was already syncing - stopped") + Handler(Looper.getMainLooper()).post { // TODO: This was needed again which is probably a design a flaw + playerNotificationService.preparePlayer( + playbackSession, + playWhenReady, + playbackRate + ) + } + } + } else { + playerNotificationService.preparePlayer(playbackSession, playWhenReady, playbackRate) + } } return call.resolve(JSObject()) } @@ -188,9 +202,20 @@ class AbsAudioPlayer : Plugin() { if (it == null) { call.resolve(JSObject("{\"error\":\"Server play request failed\"}")) } else { + Handler(Looper.getMainLooper()).post { - Log.d(tag, "Preparing Player TEST ${jacksonMapper.writeValueAsString(it)}") - playerNotificationService.preparePlayer(it, playWhenReady, playbackRate) + Log.d(tag, "Preparing Player playback session ${jacksonMapper.writeValueAsString(it)}") + + if (playerNotificationService.mediaProgressSyncer.listeningTimerRunning) { // If progress syncing then first stop before preparing next + playerNotificationService.mediaProgressSyncer.stop { + Log.d(tag, "Media progress syncer was already syncing - stopped") + Handler(Looper.getMainLooper()).post { // TODO: This was needed again which is probably a design a flaw + playerNotificationService.preparePlayer(it, playWhenReady, playbackRate) + } + } + } else { + playerNotificationService.preparePlayer(it, playWhenReady, playbackRate) + } } call.resolve(JSObject(jacksonMapper.writeValueAsString(it)))