From a0ea262a105ff7e0f9a39caa11c36c3e5a228217 Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Sun, 25 Dec 2022 12:27:17 +0100 Subject: [PATCH] Improve media source id handling --- .../jellyfin/mobile/bridge/ExternalPlayer.kt | 6 +++++- .../mobile/player/interaction/PlayOptions.kt | 4 ++-- .../mobile/player/queue/QueueManager.kt | 17 ++++++++++++++--- .../mobile/player/source/MediaSourceResolver.kt | 3 ++- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/jellyfin/mobile/bridge/ExternalPlayer.kt b/app/src/main/java/org/jellyfin/mobile/bridge/ExternalPlayer.kt index c76cf3788..1baf1849e 100644 --- a/app/src/main/java/org/jellyfin/mobile/bridge/ExternalPlayer.kt +++ b/app/src/main/java/org/jellyfin/mobile/bridge/ExternalPlayer.kt @@ -32,6 +32,7 @@ import org.jellyfin.sdk.api.operations.VideosApi import org.jellyfin.sdk.model.api.DeviceProfile import org.jellyfin.sdk.model.api.MediaStream import org.jellyfin.sdk.model.api.PlayMethod +import org.jellyfin.sdk.model.serializer.toUUIDOrNull import org.json.JSONArray import org.json.JSONObject import org.koin.core.component.KoinComponent @@ -85,7 +86,9 @@ class ExternalPlayer( @JavascriptInterface fun initPlayer(args: String) { val playOptions = PlayOptions.fromJson(JSONObject(args)) - val itemId = playOptions?.run { mediaSourceId ?: ids.firstOrNull() } + val itemId = playOptions?.run { + ids.firstOrNull() ?: mediaSourceId?.toUUIDOrNull() // fallback if ids is empty + } if (playOptions == null || itemId == null) { context.toast(R.string.player_error_invalid_play_options) return @@ -94,6 +97,7 @@ class ExternalPlayer( coroutinesScope.launch { mediaSourceResolver.resolveMediaSource( itemId = itemId, + mediaSourceId = playOptions.mediaSourceId, deviceProfile = externalPlayerProfile, startTimeTicks = playOptions.startPositionTicks, audioStreamIndex = playOptions.audioStreamIndex, diff --git a/app/src/main/java/org/jellyfin/mobile/player/interaction/PlayOptions.kt b/app/src/main/java/org/jellyfin/mobile/player/interaction/PlayOptions.kt index 413cc4f65..4fb8bfef5 100644 --- a/app/src/main/java/org/jellyfin/mobile/player/interaction/PlayOptions.kt +++ b/app/src/main/java/org/jellyfin/mobile/player/interaction/PlayOptions.kt @@ -11,8 +11,8 @@ import java.util.UUID @Parcelize data class PlayOptions( - val mediaSourceId: UUID?, val ids: List, + val mediaSourceId: String?, val startIndex: Int, val startPositionTicks: Long?, val audioStreamIndex: Int?, @@ -22,7 +22,6 @@ data class PlayOptions( companion object { fun fromJson(json: JSONObject): PlayOptions? = try { PlayOptions( - mediaSourceId = json.optString("mediaSourceId").toUUIDOrNull(), ids = json.optJSONArray("ids")?.let { array -> ArrayList().apply { for (i in 0 until array.size) { @@ -30,6 +29,7 @@ data class PlayOptions( } } } ?: emptyList(), + mediaSourceId = json.optString("mediaSourceId"), startIndex = json.optInt("startIndex"), startPositionTicks = json.optLong("startPositionTicks").takeIf { it > 0 }, audioStreamIndex = json.optString("audioStreamIndex").toIntOrNull(), diff --git a/app/src/main/java/org/jellyfin/mobile/player/queue/QueueManager.kt b/app/src/main/java/org/jellyfin/mobile/player/queue/QueueManager.kt index 99d1299cc..542e2a9e4 100644 --- a/app/src/main/java/org/jellyfin/mobile/player/queue/QueueManager.kt +++ b/app/src/main/java/org/jellyfin/mobile/player/queue/QueueManager.kt @@ -27,6 +27,7 @@ import org.jellyfin.sdk.api.operations.VideosApi import org.jellyfin.sdk.model.api.MediaProtocol import org.jellyfin.sdk.model.api.MediaStream import org.jellyfin.sdk.model.api.PlayMethod +import org.jellyfin.sdk.model.serializer.toUUIDOrNull import org.koin.core.component.KoinComponent import org.koin.core.component.get import org.koin.core.component.inject @@ -57,9 +58,13 @@ class QueueManager( */ suspend fun startPlayback(playOptions: PlayOptions, playWhenReady: Boolean): PlayerException? { if (playOptions != currentPlayOptions) { - val itemId = playOptions.run { mediaSourceId ?: ids[playOptions.startIndex] } + val itemId = playOptions.run { + ids.getOrNull(startIndex) ?: mediaSourceId?.toUUIDOrNull() // fallback if ids is empty + } ?: return PlayerException.InvalidPlayOptions() + mediaSourceResolver.resolveMediaSource( itemId = itemId, + mediaSourceId = playOptions.mediaSourceId, deviceProfile = deviceProfile, maxStreamingBitrate = playOptions.maxBitrate, startTimeTicks = playOptions.startPositionTicks, @@ -124,7 +129,10 @@ class QueueManager( } is QueueItem.Stub -> { val previousId = previous.ids.lastOrNull() ?: return false - val jellyfinMediaSource = mediaSourceResolver.resolveMediaSource(previousId, deviceProfile).getOrNull() ?: return false + val jellyfinMediaSource = mediaSourceResolver.resolveMediaSource( + itemId = previousId, + deviceProfile = deviceProfile, + ).getOrNull() ?: return false val previousPrevious = QueueItem.Stub(previous.ids.dropLast(1)) createQueueItem(jellyfinMediaSource, previousPrevious, current).play() @@ -141,7 +149,10 @@ class QueueManager( } is QueueItem.Stub -> { val nextId = next.ids.firstOrNull() ?: return false - val jellyfinMediaSource = mediaSourceResolver.resolveMediaSource(nextId, deviceProfile).getOrNull() ?: return false + val jellyfinMediaSource = mediaSourceResolver.resolveMediaSource( + itemId = nextId, + deviceProfile = deviceProfile, + ).getOrNull() ?: return false val nextNext = QueueItem.Stub(next.ids.drop(1)) createQueueItem(jellyfinMediaSource, current, nextNext).play() diff --git a/app/src/main/java/org/jellyfin/mobile/player/source/MediaSourceResolver.kt b/app/src/main/java/org/jellyfin/mobile/player/source/MediaSourceResolver.kt index 792a0ec0b..9c13d7e3b 100644 --- a/app/src/main/java/org/jellyfin/mobile/player/source/MediaSourceResolver.kt +++ b/app/src/main/java/org/jellyfin/mobile/player/source/MediaSourceResolver.kt @@ -20,6 +20,7 @@ class MediaSourceResolver(private val apiClient: ApiClient) { @Suppress("ReturnCount") suspend fun resolveMediaSource( itemId: UUID, + mediaSourceId: String? = null, deviceProfile: DeviceProfile, maxStreamingBitrate: Int? = null, startTimeTicks: Long? = null, @@ -36,7 +37,7 @@ class MediaSourceResolver(private val apiClient: ApiClient) { // We need to remove the dashes so that the server can find the correct media source. // And if we didn't pass the mediaSourceId, our stream indices would silently get ignored. // https://github.com/jellyfin/jellyfin/blob/9a35fd673203cfaf0098138b2768750f4818b3ab/Jellyfin.Api/Helpers/MediaInfoHelper.cs#L196-L201 - mediaSourceId = itemId.toString().replace("-", ""), + mediaSourceId = mediaSourceId ?: itemId.toString().replace("-", ""), deviceProfile = deviceProfile, maxStreamingBitrate = maxStreamingBitrate ?: deviceProfile.maxStreamingBitrate, startTimeTicks = startTimeTicks,