refactor(playback): simplify getVideoStreamUrl usage and improve direct play handling

This commit is contained in:
2026-05-29 18:09:12 +02:00
parent 97dcea57ec
commit 5c1ddffe7a
3 changed files with 16 additions and 27 deletions

View File

@@ -465,21 +465,11 @@ class JellyfinApiClient @Inject constructor(
} }
fun getVideoStreamUrl( fun getVideoStreamUrl(
itemId: UUID, itemId: UUID
mediaSourceId: String?,
container: String? = null,
tag: String? = null,
playSessionId: String? = null,
liveStreamId: String? = null,
): String = try { ): String = try {
api.videosApi.getVideoStreamUrl( api.videosApi.getVideoStreamUrl(
itemId = itemId, itemId = itemId,
container = container,
mediaSourceId = mediaSourceId,
static = true, static = true,
tag = tag,
playSessionId = playSessionId,
liveStreamId = liveStreamId,
) )
} catch (error: Exception) { } catch (error: Exception) {
Timber.tag(TAG).e(error, "getVideoStreamUrl") Timber.tag(TAG).e(error, "getVideoStreamUrl")

View File

@@ -2,15 +2,15 @@ package hu.bbara.purefin.data.jellyfin.playback
import hu.bbara.purefin.core.data.UserSessionRepository import hu.bbara.purefin.core.data.UserSessionRepository
import hu.bbara.purefin.data.jellyfin.client.JellyfinApiClient import hu.bbara.purefin.data.jellyfin.client.JellyfinApiClient
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.model.ServerVersion import org.jellyfin.sdk.model.ServerVersion
import timber.log.Timber import timber.log.Timber
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import javax.inject.Singleton
@Singleton @Singleton
class JellyfinPlaybackResolver @Inject constructor( class JellyfinPlaybackResolver @Inject constructor(
@@ -37,20 +37,19 @@ class JellyfinPlaybackResolver @Inject constructor(
return@withContext null return@withContext null
} }
val directPlayUrl = jellyfinApiClient.getVideoStreamUrl(
itemId = mediaId,
)
if (directPlayUrl.isBlank()) {
Timber.tag(TAG).e("Direct play URL is blank for $mediaId")
return@withContext null
}
val decision = PlaybackDecisionResolver.resolve( val decision = PlaybackDecisionResolver.resolve(
mediaSources = playbackInfo.mediaSources, mediaSources = playbackInfo.mediaSources,
playSessionId = playbackInfo.playSessionId, playSessionId = playbackInfo.playSessionId,
serverUrl = serverUrl, serverUrl = serverUrl,
directPlayUrl = { mediaSource -> directPlayUrl = directPlayUrl
jellyfinApiClient.getVideoStreamUrl(
itemId = mediaId,
container = mediaSource.container,
mediaSourceId = mediaSource.id,
tag = mediaSource.eTag,
playSessionId = playbackInfo.playSessionId,
liveStreamId = mediaSource.liveStreamId,
)
},
) )
if (decision == null) { if (decision == null) {

View File

@@ -10,7 +10,7 @@ internal object PlaybackDecisionResolver {
mediaSources: List<MediaSourceInfo>, mediaSources: List<MediaSourceInfo>,
playSessionId: String?, playSessionId: String?,
serverUrl: String, serverUrl: String,
directPlayUrl: (MediaSourceInfo) -> String, directPlayUrl: String,
): PlaybackDecision? { ): PlaybackDecision? {
val mediaSource = mediaSources.firstOrNull { it.protocol == MediaProtocol.FILE && !it.isRemote } val mediaSource = mediaSources.firstOrNull { it.protocol == MediaProtocol.FILE && !it.isRemote }
?: return null ?: return null
@@ -23,7 +23,7 @@ internal object PlaybackDecisionResolver {
} }
val url = when (playMethod) { val url = when (playMethod) {
PlaybackMethod.DIRECT_PLAY -> directPlayUrl(mediaSource) PlaybackMethod.DIRECT_PLAY -> directPlayUrl
PlaybackMethod.DIRECT_STREAM, PlaybackMethod.DIRECT_STREAM,
PlaybackMethod.TRANSCODE, PlaybackMethod.TRANSCODE,
-> absolutePlaybackUrl(serverUrl, requireNotNull(mediaSource.transcodingUrl)) -> absolutePlaybackUrl(serverUrl, requireNotNull(mediaSource.transcodingUrl))