Update App.kt

This commit is contained in:
AdityasahuX07 2026-05-14 16:43:55 +05:30 committed by GitHub
parent 83eca2fade
commit 823f1e894d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -128,9 +128,6 @@ import com.nuvio.app.features.player.PlayerLaunch
import com.nuvio.app.features.player.PlayerLaunchStore
import com.nuvio.app.features.player.PlayerRoute
import com.nuvio.app.features.player.PlayerScreen
import com.nuvio.app.features.player.ExternalPlayerOpenResult
import com.nuvio.app.features.player.ExternalPlayerPlatform
import com.nuvio.app.features.player.ExternalPlayerPlaybackRequest
import com.nuvio.app.features.player.sanitizePlaybackHeaders
import com.nuvio.app.features.player.sanitizePlaybackResponseHeaders
import com.nuvio.app.features.profiles.AvatarRepository
@ -291,14 +288,6 @@ private fun NativeNavigationTab.toAppScreenTab(): AppScreenTab = when (this) {
NativeNavigationTab.Settings -> AppScreenTab.Settings
}
private fun PlayerLaunch.toExternalPlayerPlaybackRequest(): ExternalPlayerPlaybackRequest =
ExternalPlayerPlaybackRequest(
sourceUrl = sourceUrl,
title = title,
streamTitle = streamTitle,
sourceHeaders = sourceHeaders,
)
private enum class AppGateScreen {
Loading,
Auth,
@ -531,7 +520,6 @@ private fun MainAppContent(
val hapticFeedback = LocalHapticFeedback.current
val coroutineScope = rememberCoroutineScope()
var selectedTab by rememberSaveable { mutableStateOf(AppScreenTab.Home) }
var searchFocusRequestCount by remember { mutableStateOf(0) }
val currentBackStackEntry by navController.currentBackStackEntryAsState()
val nativeRequestedTab by remember { NativeTabBridge.requestedTab }.collectAsStateWithLifecycle()
val liquidGlassNativeTabBarEnabled by remember {
@ -574,9 +562,6 @@ private fun MainAppContent(
NetworkStatusRepository.uiState
}.collectAsStateWithLifecycle()
val downloadedProviderLabel = stringResource(Res.string.provider_downloaded)
val externalPlayerNotConfiguredText = stringResource(Res.string.external_player_not_configured)
val externalPlayerUnavailableText = stringResource(Res.string.external_player_unavailable)
val externalPlayerFailedText = stringResource(Res.string.external_player_failed)
val isTraktLibrarySource = libraryUiState.sourceMode == LibrarySourceMode.TRAKT
var initialHomeReady by rememberSaveable { mutableStateOf(false) }
var offlineLaunchRouteHandled by rememberSaveable { mutableStateOf(false) }
@ -598,9 +583,6 @@ private fun MainAppContent(
LaunchedEffect(selectedTab) {
NativeTabBridge.publishSelectedTab(selectedTab.toNativeNavigationTab())
if (selectedTab != AppScreenTab.Search) {
searchFocusRequestCount = 0
}
}
DisposableEffect(
@ -770,29 +752,6 @@ private fun MainAppContent(
}
}
fun openExternalPlayback(launch: PlayerLaunch): Boolean {
return when (
ExternalPlayerPlatform.open(
request = launch.toExternalPlayerPlaybackRequest(),
playerId = playerSettingsUiState.externalPlayerId,
)
) {
ExternalPlayerOpenResult.Opened -> true
ExternalPlayerOpenResult.NotConfigured -> {
NuvioToastController.show(externalPlayerNotConfiguredText)
false
}
ExternalPlayerOpenResult.NoPlayerAvailable -> {
NuvioToastController.show(externalPlayerUnavailableText)
false
}
ExternalPlayerOpenResult.Failed -> {
NuvioToastController.show(externalPlayerFailedText)
false
}
}
}
fun launchPlaybackWithDownloadPreference(
type: String,
videoId: String,
@ -824,7 +783,8 @@ private fun MainAppContent(
)
val localSourceUrl = downloadedItem?.let(DownloadsRepository::playableLocalFileUri)
if (!localSourceUrl.isNullOrBlank()) {
val playerLaunch = PlayerLaunch(
val launchId = PlayerLaunchStore.put(
PlayerLaunch(
title = title,
sourceUrl = localSourceUrl,
sourceHeaders = emptyMap(),
@ -847,12 +807,8 @@ private fun MainAppContent(
parentMetaType = parentMetaType,
initialPositionMs = targetResumePositionMs,
initialProgressFraction = targetResumeProgressFraction,
)
if (playerSettingsUiState.externalPlayerEnabled) {
openExternalPlayback(playerLaunch)
return
}
val launchId = PlayerLaunchStore.put(playerLaunch)
),
)
navController.navigate(PlayerRoute(launchId = launchId))
return
}
@ -1053,13 +1009,7 @@ private fun MainAppContent(
)
NavItem(
selected = selectedTab == AppScreenTab.Search,
onClick = {
if (selectedTab == AppScreenTab.Search) {
searchFocusRequestCount++
} else {
selectedTab = AppScreenTab.Search
}
},
onClick = { selectedTab = AppScreenTab.Search },
icon = Res.drawable.sidebar_search,
contentDescription = stringResource(Res.string.compose_nav_search),
)
@ -1093,7 +1043,6 @@ private fun MainAppContent(
.fillMaxSize()
.padding(innerPadding),
selectedTab = selectedTab,
searchFocusRequestCount = searchFocusRequestCount,
animateHomeCollectionGifs = tabsRouteActive,
onCatalogClick = onCatalogClick,
onPosterClick = { meta ->
@ -1148,13 +1097,7 @@ private fun MainAppContent(
if (isTabletLayout && !useNativeBottomTabs) {
TabletFloatingTopBar(
selectedTab = selectedTab,
onTabSelected = { tab ->
if (tab == AppScreenTab.Search && selectedTab == AppScreenTab.Search) {
searchFocusRequestCount++
} else {
selectedTab = tab
}
},
onTabSelected = { selectedTab = it },
onProfileSelected = onProfileSelected,
onAddProfileRequested = onSwitchProfile,
)
@ -1405,8 +1348,10 @@ private fun MainAppContent(
val maxAgeMs = playerSettings.streamReuseLastLinkCacheHours * 60L * 60L * 1000L
val cached = StreamLinkCacheRepository.getValid(cacheKey, maxAgeMs)
if (cached != null) {
reuseNavigated = true
StreamsRepository.clear()
val playerLaunch = PlayerLaunch(
val launchId = PlayerLaunchStore.put(
PlayerLaunch(
title = launch.title,
sourceUrl = cached.url,
sourceHeaders = sanitizePlaybackHeaders(cached.requestHeaders),
@ -1431,13 +1376,7 @@ private fun MainAppContent(
initialPositionMs = launch.resumePositionMs ?: 0L,
initialProgressFraction = launch.resumeProgressFraction,
)
if (playerSettings.externalPlayerEnabled) {
openExternalPlayback(playerLaunch)
reuseNavigated = true
return@LaunchedEffect
}
reuseNavigated = true
val launchId = PlayerLaunchStore.put(playerLaunch)
)
navController.navigate(PlayerRoute(launchId = launchId)) {
popUpTo<StreamRoute> { inclusive = true }
}
@ -1489,7 +1428,8 @@ private fun MainAppContent(
bingeGroup = stream.behaviorHints.bingeGroup,
)
}
val playerLaunch = PlayerLaunch(
val launchId = PlayerLaunchStore.put(
PlayerLaunch(
title = launch.title,
sourceUrl = sourceUrl,
sourceHeaders = sanitizePlaybackHeaders(stream.behaviorHints.proxyHeaders?.request),
@ -1514,13 +1454,9 @@ private fun MainAppContent(
initialPositionMs = launch.resumePositionMs ?: 0L,
initialProgressFraction = launch.resumeProgressFraction,
)
)
StreamsRepository.consumeAutoPlay()
StreamsRepository.cancelLoading()
if (playerSettings.externalPlayerEnabled) {
openExternalPlayback(playerLaunch)
return@LaunchedEffect
}
val launchId = PlayerLaunchStore.put(playerLaunch)
navController.navigate(PlayerRoute(launchId = launchId)) {
popUpTo<StreamRoute> { inclusive = true }
}
@ -1536,74 +1472,6 @@ private fun MainAppContent(
return@composable
}
fun openSelectedStream(
stream: com.nuvio.app.features.streams.StreamItem,
resolvedResumePositionMs: Long?,
resolvedResumeProgressFraction: Float?,
forceExternal: Boolean,
forceInternal: Boolean,
) {
val sourceUrl = stream.directPlaybackUrl ?: return
if (playerSettings.streamReuseLastLinkEnabled) {
val cacheKey = StreamLinkCacheRepository.contentKey(
type = launch.type,
videoId = effectiveVideoId,
parentMetaId = launch.parentMetaId,
season = launch.seasonNumber,
episode = launch.episodeNumber,
)
StreamLinkCacheRepository.save(
contentKey = cacheKey,
url = sourceUrl,
streamName = stream.streamLabel,
addonName = stream.addonName,
addonId = stream.addonId,
requestHeaders = sanitizePlaybackHeaders(stream.behaviorHints.proxyHeaders?.request),
responseHeaders = sanitizePlaybackResponseHeaders(stream.behaviorHints.proxyHeaders?.response),
filename = stream.behaviorHints.filename,
videoSize = stream.behaviorHints.videoSize,
bingeGroup = stream.behaviorHints.bingeGroup,
)
}
val playerLaunch = PlayerLaunch(
title = launch.title,
sourceUrl = sourceUrl,
sourceHeaders = sanitizePlaybackHeaders(stream.behaviorHints.proxyHeaders?.request),
sourceResponseHeaders = sanitizePlaybackResponseHeaders(stream.behaviorHints.proxyHeaders?.response),
logo = launch.logo,
poster = launch.poster,
background = launch.background,
seasonNumber = launch.seasonNumber,
episodeNumber = launch.episodeNumber,
episodeTitle = launch.episodeTitle,
episodeThumbnail = launch.episodeThumbnail,
streamTitle = stream.streamLabel,
streamSubtitle = stream.streamSubtitle,
bingeGroup = stream.behaviorHints.bingeGroup,
pauseDescription = pauseDescription,
providerName = stream.addonName,
providerAddonId = stream.addonId,
contentType = launch.type,
videoId = effectiveVideoId,
parentMetaId = launch.parentMetaId ?: effectiveVideoId,
parentMetaType = launch.parentMetaType ?: launch.type,
initialPositionMs = resolvedResumePositionMs ?: 0L,
initialProgressFraction = resolvedResumeProgressFraction,
)
if (!forceInternal && (forceExternal || playerSettings.externalPlayerEnabled)) {
openExternalPlayback(playerLaunch)
StreamsRepository.cancelLoading()
return
}
val launchId = PlayerLaunchStore.put(playerLaunch)
StreamsRepository.cancelLoading()
navController.navigate(
PlayerRoute(launchId = launchId)
)
}
StreamsScreen(
type = launch.type,
videoId = effectiveVideoId,
@ -1622,22 +1490,62 @@ private fun MainAppContent(
manualSelection = launch.manualSelection,
startFromBeginning = launch.startFromBeginning,
onStreamSelected = { stream, resolvedResumePositionMs, resolvedResumeProgressFraction ->
openSelectedStream(
stream = stream,
resolvedResumePositionMs = resolvedResumePositionMs,
resolvedResumeProgressFraction = resolvedResumeProgressFraction,
forceExternal = false,
forceInternal = false,
)
},
onStreamActionOpen = { stream, openExternally, resolvedResumePositionMs, resolvedResumeProgressFraction ->
openSelectedStream(
stream = stream,
resolvedResumePositionMs = resolvedResumePositionMs,
resolvedResumeProgressFraction = resolvedResumeProgressFraction,
forceExternal = openExternally,
forceInternal = !openExternally,
)
val sourceUrl = stream.directPlaybackUrl
if (sourceUrl != null) {
// Persist for Reuse Last Link
if (playerSettings.streamReuseLastLinkEnabled) {
val cacheKey = StreamLinkCacheRepository.contentKey(
type = launch.type,
videoId = effectiveVideoId,
parentMetaId = launch.parentMetaId,
season = launch.seasonNumber,
episode = launch.episodeNumber,
)
StreamLinkCacheRepository.save(
contentKey = cacheKey,
url = sourceUrl,
streamName = stream.streamLabel,
addonName = stream.addonName,
addonId = stream.addonId,
requestHeaders = sanitizePlaybackHeaders(stream.behaviorHints.proxyHeaders?.request),
responseHeaders = sanitizePlaybackResponseHeaders(stream.behaviorHints.proxyHeaders?.response),
filename = stream.behaviorHints.filename,
videoSize = stream.behaviorHints.videoSize,
bingeGroup = stream.behaviorHints.bingeGroup,
)
}
val launchId = PlayerLaunchStore.put(
PlayerLaunch(
title = launch.title,
sourceUrl = sourceUrl,
sourceHeaders = sanitizePlaybackHeaders(stream.behaviorHints.proxyHeaders?.request),
sourceResponseHeaders = sanitizePlaybackResponseHeaders(stream.behaviorHints.proxyHeaders?.response),
logo = launch.logo,
poster = launch.poster,
background = launch.background,
seasonNumber = launch.seasonNumber,
episodeNumber = launch.episodeNumber,
episodeTitle = launch.episodeTitle,
episodeThumbnail = launch.episodeThumbnail,
streamTitle = stream.streamLabel,
streamSubtitle = stream.streamSubtitle,
bingeGroup = stream.behaviorHints.bingeGroup,
pauseDescription = pauseDescription,
providerName = stream.addonName,
providerAddonId = stream.addonId,
contentType = launch.type,
videoId = effectiveVideoId,
parentMetaId = launch.parentMetaId ?: effectiveVideoId,
parentMetaType = launch.parentMetaType ?: launch.type,
initialPositionMs = resolvedResumePositionMs ?: 0L,
initialProgressFraction = resolvedResumeProgressFraction,
)
)
StreamsRepository.cancelLoading()
navController.navigate(
PlayerRoute(launchId = launchId)
)
}
},
onBack = {
StreamsRepository.clear()
@ -1722,6 +1630,9 @@ private fun MainAppContent(
onPosterClick = { meta ->
navController.navigate(DetailRoute(type = meta.type, id = meta.id))
},
onPosterLongClick = { meta ->
selectedPosterForActions = meta
},
modifier = Modifier.fillMaxSize(),
)
}
@ -1766,7 +1677,8 @@ private fun MainAppContent(
?.let(WatchProgressRepository::progressForVideo)
?.takeIf { it.isResumable }
val playerLaunch = PlayerLaunch(
val launchId = PlayerLaunchStore.put(
PlayerLaunch(
title = item.title,
sourceUrl = sourceUrl,
sourceHeaders = emptyMap(),
@ -1788,12 +1700,8 @@ private fun MainAppContent(
parentMetaType = item.parentMetaType,
initialPositionMs = resumeEntry?.lastPositionMs?.takeIf { it > 0L } ?: 0L,
initialProgressFraction = resumeEntry?.progressFraction?.takeIf { it > 0f },
),
)
if (playerSettingsUiState.externalPlayerEnabled) {
openExternalPlayback(playerLaunch)
return@DownloadsScreen
}
val launchId = PlayerLaunchStore.put(playerLaunch)
navController.navigate(PlayerRoute(launchId = launchId))
},
)
@ -2102,7 +2010,6 @@ private fun rememberGuardedPopBackStack(
private fun AppTabHost(
selectedTab: AppScreenTab,
modifier: Modifier = Modifier,
searchFocusRequestCount: Int = 0,
animateHomeCollectionGifs: Boolean = true,
onCatalogClick: ((HomeCatalogSection) -> Unit)? = null,
onPosterClick: ((MetaPreview) -> Unit)? = null,
@ -2150,7 +2057,6 @@ private fun AppTabHost(
modifier = Modifier.fillMaxSize(),
onPosterClick = onPosterClick,
onPosterLongClick = onPosterLongClick,
searchFocusRequestCount = searchFocusRequestCount,
)
}