diff --git a/android/app/src/main/java/com/audiobookshelf/app/media/MediaProgressSyncer.kt b/android/app/src/main/java/com/audiobookshelf/app/media/MediaProgressSyncer.kt index 634ef419..aaa7c09c 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/media/MediaProgressSyncer.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/media/MediaProgressSyncer.kt @@ -94,7 +94,10 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic } fun stop(shouldSync:Boolean? = true, cb: () -> Unit) { - if (!listeningTimerRunning) return + if (!listeningTimerRunning) { + reset() + return cb() + } listeningTimerTask?.cancel() listeningTimerTask = null @@ -255,6 +258,7 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic } } } else if (shouldSyncServer) { + Log.d(tag, "sync: currentSessionId=$currentSessionId") apiHandler.sendProgressSync(currentSessionId, syncData) { syncSuccess, errorMsg -> if (syncSuccess) { Log.d(tag, "Progress sync data sent to server $currentDisplayTitle for time $currentTime") 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 7039fcf3..a1ec4665 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 @@ -14,10 +14,9 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) : companion object { var lastPauseTime: Long = 0 //ms + var lazyIsPlaying: Boolean = false } - private var lazyIsPlaying: Boolean = false - override fun onPlayerError(error: PlaybackException) { val errorMessage = error.message ?: "Unknown Error" Log.e(tag, "onPlayerError $errorMessage") 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 c6e18e45..3ae9d630 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 @@ -9,6 +9,7 @@ import com.audiobookshelf.app.device.DeviceManager import com.audiobookshelf.app.media.MediaEventManager import com.audiobookshelf.app.player.CastManager import com.audiobookshelf.app.player.MediaProgressSyncer +import com.audiobookshelf.app.player.PlayerListener import com.audiobookshelf.app.player.PlayerNotificationService import com.audiobookshelf.app.server.ApiHandler import com.fasterxml.jackson.core.json.JsonReadFeature @@ -168,6 +169,8 @@ class AbsAudioPlayer : Plugin() { val episodeId = call.getString("episodeId", "").toString() val playWhenReady = call.getBoolean("playWhenReady") == true val playbackRate = call.getFloat("playbackRate",1f) ?: 1f + val startTimeOverride = call.getDouble("startTime") + Log.d(tag, "prepareLibraryItem lid=$libraryItemId, startTimeOverride=$startTimeOverride") if (libraryItemId.isEmpty()) { Log.e(tag, "Invalid call to play library item no library item id") @@ -189,10 +192,16 @@ class AbsAudioPlayer : Plugin() { Handler(Looper.getMainLooper()).post { Log.d(tag, "prepareLibraryItem: Preparing Local Media item ${jacksonMapper.writeValueAsString(it)}") val playbackSession = it.getPlaybackSession(episode) + if (startTimeOverride != null) { + Log.d(tag, "prepareLibraryItem: Using start time override $startTimeOverride") + playbackSession.currentTime = startTimeOverride + } 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") + PlayerListener.lazyIsPlaying = false + Handler(Looper.getMainLooper()).post { // TODO: This was needed again which is probably a design a flaw playerNotificationService.preparePlayer( playbackSession, @@ -202,6 +211,7 @@ class AbsAudioPlayer : Plugin() { } } } else { + playerNotificationService.mediaProgressSyncer.reset() playerNotificationService.preparePlayer(playbackSession, playWhenReady, playbackRate) } } @@ -209,28 +219,25 @@ class AbsAudioPlayer : Plugin() { } } else { // Play library item from server val playItemRequestPayload = playerNotificationService.getPlayItemRequestPayload(false) - - apiHandler.playLibraryItem(libraryItemId, episodeId, playItemRequestPayload) { - if (it == null) { - call.resolve(JSObject("{\"error\":\"Server play request failed\"}")) - } else { - - Handler(Looper.getMainLooper()).post { - 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) - } - } + Handler(Looper.getMainLooper()).post { + playerNotificationService.mediaProgressSyncer.stop { + apiHandler.playLibraryItem(libraryItemId, episodeId, playItemRequestPayload) { + if (it == null) { + call.resolve(JSObject("{\"error\":\"Server play request failed\"}")) } else { - playerNotificationService.preparePlayer(it, playWhenReady, playbackRate) + if (startTimeOverride != null) { + Log.d(tag, "prepareLibraryItem: Using start time override $startTimeOverride") + it.currentTime = startTimeOverride + } + + Handler(Looper.getMainLooper()).post { + Log.d(tag, "Preparing Player playback session ${jacksonMapper.writeValueAsString(it)}") + PlayerListener.lazyIsPlaying = false + playerNotificationService.preparePlayer(it, playWhenReady, playbackRate) + } + call.resolve(JSObject(jacksonMapper.writeValueAsString(it))) } } - - call.resolve(JSObject(jacksonMapper.writeValueAsString(it))) } } } diff --git a/components/app/AudioPlayer.vue b/components/app/AudioPlayer.vue index c948c240..b9adc80e 100644 --- a/components/app/AudioPlayer.vue +++ b/components/app/AudioPlayer.vue @@ -185,23 +185,34 @@ export default { }, computed: { menuItems() { - var items = [ - { - text: 'Chapter Track', - value: 'chapter_track', - icon: this.useChapterTrack ? 'check_box' : 'check_box_outline_blank' - }, - { - text: this.lockUi ? 'Unlock Player' : 'Lock Player', - value: 'lock', - icon: this.lockUi ? 'lock' : 'lock_open' - }, - { - text: 'Close Player', - value: 'close', - icon: 'close' - } - ] + const items = [] + // TODO: Implement on iOS + if (this.$platform !== 'ios' && !this.isPodcast && this.mediaId) { + items.push({ + text: 'History', + value: 'history' + }) + } + + items.push( + ...[ + { + text: 'Chapter Track', + value: 'chapter_track', + icon: this.useChapterTrack ? 'check_box' : 'check_box_outline_blank' + }, + { + text: this.lockUi ? 'Unlock Player' : 'Lock Player', + value: 'lock', + icon: this.lockUi ? 'lock' : 'lock_open' + }, + { + text: 'Close Player', + value: 'close', + icon: 'close' + } + ] + ) return items }, @@ -348,6 +359,16 @@ export default { }, networkConnected() { return this.$store.state.networkConnected + }, + mediaId() { + if (this.isPodcast || !this.playbackSession) return null + if (this.playbackSession.libraryItemId) { + return this.playbackSession.episodeId ? `${this.playbackSession.libraryItemId}-${this.playbackSession.episodeId}` : this.playbackSession.libraryItemId + } + const localLibraryItem = this.playbackSession.localLibraryItem + if (!localLibraryItem) return null + + return this.playbackSession.localEpisodeId ? `${localLibraryItem.id}-${this.playbackSession.localEpisodeId}` : localLibraryItem.id } }, methods: { @@ -692,7 +713,10 @@ export default { await this.$hapticsImpact() this.showMoreMenuDialog = false this.$nextTick(() => { - if (action === 'lock') { + if (action === 'history') { + this.$router.push(`/media/${this.mediaId}/history?title=${this.title}`) + this.showFullscreen = false + } else if (action === 'lock') { this.lockUi = !this.lockUi this.$localStore.setPlayerLock(this.lockUi) } else if (action === 'chapter_track') { diff --git a/pages/media/_id/history.vue b/pages/media/_id/history.vue index b013fbd4..929a5fec 100644 --- a/pages/media/_id/history.vue +++ b/pages/media/_id/history.vue @@ -198,6 +198,9 @@ export default { console.error('Invalid media item history', mediaItemHistory) return } + if (mediaItemHistory.id !== this.mediaItemHistory.id) { + return + } console.log('Media Item History updated') this.mediaItemHistory = mediaItemHistory