diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt b/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt index a6fd088b..9b5c35eb 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt @@ -48,6 +48,8 @@ class PlaybackSession( @get:JsonIgnore val isHLS get() = playMethod == PLAYMETHOD_TRANSCODE @get:JsonIgnore + val isDirectPlay get() = playMethod == PLAYMETHOD_DIRECTPLAY + @get:JsonIgnore val isLocal get() = playMethod == PLAYMETHOD_LOCAL @get:JsonIgnore val currentTimeMs get() = (currentTime * 1000L).toLong() 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 9251ba78..e5c67a17 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 @@ -15,8 +15,9 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) : private var onSeekBack: Boolean = false override fun onPlayerError(error: PlaybackException) { - error.message?.let { Log.e(tag, it) } - error.localizedMessage?.let { Log.e(tag, it) } + var errorMessage = error.message ?: "Unknown Error" + Log.e(tag, "onPlayerError $errorMessage") + playerNotificationService.handlePlayerPlaybackError(errorMessage) // If was direct playing session, fallback to transcode } override fun onEvents(player: Player, events: Player.Events) { 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 e3fc7e5b..5f461459 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 @@ -55,6 +55,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { fun onSleepTimerEnded(currentPosition: Long) fun onSleepTimerSet(sleepTimeRemaining: Int) fun onLocalMediaProgressUpdate(localMediaProgress: LocalMediaProgress) + fun onPlaybackFailed(errorMessage:String) } private val tag = "PlayerService" @@ -289,7 +290,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { mediaSession.setMetadata(metadata) var mediaItems = playbackSession.getMediaItems() if (mPlayer == currentPlayer) { - var mediaSource:MediaSource if (playbackSession.isLocal) { @@ -333,6 +333,26 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { currentPlayer.prepare() } + fun handlePlayerPlaybackError(errorMessage:String) { + // On error and was attempting to direct play - fallback to transcode + currentPlaybackSession?.let { playbackSession -> + if (playbackSession.isDirectPlay) { + Log.d(tag, "Fallback to transcode") + + var libraryItemId = playbackSession.libraryItemId ?: "" // Must be true since direct play + var episodeId = playbackSession.episodeId + apiHandler.playLibraryItem(libraryItemId, episodeId, true) { + Handler(Looper.getMainLooper()).post() { + preparePlayer(it, true) + } + } + } else { + clientEventEmitter?.onPlaybackFailed(errorMessage) + closePlayback() + } + } + } + fun switchToPlayer(useCastPlayer: Boolean) { currentPlayer = if (useCastPlayer) { Log.d(tag, "switchToPlayer: Using Cast Player " + castPlayer?.deviceInfo) 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 d7fc8bfd..28cdd95d 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 @@ -68,6 +68,10 @@ class AbsAudioPlayer : Plugin() { override fun onLocalMediaProgressUpdate(localMediaProgress: LocalMediaProgress) { notifyListeners("onLocalMediaProgressUpdate", JSObject(jacksonMapper.writeValueAsString(localMediaProgress))) } + + override fun onPlaybackFailed(errorMessage: String) { + emit("onPlaybackFailed", errorMessage) + } }) } mainActivity.pluginCallback = foregroundServiceReady diff --git a/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt b/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt index bdcd2c0f..3f2606c4 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt @@ -151,12 +151,11 @@ class ApiHandler { } } - fun playLibraryItem(libraryItemId:String, episodeId:String, forceTranscode:Boolean, cb: (PlaybackSession) -> Unit) { + fun playLibraryItem(libraryItemId:String, episodeId:String?, forceTranscode:Boolean, cb: (PlaybackSession) -> Unit) { var payload = JSObject() payload.put("mediaPlayer", "exo-player") // Only if direct play fails do we force transcode - // TODO: Fallback to transcode if (!forceTranscode) payload.put("forceDirectPlay", true) else payload.put("forceTranscode", true) diff --git a/components/app/AudioPlayer.vue b/components/app/AudioPlayer.vue index eea3cdea..6a776b52 100644 --- a/components/app/AudioPlayer.vue +++ b/components/app/AudioPlayer.vue @@ -535,6 +535,12 @@ export default { this.$refs.dropdownMenu.closeMenu() } }, + closePlayback() { + this.$store.commit('setPlayerItem', null) + this.showFullscreen = false + this.isEnded = false + this.playbackSession = null + }, // // Listeners from audio AbsAudioPlayer // @@ -586,16 +592,20 @@ export default { }, onPlaybackClosed() { console.log('Received onPlaybackClosed evt') - this.$store.commit('setPlayerItem', null) - this.showFullscreen = false - this.isEnded = false - this.playbackSession = null + this.closePlayback() + }, + onPlaybackFailed(data) { + console.log('Received onPlaybackFailed evt') + var errorMessage = data.value || 'Unknown Error' + this.$toast.error(`Playback Failed: ${errorMessage}`) + this.closePlayback() }, async init() { this.useChapterTrack = await this.$localStore.getUseChapterTrack() this.onPlaybackSessionListener = AbsAudioPlayer.addListener('onPlaybackSession', this.onPlaybackSession) this.onPlaybackClosedListener = AbsAudioPlayer.addListener('onPlaybackClosed', this.onPlaybackClosed) + this.onPlaybackFailedListener = AbsAudioPlayer.addListener('onPlaybackFailed', this.onPlaybackFailed) this.onPlayingUpdateListener = AbsAudioPlayer.addListener('onPlayingUpdate', this.onPlayingUpdate) this.onMetadataListener = AbsAudioPlayer.addListener('onMetadata', this.onMetadata) } @@ -615,6 +625,7 @@ export default { if (this.onMetadataListener) this.onMetadataListener.remove() if (this.onPlaybackSessionListener) this.onPlaybackSessionListener.remove() if (this.onPlaybackClosedListener) this.onPlaybackClosedListener.remove() + if (this.onPlaybackFailedListener) this.onPlaybackFailedListener.remove() clearInterval(this.playInterval) } } diff --git a/components/covers/BookCover.vue b/components/covers/BookCover.vue index b6faba6c..7377bd24 100644 --- a/components/covers/BookCover.vue +++ b/components/covers/BookCover.vue @@ -23,11 +23,11 @@
{{ titleCleaned }}
+{{ titleCleaned }}
{{ authorCleaned }}
+{{ authorCleaned }}