diff --git a/app/src/main/java/hu/bbara/purefin/ui/screen/player/PlayerScreen.kt b/app/src/main/java/hu/bbara/purefin/ui/screen/player/PlayerScreen.kt index 0f0f6bb6..d53404fe 100644 --- a/app/src/main/java/hu/bbara/purefin/ui/screen/player/PlayerScreen.kt +++ b/app/src/main/java/hu/bbara/purefin/ui/screen/player/PlayerScreen.kt @@ -26,7 +26,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -39,6 +38,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.media3.common.util.UnstableApi import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.PlayerView @@ -116,10 +116,6 @@ fun PlayerScreen( subtitleView?.setBottomPaddingFraction(subtitleBottomPaddingFraction) } }, - update = { - it.player = viewModel.player - it.subtitleView?.setBottomPaddingFraction(subtitleBottomPaddingFraction) - }, modifier = Modifier .fillMaxHeight() .align(Alignment.Center) diff --git a/core/src/main/java/hu/bbara/purefin/core/data/PlaybackReportContext.kt b/core/src/main/java/hu/bbara/purefin/core/data/PlaybackReportContext.kt index 8f5915cb..a6c2dbd6 100644 --- a/core/src/main/java/hu/bbara/purefin/core/data/PlaybackReportContext.kt +++ b/core/src/main/java/hu/bbara/purefin/core/data/PlaybackReportContext.kt @@ -5,6 +5,5 @@ data class PlaybackReportContext( val mediaSourceId: String?, val audioStreamIndex: Int?, val subtitleStreamIndex: Int?, - val liveStreamId: String?, val playSessionId: String?, ) diff --git a/core/src/main/java/hu/bbara/purefin/core/player/module/VideoPlayerModule.kt b/core/src/main/java/hu/bbara/purefin/core/player/module/VideoPlayerModule.kt index dd9c2ea0..0dda3528 100644 --- a/core/src/main/java/hu/bbara/purefin/core/player/module/VideoPlayerModule.kt +++ b/core/src/main/java/hu/bbara/purefin/core/player/module/VideoPlayerModule.kt @@ -13,6 +13,7 @@ import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.SeekParameters import androidx.media3.exoplayer.source.DefaultMediaSourceFactory import androidx.media3.exoplayer.trackselection.DefaultTrackSelector +import androidx.media3.exoplayer.util.EventLogger import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -69,6 +70,7 @@ object VideoPlayerModule { .apply { playWhenReady = true pauseAtEndOfMediaItems = false + addAnalyticsListener(EventLogger()) } } } diff --git a/data/src/main/java/hu/bbara/purefin/data/jellyfin/DefaultPlayableMediaRepository.kt b/data/src/main/java/hu/bbara/purefin/data/jellyfin/DefaultPlayableMediaRepository.kt index 561a2e10..3d196a83 100644 --- a/data/src/main/java/hu/bbara/purefin/data/jellyfin/DefaultPlayableMediaRepository.kt +++ b/data/src/main/java/hu/bbara/purefin/data/jellyfin/DefaultPlayableMediaRepository.kt @@ -17,8 +17,6 @@ import hu.bbara.purefin.core.image.ImageUrlBuilder import hu.bbara.purefin.core.player.preference.TrackPreferencesRepository import hu.bbara.purefin.data.jellyfin.client.JellyfinApiClient import hu.bbara.purefin.data.jellyfin.playback.JellyfinPlaybackResolver -import hu.bbara.purefin.data.jellyfin.playback.PlaybackDecision -import hu.bbara.purefin.data.jellyfin.playback.playbackCustomCacheKey import hu.bbara.purefin.model.Episode import hu.bbara.purefin.model.MediaSegment import hu.bbara.purefin.model.PlayableMedia @@ -86,14 +84,14 @@ class DefaultPlayableMediaRepository @Inject constructor( downloadedMediaItem: MediaItem?, ): PlayableMedia? { val baseItem = jellyfinApiClient.getItemInfo(mediaId) ?: return null - val playbackDecision = jellyfinPlaybackResolver.getPlaybackDecision(mediaId) ?: return null + val playbackSource = jellyfinPlaybackResolver.getPlaybackSource(mediaId) ?: return null val mediaItem = if (downloadedMediaItem == null) { - getMediaItem(baseItem, playbackDecision) + getMediaItem(baseItem, playbackSource.directPlayUrl) } else { - getDownloadedMediaItem(baseItem, playbackDecision, downloadedMediaItem) + getDownloadedMediaItem(baseItem, downloadedMediaItem) } - val resumePositionMs = calculateResumePosition(baseItem, playbackDecision.mediaSource) + val resumePositionMs = calculateResumePosition(baseItem, playbackSource.mediaSource) val preferenceMediaId = when (baseItem.type) { BaseItemKind.EPISODE -> baseItem.seriesId ?: mediaId else -> mediaId @@ -172,7 +170,7 @@ class DefaultPlayableMediaRepository @Inject constructor( return null } - private suspend fun getMediaItem(baseItem: BaseItemDto, playbackDecision: PlaybackDecision): MediaItem = withContext(Dispatchers.IO) { + private suspend fun getMediaItem(baseItem: BaseItemDto, url: String): MediaItem = withContext(Dispatchers.IO) { val mediaId = baseItem.id val baseItem = jellyfinApiClient.getItemInfo(mediaId) @@ -181,11 +179,12 @@ class DefaultPlayableMediaRepository @Inject constructor( val mediaItem = createMediaItem( mediaId = mediaId.toString(), - playbackDecision = playbackDecision, - title = baseItem?.name ?: playbackDecision.mediaSource.name ?: "Unknown", + url = url, + title = baseItem?.name ?: "Unknown", subtitle = seasonEpisodeLabel(baseItem), artworkUrl = artworkUrl, - playbackReportContext = playbackDecision.reportContext, + // TODO + playbackReportContext = null, ) return@withContext mediaItem @@ -193,7 +192,6 @@ class DefaultPlayableMediaRepository @Inject constructor( private suspend fun getDownloadedMediaItem( baseItem: BaseItemDto, - playbackDecision: PlaybackDecision, downloadedMediaItem: MediaItem, ): MediaItem = withContext(Dispatchers.IO) { val mediaId = baseItem.id @@ -203,10 +201,11 @@ class DefaultPlayableMediaRepository @Inject constructor( return@withContext downloadedMediaItem.withMetadata( mediaId = mediaId.toString(), - title = baseItem?.name ?: playbackDecision.mediaSource.name ?: "Unknown", + title = baseItem?.name ?: "Unknown", subtitle = seasonEpisodeLabel(baseItem), artworkUrl = artworkUrl, - playbackReportContext = playbackDecision.reportContext, + // TODO + playbackReportContext = null, ) } @@ -240,7 +239,7 @@ class DefaultPlayableMediaRepository @Inject constructor( @OptIn(UnstableApi::class) private fun createMediaItem( mediaId: String, - playbackDecision: PlaybackDecision, + url: String, title: String, subtitle: String?, artworkUrl: String, @@ -252,17 +251,10 @@ class DefaultPlayableMediaRepository @Inject constructor( .setArtworkUri(artworkUrl.toUri()) .build() val builder = MediaItem.Builder() - .setUri(playbackDecision.url.toUri()) + .setUri(url.toUri()) .setMediaId(mediaId) .setMediaMetadata(metadata) .setTag(playbackReportContext) - - playbackCustomCacheKey( - mediaId = mediaId, - playbackUrl = playbackDecision.url, - playMethod = playbackDecision.reportContext.playMethod, - )?.let(builder::setCustomCacheKey) - return builder.build() } diff --git a/data/src/main/java/hu/bbara/purefin/data/jellyfin/client/JellyfinApiClient.kt b/data/src/main/java/hu/bbara/purefin/data/jellyfin/client/JellyfinApiClient.kt index 0cf32ae5..cb09264f 100644 --- a/data/src/main/java/hu/bbara/purefin/data/jellyfin/client/JellyfinApiClient.kt +++ b/data/src/main/java/hu/bbara/purefin/data/jellyfin/client/JellyfinApiClient.kt @@ -511,7 +511,6 @@ class JellyfinApiClient @Inject constructor( mediaSourceId = reportContext.mediaSourceId, audioStreamIndex = reportContext.audioStreamIndex, subtitleStreamIndex = reportContext.subtitleStreamIndex, - liveStreamId = reportContext.liveStreamId, playSessionId = reportContext.playSessionId, playMethod = reportContext.playMethod.toJellyfinPlayMethod(), repeatMode = RepeatMode.REPEAT_NONE, @@ -539,7 +538,6 @@ class JellyfinApiClient @Inject constructor( mediaSourceId = reportContext.mediaSourceId, audioStreamIndex = reportContext.audioStreamIndex, subtitleStreamIndex = reportContext.subtitleStreamIndex, - liveStreamId = reportContext.liveStreamId, playSessionId = reportContext.playSessionId, playMethod = reportContext.playMethod.toJellyfinPlayMethod(), repeatMode = RepeatMode.REPEAT_NONE, @@ -561,7 +559,6 @@ class JellyfinApiClient @Inject constructor( itemId = itemId, positionTicks = positionTicks, mediaSourceId = reportContext.mediaSourceId, - liveStreamId = reportContext.liveStreamId, playSessionId = reportContext.playSessionId, failed = false, ), diff --git a/data/src/main/java/hu/bbara/purefin/data/jellyfin/download/JellyfinDownloadMediaSourceResolver.kt b/data/src/main/java/hu/bbara/purefin/data/jellyfin/download/JellyfinDownloadMediaSourceResolver.kt index 5d6f259b..5abbe224 100644 --- a/data/src/main/java/hu/bbara/purefin/data/jellyfin/download/JellyfinDownloadMediaSourceResolver.kt +++ b/data/src/main/java/hu/bbara/purefin/data/jellyfin/download/JellyfinDownloadMediaSourceResolver.kt @@ -3,7 +3,6 @@ package hu.bbara.purefin.data.jellyfin.download import hu.bbara.purefin.core.data.DownloadMediaSourceResolver import hu.bbara.purefin.core.data.EpisodeDownloadSource import hu.bbara.purefin.core.data.MovieDownloadSource -import hu.bbara.purefin.core.data.PlaybackMethod import hu.bbara.purefin.core.data.UserSessionRepository import hu.bbara.purefin.data.converter.toEpisode import hu.bbara.purefin.data.converter.toMovie @@ -11,7 +10,7 @@ import hu.bbara.purefin.data.converter.toSeason import hu.bbara.purefin.data.converter.toSeries import hu.bbara.purefin.data.jellyfin.client.JellyfinApiClient import hu.bbara.purefin.data.jellyfin.playback.JellyfinPlaybackResolver -import hu.bbara.purefin.data.jellyfin.playback.PlaybackDecision +import hu.bbara.purefin.data.jellyfin.playback.PlaybackSource import hu.bbara.purefin.data.jellyfin.playback.playbackCustomCacheKey import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first @@ -32,13 +31,13 @@ class JellyfinDownloadMediaSourceResolver @Inject constructor( return@withContext null } - val playbackDecision = jellyfinPlaybackResolver.getPlaybackDecision(movieId) ?: return@withContext null + val playbackSource = jellyfinPlaybackResolver.getPlaybackSource(movieId) ?: return@withContext null val itemInfo = jellyfinApiClient.getItemInfo(movieId) ?: return@withContext null MovieDownloadSource( movie = itemInfo.toMovie(serverUrl), - playbackUrl = playbackDecision.url, - customCacheKey = playbackDecision.downloadCustomCacheKey(movieId), + playbackUrl = playbackSource.directPlayUrl, + customCacheKey = playbackSource.downloadCustomCacheKey(movieId), ) } @@ -48,7 +47,7 @@ class JellyfinDownloadMediaSourceResolver @Inject constructor( return@withContext null } - val playbackDecision = jellyfinPlaybackResolver.getPlaybackDecision(episodeId) ?: return@withContext null + val playbackSource = jellyfinPlaybackResolver.getPlaybackSource(episodeId) ?: return@withContext null val episodeDto = jellyfinApiClient.getItemInfo(episodeId) ?: return@withContext null val episode = episodeDto.toEpisode(serverUrl) val series = jellyfinApiClient.getItemInfo(episode.seriesId)?.toSeries(serverUrl) ?: return@withContext null @@ -58,8 +57,8 @@ class JellyfinDownloadMediaSourceResolver @Inject constructor( episode = episode, series = series, season = season, - playbackUrl = playbackDecision.url, - customCacheKey = playbackDecision.downloadCustomCacheKey(episodeId), + playbackUrl = playbackSource.directPlayUrl, + customCacheKey = playbackSource.downloadCustomCacheKey(episodeId), ) } @@ -91,14 +90,11 @@ class JellyfinDownloadMediaSourceResolver @Inject constructor( .map { it.id } } - private fun PlaybackDecision.downloadCustomCacheKey(mediaId: UUID): String? { - if (reportContext.playMethod != PlaybackMethod.DIRECT_PLAY) { - return null - } + private fun PlaybackSource.downloadCustomCacheKey(mediaId: UUID): String? { return playbackCustomCacheKey( mediaId = mediaId.toString(), - playbackUrl = url, - playMethod = reportContext.playMethod, + playbackUrl = directPlayUrl, + playMethod = playbackReportContext.playMethod, ) } } diff --git a/data/src/main/java/hu/bbara/purefin/data/jellyfin/playback/JellyfinPlaybackResolver.kt b/data/src/main/java/hu/bbara/purefin/data/jellyfin/playback/JellyfinPlaybackResolver.kt index a32cc66e..b6000c23 100644 --- a/data/src/main/java/hu/bbara/purefin/data/jellyfin/playback/JellyfinPlaybackResolver.kt +++ b/data/src/main/java/hu/bbara/purefin/data/jellyfin/playback/JellyfinPlaybackResolver.kt @@ -1,5 +1,7 @@ package hu.bbara.purefin.data.jellyfin.playback +import hu.bbara.purefin.core.data.PlaybackMethod +import hu.bbara.purefin.core.data.PlaybackReportContext import hu.bbara.purefin.core.data.UserSessionRepository import hu.bbara.purefin.data.jellyfin.client.JellyfinApiClient import kotlinx.coroutines.Dispatchers @@ -17,7 +19,7 @@ class JellyfinPlaybackResolver @Inject constructor( private val playbackProfilePolicy: PlaybackProfilePolicy, ) { - suspend fun getPlaybackDecision(mediaId: UUID): PlaybackDecision? = withContext(Dispatchers.IO) { + suspend fun getPlaybackSource(mediaId: UUID): PlaybackSource? = withContext(Dispatchers.IO) { val serverUrl = userSessionRepository.serverUrl.first().trim() if (serverUrl.isBlank()) { return@withContext null @@ -33,6 +35,11 @@ class JellyfinPlaybackResolver @Inject constructor( return@withContext null } + if (playbackInfo.mediaSources.isEmpty()) { + Timber.tag(TAG).w("No media sources available for $mediaId") + return@withContext null + } + val directPlayUrl = jellyfinApiClient.getVideoStreamUrl( itemId = mediaId, ) @@ -41,19 +48,18 @@ class JellyfinPlaybackResolver @Inject constructor( return@withContext null } - val decision = PlaybackDecisionResolver.resolve( - mediaSources = playbackInfo.mediaSources, - playSessionId = playbackInfo.playSessionId, - serverUrl = serverUrl, - directPlayUrl = directPlayUrl + PlaybackSource( + mediaSource = playbackInfo.mediaSources.first(), + directPlayUrl = directPlayUrl, + transcodingUrl = playbackInfo.mediaSources.firstOrNull()?.transcodingUrl, + playbackReportContext = PlaybackReportContext( + playMethod = PlaybackMethod.DIRECT_PLAY, + mediaSourceId = playbackInfo.mediaSources.firstOrNull()?.id, + audioStreamIndex = playbackInfo.mediaSources.firstOrNull()?.defaultAudioStreamIndex, + subtitleStreamIndex = playbackInfo.mediaSources.firstOrNull()?.defaultSubtitleStreamIndex, + playSessionId = playbackInfo.playSessionId, + ), ) - - if (decision == null) { - Timber.tag(TAG).w("No compatible playback path for $mediaId") - } else { - Timber.tag(TAG).d("Playback decision for $mediaId resolved as ${decision.reportContext.playMethod}") - } - decision } private companion object { diff --git a/data/src/main/java/hu/bbara/purefin/data/jellyfin/playback/PlaybackDecisionResolver.kt b/data/src/main/java/hu/bbara/purefin/data/jellyfin/playback/PlaybackDecisionResolver.kt deleted file mode 100644 index 1f68dcb5..00000000 --- a/data/src/main/java/hu/bbara/purefin/data/jellyfin/playback/PlaybackDecisionResolver.kt +++ /dev/null @@ -1,52 +0,0 @@ -package hu.bbara.purefin.data.jellyfin.playback - -import hu.bbara.purefin.core.data.PlaybackMethod -import hu.bbara.purefin.core.data.PlaybackReportContext -import org.jellyfin.sdk.model.api.MediaProtocol -import org.jellyfin.sdk.model.api.MediaSourceInfo - -internal object PlaybackDecisionResolver { - fun resolve( - mediaSources: List, - playSessionId: String?, - serverUrl: String, - directPlayUrl: String, - ): PlaybackDecision? { - val mediaSource = mediaSources.firstOrNull { it.protocol == MediaProtocol.FILE && !it.isRemote } - ?: return null - - val playMethod = when { - mediaSource.supportsDirectPlay -> PlaybackMethod.DIRECT_PLAY - mediaSource.supportsDirectStream && !mediaSource.transcodingUrl.isNullOrBlank() -> PlaybackMethod.DIRECT_STREAM - mediaSource.supportsTranscoding && !mediaSource.transcodingUrl.isNullOrBlank() -> PlaybackMethod.TRANSCODE - else -> return null - } - - val url = when (playMethod) { - PlaybackMethod.DIRECT_PLAY -> directPlayUrl - PlaybackMethod.DIRECT_STREAM, - PlaybackMethod.TRANSCODE, - -> absolutePlaybackUrl(serverUrl, requireNotNull(mediaSource.transcodingUrl)) - } - - return PlaybackDecision( - url = url, - mediaSource = mediaSource, - reportContext = PlaybackReportContext( - playMethod = playMethod, - mediaSourceId = mediaSource.id, - audioStreamIndex = mediaSource.defaultAudioStreamIndex, - subtitleStreamIndex = mediaSource.defaultSubtitleStreamIndex, - liveStreamId = mediaSource.liveStreamId, - playSessionId = playSessionId, - ), - ) - } - - fun absolutePlaybackUrl(serverUrl: String, playbackUrl: String): String = when { - playbackUrl.startsWith("http://", ignoreCase = true) -> playbackUrl - playbackUrl.startsWith("https://", ignoreCase = true) -> playbackUrl - playbackUrl.startsWith("/") -> "${serverUrl.trimEnd('/')}$playbackUrl" - else -> "${serverUrl.trimEnd('/')}/${playbackUrl.trimStart('/')}" - } -} diff --git a/data/src/main/java/hu/bbara/purefin/data/jellyfin/playback/PlaybackSource.kt b/data/src/main/java/hu/bbara/purefin/data/jellyfin/playback/PlaybackSource.kt new file mode 100644 index 00000000..1e64a4ad --- /dev/null +++ b/data/src/main/java/hu/bbara/purefin/data/jellyfin/playback/PlaybackSource.kt @@ -0,0 +1,11 @@ +package hu.bbara.purefin.data.jellyfin.playback + +import hu.bbara.purefin.core.data.PlaybackReportContext +import org.jellyfin.sdk.model.api.MediaSourceInfo + +data class PlaybackSource( + val mediaSource: MediaSourceInfo, + val directPlayUrl: String, + val transcodingUrl: String?, + val playbackReportContext: PlaybackReportContext, +) \ No newline at end of file diff --git a/data/src/test/java/hu/bbara/purefin/data/jellyfin/playback/PlaybackDecisionResolverTest.kt b/data/src/test/java/hu/bbara/purefin/data/jellyfin/playback/PlaybackDecisionResolverTest.kt deleted file mode 100644 index c8da70ab..00000000 --- a/data/src/test/java/hu/bbara/purefin/data/jellyfin/playback/PlaybackDecisionResolverTest.kt +++ /dev/null @@ -1,151 +0,0 @@ -package hu.bbara.purefin.data.jellyfin.playback - -import hu.bbara.purefin.core.data.PlaybackMethod -import org.jellyfin.sdk.model.api.MediaProtocol -import org.jellyfin.sdk.model.api.MediaSourceInfo -import org.jellyfin.sdk.model.api.MediaSourceType -import org.jellyfin.sdk.model.api.MediaStreamProtocol -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNull -import org.junit.Assert.assertSame -import org.junit.Test - -class PlaybackDecisionResolverTest { - @Test - fun `resolve prefers local file-backed source for direct play`() { - val remoteSource = mediaSource( - id = "remote", - protocol = MediaProtocol.HTTP, - isRemote = true, - supportsDirectPlay = true, - ) - val localSource = mediaSource( - id = "local", - supportsDirectPlay = true, - defaultAudioStreamIndex = 2, - defaultSubtitleStreamIndex = 4, - liveStreamId = "live-1", - ) - - val decision = requireNotNull( - PlaybackDecisionResolver.resolve( - mediaSources = listOf(remoteSource, localSource), - playSessionId = "session-1", - serverUrl = "https://example.com/jellyfin", - ) { mediaSource -> - "direct://${mediaSource.id}" - } - ) - - assertSame(localSource, decision.mediaSource) - assertEquals("direct://local", decision.url) - assertEquals(PlaybackMethod.DIRECT_PLAY, decision.reportContext.playMethod) - assertEquals("local", decision.reportContext.mediaSourceId) - assertEquals(2, decision.reportContext.audioStreamIndex) - assertEquals(4, decision.reportContext.subtitleStreamIndex) - assertEquals("live-1", decision.reportContext.liveStreamId) - assertEquals("session-1", decision.reportContext.playSessionId) - } - - @Test - fun `resolve uses transcoding url for direct stream`() { - val source = mediaSource( - id = "direct-stream", - supportsDirectStream = true, - transcodingUrl = "/Videos/1/master.m3u8", - ) - - val decision = requireNotNull( - PlaybackDecisionResolver.resolve( - mediaSources = listOf(source), - playSessionId = "session-2", - serverUrl = "https://example.com/jellyfin", - ) { error("direct play should not be used") } - ) - - assertEquals(PlaybackMethod.DIRECT_STREAM, decision.reportContext.playMethod) - assertEquals("https://example.com/jellyfin/Videos/1/master.m3u8", decision.url) - } - - @Test - fun `resolve uses transcoding when direct stream is unavailable`() { - val source = mediaSource( - id = "transcode", - supportsTranscoding = true, - transcodingUrl = "Videos/2/master.m3u8", - ) - - val decision = requireNotNull( - PlaybackDecisionResolver.resolve( - mediaSources = listOf(source), - playSessionId = "session-3", - serverUrl = "https://example.com/jellyfin", - ) { error("direct play should not be used") } - ) - - assertEquals(PlaybackMethod.TRANSCODE, decision.reportContext.playMethod) - assertEquals("https://example.com/jellyfin/Videos/2/master.m3u8", decision.url) - } - - @Test - fun `resolve returns null when no usable source exists`() { - val source = mediaSource(id = "unusable") - - val decision = PlaybackDecisionResolver.resolve( - mediaSources = listOf(source), - playSessionId = "session-4", - serverUrl = "https://example.com/jellyfin", - ) { error("direct play should not be used") } - - assertNull(decision) - } - - @Test - fun `absolute playback url preserves absolute urls`() { - val url = PlaybackDecisionResolver.absolutePlaybackUrl( - serverUrl = "https://example.com/jellyfin", - playbackUrl = "https://cdn.example.com/master.m3u8", - ) - - assertEquals("https://cdn.example.com/master.m3u8", url) - } - - private fun mediaSource( - id: String, - protocol: MediaProtocol = MediaProtocol.FILE, - isRemote: Boolean = false, - supportsDirectPlay: Boolean = false, - supportsDirectStream: Boolean = false, - supportsTranscoding: Boolean = false, - transcodingUrl: String? = null, - defaultAudioStreamIndex: Int? = null, - defaultSubtitleStreamIndex: Int? = null, - liveStreamId: String? = null, - ): MediaSourceInfo { - return MediaSourceInfo( - protocol = protocol, - id = id, - type = MediaSourceType.DEFAULT, - container = "mkv", - isRemote = isRemote, - readAtNativeFramerate = true, - ignoreDts = false, - ignoreIndex = false, - genPtsInput = false, - supportsTranscoding = supportsTranscoding, - supportsDirectStream = supportsDirectStream, - supportsDirectPlay = supportsDirectPlay, - isInfiniteStream = false, - requiresOpening = false, - requiresClosing = false, - liveStreamId = liveStreamId, - requiresLooping = false, - supportsProbing = true, - transcodingUrl = transcodingUrl, - defaultAudioStreamIndex = defaultAudioStreamIndex, - defaultSubtitleStreamIndex = defaultSubtitleStreamIndex, - transcodingSubProtocol = MediaStreamProtocol.HLS, - hasSegments = false, - ) - } -}