fix: resolve debrid instant streams before download/copy link

Long-press menu was blocked for debrid instant streams because
playableDirectUrl is null until resolved. Now the menu opens for
any stream that has a direct URL or is a debrid candidate.

Download and Copy Link actions now resolve the debrid stream URL
via DirectDebridPlaybackResolver before enqueuing/copying, matching
the same flow used by the player.
This commit is contained in:
skoruppa 2026-06-18 13:54:59 +02:00
parent 1951785fdc
commit 8039c32a70

View file

@ -84,6 +84,9 @@ import androidx.compose.material3.rememberModalBottomSheetState
import coil3.compose.AsyncImage
import com.nuvio.app.core.ui.nuvioSafeBottomPadding
import com.nuvio.app.features.debrid.DebridSettingsRepository
import com.nuvio.app.features.debrid.DirectDebridPlayableResult
import com.nuvio.app.features.debrid.DirectDebridPlaybackResolver
import com.nuvio.app.features.debrid.toastMessage
import com.nuvio.app.features.player.PlayerSettingsRepository
import com.nuvio.app.features.watchprogress.WatchProgressRepository
import kotlinx.coroutines.launch
@ -144,6 +147,7 @@ fun StreamsScreen(
val streamLinkCopiedText = stringResource(Res.string.streams_link_copied)
val noDirectStreamLinkText = stringResource(Res.string.streams_no_direct_link)
var streamActionsTarget by remember(videoId) { mutableStateOf<StreamItem?>(null) }
val downloadScope = rememberCoroutineScope()
var preferredFilterApplied by remember(videoId) { mutableStateOf(false) }
var autoPlayOverlayLogoLoadError by remember(logo) { mutableStateOf(false) }
val autoPlayOverlayLogoUrl = logo?.takeIf { it.isNotBlank() }
@ -355,27 +359,88 @@ fun StreamsScreen(
if (!directUrl.isNullOrBlank()) {
clipboardManager.setText(AnnotatedString(directUrl))
NuvioToastController.show(streamLinkCopiedText)
} else if (DirectDebridPlaybackResolver.shouldResolveToPlayableStream(stream)) {
downloadScope.launch {
val resolved = DirectDebridPlaybackResolver.resolveToPlayableStream(
stream = stream,
season = seasonNumber,
episode = episodeNumber,
)
when (resolved) {
is DirectDebridPlayableResult.Success -> {
val resolvedUrl = resolved.stream.playableDirectUrl
if (!resolvedUrl.isNullOrBlank()) {
clipboardManager.setText(AnnotatedString(resolvedUrl))
NuvioToastController.show(streamLinkCopiedText)
} else {
NuvioToastController.show(noDirectStreamLinkText)
}
}
else -> {
val message = resolved.toastMessage()
if (message != null) {
NuvioToastController.show(message)
}
}
}
}
} else {
NuvioToastController.show(noDirectStreamLinkText)
}
},
onDownload = { stream ->
val result = DownloadsRepository.enqueueFromStream(
contentType = type,
videoId = videoId,
parentMetaId = parentMetaId,
parentMetaType = parentMetaType,
title = title,
logo = logo,
poster = poster,
background = background,
seasonNumber = seasonNumber,
episodeNumber = episodeNumber,
episodeTitle = episodeTitle,
episodeThumbnail = episodeThumbnail,
stream = stream,
)
NuvioToastController.show(result.toastMessage())
if (DirectDebridPlaybackResolver.shouldResolveToPlayableStream(stream)) {
downloadScope.launch {
val resolved = DirectDebridPlaybackResolver.resolveToPlayableStream(
stream = stream,
season = seasonNumber,
episode = episodeNumber,
)
when (resolved) {
is DirectDebridPlayableResult.Success -> {
val result = DownloadsRepository.enqueueFromStream(
contentType = type,
videoId = videoId,
parentMetaId = parentMetaId,
parentMetaType = parentMetaType,
title = title,
logo = logo,
poster = poster,
background = background,
seasonNumber = seasonNumber,
episodeNumber = episodeNumber,
episodeTitle = episodeTitle,
episodeThumbnail = episodeThumbnail,
stream = resolved.stream,
)
NuvioToastController.show(result.toastMessage())
}
else -> {
val message = resolved.toastMessage()
if (message != null) {
NuvioToastController.show(message)
}
}
}
}
} else {
val result = DownloadsRepository.enqueueFromStream(
contentType = type,
videoId = videoId,
parentMetaId = parentMetaId,
parentMetaType = parentMetaType,
title = title,
logo = logo,
poster = poster,
background = background,
seasonNumber = seasonNumber,
episodeNumber = episodeNumber,
episodeTitle = episodeTitle,
episodeThumbnail = episodeThumbnail,
stream = stream,
)
NuvioToastController.show(result.toastMessage())
}
},
onOpen = { stream, openExternally ->
onStreamActionOpen(
@ -911,7 +976,7 @@ private fun LazyListScope.streamSection(
}
},
onLongClick = {
if (stream.playableDirectUrl != null) {
if (stream.playableDirectUrl != null || stream.isAddonDebridCandidate) {
onStreamLongPress(stream)
}
},