From a69054fefaf8b142effde01967f5cf7cde118ae1 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 19 Jun 2022 11:57:19 -0500 Subject: [PATCH] Fix crash when server requests fail --- .../audiobookshelf/app/media/MediaManager.kt | 8 +++- .../app/player/MediaSessionCallback.kt | 27 ++++++++---- .../player/MediaSessionPlaybackPreparer.kt | 26 ++++++++---- .../app/player/PlayerNotificationService.kt | 17 ++++++-- .../app/plugins/AbsAudioPlayer.kt | 17 ++++---- .../app/plugins/AbsDownloader.kt | 40 ++++++++++-------- .../audiobookshelf/app/server/ApiHandler.kt | 41 +++++++++++++------ components/app/AudioPlayerContainer.vue | 24 ++++++++--- 8 files changed, 134 insertions(+), 66 deletions(-) diff --git a/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt b/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt index 36e53b61..63e4d7ab 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt @@ -270,14 +270,18 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) { } } - fun play(libraryItemWrapper:LibraryItemWrapper, episode:PodcastEpisode?, playItemRequestPayload:PlayItemRequestPayload, cb: (PlaybackSession) -> Unit) { + fun play(libraryItemWrapper:LibraryItemWrapper, episode:PodcastEpisode?, playItemRequestPayload:PlayItemRequestPayload, cb: (PlaybackSession?) -> Unit) { if (libraryItemWrapper is LocalLibraryItem) { val localLibraryItem = libraryItemWrapper as LocalLibraryItem cb(localLibraryItem.getPlaybackSession(episode)) } else { val libraryItem = libraryItemWrapper as LibraryItem apiHandler.playLibraryItem(libraryItem.id,episode?.id ?: "",playItemRequestPayload) { - cb(it) + if (it == null) { + cb(null) + } else { + cb(it) + } } } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt b/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt index 03628b4a..7593e0f9 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt @@ -25,9 +25,12 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi Log.d(tag, "ON PREPARE MEDIA SESSION COMPAT") playerNotificationService.mediaManager.getFirstItem()?.let { li -> playerNotificationService.mediaManager.play(li, null, playerNotificationService.getPlayItemRequestPayload(false)) { - Log.d(tag, "About to prepare player with ${it.displayTitle}") - Handler(Looper.getMainLooper()).post() { - playerNotificationService.preparePlayer(it,true,null) + if (it == null) { + Log.e(tag, "Failed to play library item") + } else { + Handler(Looper.getMainLooper()).post() { + playerNotificationService.preparePlayer(it,true,null) + } } } } @@ -47,9 +50,12 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi Log.d(tag, "ON PLAY FROM SEARCH $query") playerNotificationService.mediaManager.getFromSearch(query)?.let { li -> playerNotificationService.mediaManager.play(li, null, playerNotificationService.getPlayItemRequestPayload(false)) { - Log.d(tag, "About to prepare player with ${it.displayTitle}") - Handler(Looper.getMainLooper()).post() { - playerNotificationService.preparePlayer(it,true,null) + if (it == null) { + Log.e(tag, "Failed to play library item") + } else { + Handler(Looper.getMainLooper()).post() { + playerNotificationService.preparePlayer(it, true, null) + } } } } @@ -105,9 +111,12 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi libraryItemWrapper?.let { li -> playerNotificationService.mediaManager.play(li, podcastEpisode, playerNotificationService.getPlayItemRequestPayload(false)) { - Log.d(tag, "About to prepare player with ${it.displayTitle}") - Handler(Looper.getMainLooper()).post() { - playerNotificationService.preparePlayer(it,true,null) + if (it == null) { + Log.e(tag, "Failed to play library item") + } else { + Handler(Looper.getMainLooper()).post() { + playerNotificationService.preparePlayer(it, true, null) + } } } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionPlaybackPreparer.kt b/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionPlaybackPreparer.kt index 8c7a6984..33381e07 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionPlaybackPreparer.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionPlaybackPreparer.kt @@ -31,8 +31,12 @@ class MediaSessionPlaybackPreparer(var playerNotificationService:PlayerNotificat Log.d(tag, "ON PREPARE $playWhenReady") playerNotificationService.mediaManager.getFirstItem()?.let { li -> playerNotificationService.mediaManager.play(li, null, playerNotificationService.getPlayItemRequestPayload(false)) { - Handler(Looper.getMainLooper()).post() { - playerNotificationService.preparePlayer(it,playWhenReady,null) + if (it == null) { + Log.e(tag, "Failed to play library item") + } else { + Handler(Looper.getMainLooper()).post() { + playerNotificationService.preparePlayer(it, playWhenReady, null) + } } } } @@ -54,9 +58,12 @@ class MediaSessionPlaybackPreparer(var playerNotificationService:PlayerNotificat libraryItemWrapper?.let { li -> playerNotificationService.mediaManager.play(li, podcastEpisode, playerNotificationService.getPlayItemRequestPayload(false)) { - Log.d(tag, "About to prepare player with ${it.displayTitle}") - Handler(Looper.getMainLooper()).post() { - playerNotificationService.preparePlayer(it,playWhenReady,null) + if (it == null) { + Log.e(tag, "Failed to play library item") + } else { + Handler(Looper.getMainLooper()).post() { + playerNotificationService.preparePlayer(it, playWhenReady, null) + } } } } @@ -66,9 +73,12 @@ class MediaSessionPlaybackPreparer(var playerNotificationService:PlayerNotificat Log.d(tag, "ON PREPARE FROM SEARCH $query") playerNotificationService.mediaManager.getFromSearch(query)?.let { li -> playerNotificationService.mediaManager.play(li, null, playerNotificationService.getPlayItemRequestPayload(false)) { - Log.d(tag, "About to prepare player with ${it.displayTitle}") - Handler(Looper.getMainLooper()).post() { - playerNotificationService.preparePlayer(it,playWhenReady,null) + if (it == null) { + Log.e(tag, "Failed to play library item") + } else { + Handler(Looper.getMainLooper()).post() { + playerNotificationService.preparePlayer(it, playWhenReady, null) + } } } } 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 ce80c1bd..bece9d1f 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 @@ -381,8 +381,13 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { val libraryItemId = playbackSession.libraryItemId ?: "" // Must be true since direct play val episodeId = playbackSession.episodeId apiHandler.playLibraryItem(libraryItemId, episodeId, playItemRequestPayload) { - Handler(Looper.getMainLooper()).post { - preparePlayer(it, true, null) + if (it == null) { // Play request failed + clientEventEmitter?.onPlaybackFailed(errorMessage) + closePlayback() + } else { + Handler(Looper.getMainLooper()).post { + preparePlayer(it, true, null) + } } } } else { @@ -400,8 +405,12 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { val libraryItemId = playbackSession.libraryItemId ?: "" // Must be true since direct play val episodeId = playbackSession.episodeId apiHandler.playLibraryItem(libraryItemId, episodeId, playItemRequestPayload) { - Handler(Looper.getMainLooper()).post { - preparePlayer(it, true, null) + if (it == null) { + Log.e(tag, "Failed to start new playback session") + } else { + Handler(Looper.getMainLooper()).post { + preparePlayer(it, true, null) + } } } } 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 44c1491c..eacf09bf 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 @@ -165,7 +165,7 @@ class AbsAudioPlayer : Plugin() { if (libraryItemId.isEmpty()) { Log.e(tag, "Invalid call to play library item no library item id") - return call.resolve() + return call.resolve(JSObject("{\"error\":\"Invalid request\"}")) } if (libraryItemId.startsWith("local")) { // Play local media item @@ -176,7 +176,7 @@ class AbsAudioPlayer : Plugin() { episode = podcastMedia.episodes?.find { ep -> ep.id == episodeId } if (episode == null) { Log.e(tag, "prepareLibraryItem: Podcast episode not found $episodeId") - return call.resolve(JSObject()) + return call.resolve(JSObject("{\"error\":\"Podcast episode not found\"}")) } } @@ -191,13 +191,16 @@ class AbsAudioPlayer : Plugin() { 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 TEST ${jacksonMapper.writeValueAsString(it)}") + playerNotificationService.preparePlayer(it, playWhenReady, playbackRate) + } - Handler(Looper.getMainLooper()).post { - Log.d(tag, "Preparing Player TEST ${jacksonMapper.writeValueAsString(it)}") - playerNotificationService.preparePlayer(it, playWhenReady, playbackRate) + call.resolve(JSObject(jacksonMapper.writeValueAsString(it))) } - - call.resolve(JSObject(jacksonMapper.writeValueAsString(it))) } } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDownloader.kt b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDownloader.kt index 0b05ade7..3e6c0e70 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDownloader.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDownloader.kt @@ -143,31 +143,35 @@ class AbsDownloader : Plugin() { } apiHandler.getLibraryItemWithProgress(libraryItemId, episodeId) { libraryItem -> - Log.d(tag, "Got library item from server ${libraryItem.id}") + if (libraryItem == null) { + call.resolve(JSObject("{\"error\":\"Server request failed\"}")) + } else { + Log.d(tag, "Got library item from server ${libraryItem.id}") - val localFolder = DeviceManager.dbManager.getLocalFolder(localFolderId) - if (localFolder != null) { + val localFolder = DeviceManager.dbManager.getLocalFolder(localFolderId) + if (localFolder != null) { - if (episodeId.isNotEmpty() && libraryItem.mediaType != "podcast") { - Log.e(tag, "Library item is not a podcast but episode was requested") - call.resolve(JSObject("{\"error\":\"Invalid library item not a podcast\"}")) - } else if (episodeId.isNotEmpty()) { - val podcast = libraryItem.media as Podcast - val episode = podcast.episodes?.find { podcastEpisode -> - podcastEpisode.id == episodeId - } - if (episode == null) { - call.resolve(JSObject("{\"error\":\"Invalid podcast episode not found\"}")) + if (episodeId.isNotEmpty() && libraryItem.mediaType != "podcast") { + Log.e(tag, "Library item is not a podcast but episode was requested") + call.resolve(JSObject("{\"error\":\"Invalid library item not a podcast\"}")) + } else if (episodeId.isNotEmpty()) { + val podcast = libraryItem.media as Podcast + val episode = podcast.episodes?.find { podcastEpisode -> + podcastEpisode.id == episodeId + } + if (episode == null) { + call.resolve(JSObject("{\"error\":\"Invalid podcast episode not found\"}")) + } else { + startLibraryItemDownload(libraryItem, localFolder, episode) + call.resolve() + } } else { - startLibraryItemDownload(libraryItem, localFolder, episode) + startLibraryItemDownload(libraryItem, localFolder, null) call.resolve() } } else { - startLibraryItemDownload(libraryItem, localFolder, null) - call.resolve() + call.resolve(JSObject("{\"error\":\"Local Folder Not Found\"}")) } - } else { - call.resolve(JSObject("{\"error\":\"Local Folder Not Found\"}")) } } } 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 874507a1..87c98b8c 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 @@ -135,19 +135,29 @@ class ApiHandler(var ctx:Context) { } } - fun getLibraryItem(libraryItemId:String, cb: (LibraryItem) -> Unit) { + fun getLibraryItem(libraryItemId:String, cb: (LibraryItem?) -> Unit) { getRequest("/api/items/$libraryItemId?expanded=1", null, null) { - val libraryItem = jacksonMapper.readValue(it.toString()) - cb(libraryItem) + if (it.has("error")) { + Log.e(tag, it.getString("error") ?: "getLibraryItem Failed") + cb(null) + } else { + val libraryItem = jacksonMapper.readValue(it.toString()) + cb(libraryItem) + } } } - fun getLibraryItemWithProgress(libraryItemId:String, episodeId:String?, cb: (LibraryItem) -> Unit) { + fun getLibraryItemWithProgress(libraryItemId:String, episodeId:String?, cb: (LibraryItem?) -> Unit) { var requestUrl = "/api/items/$libraryItemId?expanded=1&include=progress" if (!episodeId.isNullOrEmpty()) requestUrl += "&episode=$episodeId" getRequest(requestUrl, null, null) { - val libraryItem = jacksonMapper.readValue(it.toString()) - cb(libraryItem) + if (it.has("error")) { + Log.e(tag, it.getString("error") ?: "getLibraryItemWithProgress Failed") + cb(null) + } else { + val libraryItem = jacksonMapper.readValue(it.toString()) + cb(libraryItem) + } } } @@ -186,15 +196,20 @@ class ApiHandler(var ctx:Context) { } } - fun playLibraryItem(libraryItemId:String, episodeId:String?, playItemRequestPayload:PlayItemRequestPayload, cb: (PlaybackSession) -> Unit) { + fun playLibraryItem(libraryItemId:String, episodeId:String?, playItemRequestPayload:PlayItemRequestPayload, cb: (PlaybackSession?) -> Unit) { val payload = JSObject(jacksonMapper.writeValueAsString(playItemRequestPayload)) val endpoint = if (episodeId.isNullOrEmpty()) "/api/items/$libraryItemId/play" else "/api/items/$libraryItemId/play/$episodeId" postRequest(endpoint, payload) { - it.put("serverConnectionConfigId", DeviceManager.serverConnectionConfig?.id) - it.put("serverAddress", DeviceManager.serverAddress) - val playbackSession = jacksonMapper.readValue(it.toString()) - cb(playbackSession) + if (it.has("error")) { + Log.e(tag, it.getString("error") ?: "Play Library Item Failed") + cb(null) + } else { + it.put("serverConnectionConfigId", DeviceManager.serverConnectionConfig?.id) + it.put("serverAddress", DeviceManager.serverAddress) + val playbackSession = jacksonMapper.readValue(it.toString()) + cb(playbackSession) + } } } @@ -236,10 +251,12 @@ class ApiHandler(var ctx:Context) { Log.d(tag, "Sending sync local progress request with ${localMediaProgress.size} progress items") val payload = JSObject(jacksonMapper.writeValueAsString(LocalMediaProgressSyncPayload(localMediaProgress))) postRequest("/api/me/sync-local-progress", payload) { - Log.d(tag, "Media Progress Sync payload $payload - response ${it.toString()}") + Log.d(tag, "Media Progress Sync payload $payload - response ${it}") if (it.toString() == "{}") { Log.e(tag, "Progress sync received empty object") + } else if (it.has("error")) { + Log.e(tag, it.getString("error") ?: "Progress sync error") } else { val progressSyncResponsePayload = jacksonMapper.readValue(it.toString()) diff --git a/components/app/AudioPlayerContainer.vue b/components/app/AudioPlayerContainer.vue index 6a25eec7..df90791c 100644 --- a/components/app/AudioPlayerContainer.vue +++ b/components/app/AudioPlayerContainer.vue @@ -185,11 +185,17 @@ export default { } AbsAudioPlayer.prepareLibraryItem({ libraryItemId, episodeId: null, playWhenReady: false, playbackRate }) .then((data) => { - console.log('Library item play response', JSON.stringify(data)) - AbsAudioPlayer.requestSession() + if (data.error) { + const errorMsg = data.error || 'Failed to play' + this.$toast.error(errorMsg) + } else { + console.log('Library item play response', JSON.stringify(data)) + AbsAudioPlayer.requestSession() + } }) .catch((error) => { console.error('Failed', error) + this.$toast.error('Failed to play') }) }, async playLibraryItem(payload) { @@ -220,15 +226,21 @@ export default { console.log('Called playLibraryItem', libraryItemId) AbsAudioPlayer.prepareLibraryItem({ libraryItemId, episodeId, playWhenReady: true, playbackRate }) .then((data) => { - console.log('Library item play response', JSON.stringify(data)) - if (!libraryItemId.startsWith('local')) { - this.serverLibraryItemId = libraryItemId + if (data.error) { + const errorMsg = data.error || 'Failed to play' + this.$toast.error(errorMsg) } else { - this.serverLibraryItemId = serverLibraryItemId + console.log('Library item play response', JSON.stringify(data)) + if (!libraryItemId.startsWith('local')) { + this.serverLibraryItemId = libraryItemId + } else { + this.serverLibraryItemId = serverLibraryItemId + } } }) .catch((error) => { console.error('Failed', error) + this.$toast.error('Failed to play') }) }, pauseItem() {