fix: show player streams progressively

This commit is contained in:
tapframe 2026-07-18 13:17:01 +05:30
parent 83ac09bac3
commit f5b75adccb
3 changed files with 123 additions and 123 deletions

View file

@ -450,18 +450,8 @@ private fun EpisodeStreamsPanelContent(
modifier: Modifier = Modifier,
) {
val tokens = MaterialTheme.nuvio
val debridSettings by remember {
DebridSettingsRepository.ensureLoaded()
DebridSettingsRepository.uiState
}.collectAsStateWithLifecycle()
val streamBadgeSettings by remember {
StreamBadgeSettingsRepository.ensureLoaded()
StreamBadgeSettingsRepository.uiState
}.collectAsStateWithLifecycle()
val episode = state.selectedEpisode ?: return
val streamsUiState = state.streamsUiState
val streams = streamsUiState.allStreams
val visibleGroups = streamsUiState.filteredGroups
Column(modifier = modifier) {
Row(
@ -530,53 +520,11 @@ private fun EpisodeStreamsPanelContent(
Spacer(Modifier.height(16.dp))
when {
streamsUiState.isAnyLoading -> {
PlayerModalLoading(
modifier = Modifier.padding(vertical = 24.dp),
)
}
streams.isEmpty() -> {
val error = visibleGroups.firstOrNull { it.error != null }?.error
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 24.dp),
contentAlignment = Alignment.CenterStart,
) {
Text(
text = error ?: stringResource(Res.string.compose_player_no_streams_found),
color = Color.White.copy(alpha = if (error == null) 0.7f else 0.85f),
style = MaterialTheme.typography.bodyLarge,
)
}
}
else -> {
val streamKeys = remember(streams) { streams.stablePlayerKeys() }
LazyColumn(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(top = 4.dp, bottom = 8.dp),
) {
itemsIndexed(
items = streams,
key = { index, _ -> streamKeys[index] },
) { _, stream ->
StreamCard(
stream = stream,
enabled = stream.isSelectableForPlayback(debridSettings.canResolvePlayableLinks),
appendInstantServiceToDefaultName = debridSettings.canResolvePlayableLinks &&
!debridSettings.hasCustomStreamFormatting,
showFileSizeBadges = streamBadgeSettings.showFileSizeBadges,
showAddonLogo = streamBadgeSettings.showAddonLogo,
badgePlacement = streamBadgeSettings.badgePlacement,
onClick = { onStreamSelected(stream, episode) },
)
}
}
}
}
PlayerStreamList(
streamsUiState = streamsUiState,
onStreamSelected = { stream -> onStreamSelected(stream, episode) },
modifier = Modifier.weight(1f),
contentPadding = PaddingValues(top = 4.dp, bottom = 8.dp),
)
}
}

View file

@ -59,17 +59,7 @@ fun PlayerSourcesPanel(
modifier: Modifier = Modifier,
) {
val tokens = MaterialTheme.nuvio
val debridSettings by remember {
DebridSettingsRepository.ensureLoaded()
DebridSettingsRepository.uiState
}.collectAsStateWithLifecycle()
val streamBadgeSettings by remember {
StreamBadgeSettingsRepository.ensureLoaded()
StreamBadgeSettingsRepository.uiState
}.collectAsStateWithLifecycle()
val streams = streamsUiState.allStreams
val addonGroups = streamsUiState.groups
val visibleGroups = streamsUiState.filteredGroups
val contentLabel = if (currentSeason != null && currentEpisode != null) {
buildString {
append(stringResource(Res.string.compose_player_episode_code_full, currentSeason, currentEpisode))
@ -144,61 +134,14 @@ fun PlayerSourcesPanel(
Spacer(Modifier.height(16.dp))
when {
streamsUiState.isAnyLoading -> {
PlayerModalLoading(
modifier = Modifier.padding(vertical = 24.dp),
)
}
streams.isEmpty() -> {
val error = visibleGroups.firstOrNull { it.error != null }?.error
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 24.dp),
contentAlignment = Alignment.CenterStart,
) {
Text(
text = error ?: stringResource(Res.string.compose_player_no_streams_found),
color = Color.White.copy(alpha = if (error == null) 0.7f else 0.85f),
style = MaterialTheme.typography.bodyLarge,
)
}
}
else -> {
val streamKeys = remember(streams) { streams.stablePlayerKeys() }
LazyColumn(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(
start = 8.dp,
top = 14.dp,
end = 8.dp,
bottom = 8.dp,
),
) {
itemsIndexed(
items = streams,
key = { index, _ -> streamKeys[index] },
) { _, stream ->
StreamCard(
stream = stream,
enabled = stream.isSelectableForPlayback(debridSettings.canResolvePlayableLinks),
appendInstantServiceToDefaultName = debridSettings.canResolvePlayableLinks &&
!debridSettings.hasCustomStreamFormatting,
showFileSizeBadges = streamBadgeSettings.showFileSizeBadges,
showAddonLogo = streamBadgeSettings.showAddonLogo,
badgePlacement = streamBadgeSettings.badgePlacement,
isCurrent = stream.isCurrentPlayerStream(currentStreamUrl, currentStreamName),
currentLabel = stringResource(Res.string.compose_player_playing),
onClick = { onStreamSelected(stream) },
)
}
}
}
}
PlayerStreamList(
streamsUiState = streamsUiState,
onStreamSelected = onStreamSelected,
modifier = Modifier.weight(1f),
currentStreamUrl = currentStreamUrl,
currentStreamName = currentStreamName,
currentLabel = stringResource(Res.string.compose_player_playing),
)
}
}
}

View file

@ -0,0 +1,109 @@
package com.nuvio.app.features.player
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.nuvio.app.features.debrid.DebridSettingsRepository
import com.nuvio.app.features.streams.StreamBadgeSettingsRepository
import com.nuvio.app.features.streams.StreamCard
import com.nuvio.app.features.streams.StreamItem
import com.nuvio.app.features.streams.StreamsUiState
import com.nuvio.app.features.streams.isSelectableForPlayback
import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.compose_player_no_streams_found
import org.jetbrains.compose.resources.stringResource
@Composable
internal fun PlayerStreamList(
streamsUiState: StreamsUiState,
onStreamSelected: (StreamItem) -> Unit,
modifier: Modifier = Modifier,
contentPadding: PaddingValues = PaddingValues(
start = 8.dp,
top = 14.dp,
end = 8.dp,
bottom = 8.dp,
),
currentStreamUrl: String? = null,
currentStreamName: String? = null,
currentLabel: String? = null,
) {
val debridSettings by remember {
DebridSettingsRepository.ensureLoaded()
DebridSettingsRepository.uiState
}.collectAsStateWithLifecycle()
val streamBadgeSettings by remember {
StreamBadgeSettingsRepository.ensureLoaded()
StreamBadgeSettingsRepository.uiState
}.collectAsStateWithLifecycle()
val streams = streamsUiState.allStreams
val visibleGroups = streamsUiState.filteredGroups
when {
streams.isEmpty() && streamsUiState.isAnyLoading -> {
PlayerModalLoading(modifier = Modifier.padding(vertical = 24.dp))
}
streams.isEmpty() -> {
val error = visibleGroups.firstOrNull { it.error != null }?.error
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 24.dp),
contentAlignment = Alignment.CenterStart,
) {
Text(
text = error ?: stringResource(Res.string.compose_player_no_streams_found),
color = Color.White.copy(alpha = if (error == null) 0.7f else 0.85f),
style = MaterialTheme.typography.bodyLarge,
)
}
}
else -> {
val streamKeys = remember(streams) { streams.stablePlayerKeys() }
LazyColumn(
modifier = modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = contentPadding,
) {
itemsIndexed(
items = streams,
key = { index, _ -> streamKeys[index] },
) { _, stream ->
StreamCard(
stream = stream,
enabled = stream.isSelectableForPlayback(debridSettings.canResolvePlayableLinks),
appendInstantServiceToDefaultName = debridSettings.canResolvePlayableLinks &&
!debridSettings.hasCustomStreamFormatting,
showFileSizeBadges = streamBadgeSettings.showFileSizeBadges,
showAddonLogo = streamBadgeSettings.showAddonLogo,
badgePlacement = streamBadgeSettings.badgePlacement,
isCurrent = stream.isCurrentPlayerStream(currentStreamUrl, currentStreamName),
currentLabel = currentLabel,
onClick = { onStreamSelected(stream) },
)
}
if (streamsUiState.isAnyLoading) {
item {
PlayerModalLoading(modifier = Modifier.padding(vertical = 16.dp))
}
}
}
}
}
}