From f76556f90e665b47f272cad362341276ae2e0c0f Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:07:00 -0400 Subject: [PATCH] Fix network on main exception trying to play theme songs (#111) Fetch the theme song info on the IO thread This would throw an exception on some devices, but it would be caught without affecting the app, other than just not playing theme music. --- .../ui/detail/series/SeriesViewModel.kt | 4 +-- .../wholphin/ui/playback/PlaybackViewModel.kt | 29 ++++++++++--------- .../wholphin/util/ServerEventListener.kt | 4 +-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt index 35a9c8c3..28fb29b7 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt @@ -146,7 +146,7 @@ class SeriesViewModel seriesId: UUID, playThemeSongs: ThemeSongVolume, ) { - viewModelScope.launch(ExceptionHandler()) { + viewModelScope.launchIO { val themeSongs = api.libraryApi.getThemeSongs(seriesId).content themeSongs.items.firstOrNull()?.let { theme -> theme.mediaSources?.firstOrNull()?.let { source -> @@ -161,7 +161,7 @@ class SeriesViewModel Codec.Audio.FLAC, ), ) - Timber.Forest.v("Found theme song for series $seriesId") + Timber.v("Found theme song for series $seriesId") withContext(Dispatchers.Main) { themeSongPlayer.play(playThemeSongs, url) addCloseable { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt index 67925aae..db1a3e8b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt @@ -173,10 +173,11 @@ class PlaybackViewModel this.itemId = itemId val item = destination.item viewModelScope.launch( - LoadingExceptionHandler( - loading, - "Error preparing for playback for ${destination.itemId}", - ) + Dispatchers.IO, + Dispatchers.IO + + LoadingExceptionHandler( + loading, + "Error preparing for playback for ${destination.itemId}", + ), ) { val queriedItem = item?.data ?: api.userLibraryApi.getItem(itemId).content val base = @@ -493,7 +494,7 @@ class PlaybackViewModel subtitleIndex = subtitleIndex ?: TrackIndex.DISABLED, ) if (userInitiated) { - viewModelScope.launch(Dispatchers.IO + ExceptionHandler()) { + viewModelScope.launchIO { Timber.v("Saving user initiated item playback: %s", itemPlayback) val updated = itemPlaybackRepository.saveItemPlayback(itemPlayback) withContext(Dispatchers.Main) { @@ -560,7 +561,7 @@ class PlaybackViewModel } fun changeAudioStream(index: Int) { - viewModelScope.launch(ExceptionHandler()) { + viewModelScope.launchIO { changeStreams( dto, currentItemPlayback.value!!, @@ -573,7 +574,7 @@ class PlaybackViewModel } fun changeSubtitleStream(index: Int?): Job = - viewModelScope.launch(ExceptionHandler()) { + viewModelScope.launchIO { changeStreams( dto, currentItemPlayback.value!!, @@ -604,7 +605,7 @@ class PlaybackViewModel object : Player.Listener { override fun onPlaybackStateChanged(playbackState: Int) { if (playbackState == Player.STATE_ENDED) { - viewModelScope.launch(Dispatchers.IO + ExceptionHandler()) { + viewModelScope.launchIO { val nextItem = playlist.peek() Timber.v("Setting next up to ${nextItem?.id}") withContext(Dispatchers.Main) { @@ -625,7 +626,7 @@ class PlaybackViewModel private fun listenForSegments() { segmentJob?.cancel() segmentJob = - viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) { + viewModelScope.launchIO { val prefs = preferences.appPreferences.playbackPreferences val segments by api.mediaSegmentsApi.getItemSegments(itemId) if (segments.items.isNotEmpty()) { @@ -700,7 +701,7 @@ class PlaybackViewModel fun playUpNextUp() { playlist.value?.let { if (it.hasNext()) { - viewModelScope.launch(ExceptionHandler()) { + viewModelScope.launchIO { cancelUpNextEpisode() val item = it.getAndAdvance() val played = play(item.data, 0) @@ -715,7 +716,7 @@ class PlaybackViewModel fun playPrevious() { playlist.value?.let { if (it.hasPrevious()) { - viewModelScope.launch(ExceptionHandler()) { + viewModelScope.launchIO { cancelUpNextEpisode() val item = it.getPreviousAndReverse() val played = play(item.data, 0) @@ -727,13 +728,13 @@ class PlaybackViewModel } } - fun cancelUpNextEpisode() { - nextUp.value = null + suspend fun cancelUpNextEpisode() { + nextUp.setValueOnMain(null) } fun playItemInPlaylist(item: BaseItem) { playlist.value?.let { playlist -> - viewModelScope.launch(ExceptionHandler()) { + viewModelScope.launchIO { val toPlay = playlist.advanceTo(item.id) if (toPlay != null) { val played = play(toPlay.data, 0) diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/ServerEventListener.kt b/app/src/main/java/com/github/damontecres/wholphin/util/ServerEventListener.kt index 6c819a8d..0fef2112 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/ServerEventListener.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/ServerEventListener.kt @@ -3,12 +3,12 @@ package com.github.damontecres.wholphin.util import android.content.Context import android.widget.Toast import com.github.damontecres.wholphin.hilt.IoCoroutineScope +import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.showToast import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.extensions.sessionApi import org.jellyfin.sdk.api.sockets.subscribe @@ -32,7 +32,7 @@ class ServerEventListener } fun init() { - scope.launch(ExceptionHandler()) { + scope.launchIO { api.sessionApi.postCapabilities( playableMediaTypes = listOf(MediaType.VIDEO), supportedCommands =