diff --git a/android/app/src/main/java/com/audiobookshelf/app/MyNativeAudio.kt b/android/app/src/main/java/com/audiobookshelf/app/MyNativeAudio.kt index 8cab491c..b1104a4d 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/MyNativeAudio.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/MyNativeAudio.kt @@ -85,6 +85,26 @@ class MyNativeAudio : Plugin() { } } + @PluginMethod + fun getStreamSyncData(call: PluginCall) { + Handler(Looper.getMainLooper()).post() { + var isPlaying = playerNotificationService.getPlayStatus() + var lastPauseTime = playerNotificationService.getTheLastPauseTime() + Log.d(tag, "Get Last Pause Time $lastPauseTime") + var currentTime = playerNotificationService.getCurrentTime() + Log.d(tag, "Get Current Time $currentTime") + //if (!isPlaying) currentTime -= playerNotificationService.calcPauseSeekBackTime() + var id = playerNotificationService.getCurrentAudiobookId() + Log.d(tag, "Get Current id $id") + val ret = JSObject() + ret.put("lastPauseTime", lastPauseTime) + ret.put("currentTime", currentTime) + ret.put("isPlaying", isPlaying) + ret.put("id", id) + call.resolve(ret) + } + } + @PluginMethod fun pausePlayer(call: PluginCall) { Handler(Looper.getMainLooper()).post() { diff --git a/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt b/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt index 22fab7db..6c15b91b 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt @@ -81,6 +81,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { var mediaButtonClickTimeout: Long = 1000 //ms var seekAmount: Long = 20000 //ms + private var lastPauseTime: Long = 0 //ms + fun setCustomObjectListener(mylistener: MyCustomObjectListener) { listener = mylistener } @@ -380,10 +382,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { if (mPlayer.playbackState == Player.STATE_READY) { Log.d(tag, "STATE_READY : " + mPlayer.duration.toString()) - if (!currentAudiobook!!.hasPlayerLoaded && currentAudiobook!!.startTime > 0) { + /*if (!currentAudiobook!!.hasPlayerLoaded && currentAudiobook!!.startTime > 0) { Log.d(tag, "Should seek to ${currentAudiobook!!.startTime}") mPlayer.seekTo(currentAudiobook!!.startTime) - } + }*/ currentAudiobook!!.hasPlayerLoaded = true sendClientMetadata("ready") @@ -468,10 +470,13 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { } - mPlayer.setMediaSource(mediaSource, true) + //mPlayer.setMediaSource(mediaSource, true) + mPlayer.setMediaSource(mediaSource, currentAudiobook!!.startTime) mPlayer.prepare() mPlayer.playWhenReady = currentAudiobook!!.playWhenReady mPlayer.setPlaybackSpeed(audiobook.playbackSpeed) + + lastPauseTime = 0 } @@ -479,16 +484,48 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { return mPlayer.currentPosition } + fun getTheLastPauseTime() : Long { + return lastPauseTime + } + + fun calcPauseSeekBackTime() : Long { + if (lastPauseTime <= 0) return 0 + var time: Long = System.currentTimeMillis() - lastPauseTime + var seekback: Long = 0 + if (time < 3000) seekback = 0 + else if (time < 60000) seekback = time / 6 + else if (time < 300000) seekback = 15000 + else if (time < 1800000) seekback = 20000 + else if (time < 3600000) seekback = 25000 + else seekback = 29500 + return seekback + } + + fun getPlayStatus() : Boolean { + return mPlayer.isPlaying + } + + fun getCurrentAudiobookId() : String { + return currentAudiobook?.id.toString() + } + fun play() { if (mPlayer.isPlaying) { Log.d(tag, "Already playing") return } + if (lastPauseTime > 0) { + var backTime = calcPauseSeekBackTime() + if (backTime >= mPlayer.currentPosition) backTime = mPlayer.currentPosition - 500 + Log.d(tag, "SeekBackTime $backTime") + seekBackward(backTime) + } mPlayer.play() } fun pause() { mPlayer.pause() + lastPauseTime = System.currentTimeMillis() } fun seekPlayer(time: Long) { @@ -511,6 +548,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { if (mPlayer.playbackState == Player.STATE_READY) { mPlayer.clearMediaItems() } + lastPauseTime = 0 } fun sendClientMetadata(stateName: String) { diff --git a/components/AudioPlayerMini.vue b/components/AudioPlayerMini.vue index a58a1ab4..7661c3e6 100644 --- a/components/AudioPlayerMini.vue +++ b/components/AudioPlayerMini.vue @@ -200,17 +200,63 @@ export default { this.pause() } }, - set(audiobookStreamData) { + calcSeekBackTime(lastUpdate) { + var time = Date.now() - lastUpdate + var seekback = 0 + if (time < 3000) seekback = 0 + else if (time < 60000) seekback = time / 6 + else if (time < 300000) seekback = 15000 + else if (time < 1800000) seekback = 20000 + else if (time < 3600000) seekback = 25000 + else seekback = 29500 + return seekback + }, + async set(audiobookStreamData, stream, fromAppDestroy) { this.isResetting = false this.initObject = { ...audiobookStreamData } - this.currentPlaybackRate = this.initObject.playbackSpeed - MyNativeAudio.initPlayer(this.initObject).then((res) => { - if (res && res.success) { - console.log('Success init audio player') - } else { - console.error('Failed to init audio player') + + var init = true + if (!!stream) { + //console.log(JSON.stringify(stream)) + var data = await MyNativeAudio.getStreamSyncData() + console.log('getStreamSyncData', JSON.stringify(data)) + console.log('lastUpdate', stream.lastUpdate || 0) + //Same audiobook + if (data.id == stream.id && (data.isPlaying || data.lastPauseTime >= (stream.lastUpdate || 0))) { + console.log('Same audiobook') + this.isPaused = !data.isPlaying + this.currentTime = Number((data.currentTime / 1000).toFixed(2)) + this.timeupdate() + if (data.isPlaying) { + console.log('playing - continue') + if (fromAppDestroy) this.startPlayInterval() + } else console.log('paused and newer') + if (!fromAppDestroy) return + init = false + this.initObject.startTime = String(Math.floor(this.currentTime * 1000)) } - }) + //new audiobook stream or sync from other client + else if (stream.clientCurrentTime > 0) { + console.log('new audiobook stream or sync from other client') + if (!!stream.lastUpdate) { + var backTime = this.calcSeekBackTime(stream.lastUpdate) + var currentTime = Math.floor(stream.clientCurrentTime * 1000) + if (backTime >= currentTime) backTime = currentTime - 500 + console.log('SeekBackTime', backTime) + this.initObject.startTime = String(Math.floor(currentTime - backTime)) + } + } + } + + this.currentPlaybackRate = this.initObject.playbackSpeed + if (init) + MyNativeAudio.initPlayer(this.initObject).then((res) => { + if (res && res.success) { + console.log('Success init audio player') + } else { + console.error('Failed to init audio player') + } + }) if (audiobookStreamData.isLocal) { this.setStreamReady() diff --git a/components/app/StreamContainer.vue b/components/app/StreamContainer.vue index 9413e78a..1883ea68 100644 --- a/components/app/StreamContainer.vue +++ b/components/app/StreamContainer.vue @@ -269,7 +269,7 @@ export default { isLocal: true } - this.$refs.audioPlayerMini.set(audiobookStreamData) + this.$refs.audioPlayerMini.set(audiobookStreamData, null, false) }, streamOpen(stream) { if (this.download) { @@ -286,8 +286,6 @@ export default { return } - this.stream = stream - // Update local remove current this.$localStore.setCurrent(null) @@ -298,6 +296,7 @@ export default { if (playOnLoad) this.$store.commit('setPlayOnLoad', false) var audiobookStreamData = { + id: stream.id, title: this.title, author: this.author, playWhenReady: !!playOnLoad, @@ -309,7 +308,9 @@ export default { playlistUrl: this.$server.url + playlistUrl, token: this.$store.getters['user/getToken'] } - this.$refs.audioPlayerMini.set(audiobookStreamData) + this.$refs.audioPlayerMini.set(audiobookStreamData, stream, !this.stream) + + this.stream = stream }, audioPlayerMounted() { console.log('Audio Player Mounted', this.$server.stream)