mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-16 23:12:12 +00:00
Refactor playback launch logic and navigation
Refactor launchPlaybackWithDownloadPreference to use StreamLaunchStore for launching streams and update related navigation logic.
This commit is contained in:
parent
727b48bbc8
commit
5b5a8b6906
1 changed files with 102 additions and 166 deletions
|
|
@ -656,66 +656,7 @@ private fun MainAppContent(
|
||||||
AppDeepLinkRepository.markConsumed(deepLink)
|
AppDeepLinkRepository.markConsumed(deepLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun launchPlaybackWithDownloadPreference(
|
null -> Unit
|
||||||
type: String,
|
|
||||||
videoId: String,
|
|
||||||
parentMetaId: String,
|
|
||||||
parentMetaType: String,
|
|
||||||
title: String,
|
|
||||||
logo: String?,
|
|
||||||
poster: String?,
|
|
||||||
background: String?,
|
|
||||||
seasonNumber: Int?,
|
|
||||||
episodeNumber: Int?,
|
|
||||||
episodeTitle: String?,
|
|
||||||
episodeThumbnail: String?,
|
|
||||||
pauseDescription: String?,
|
|
||||||
resumePositionMs: Long?,
|
|
||||||
resumeProgressFraction: Float?,
|
|
||||||
manualSelection: Boolean,
|
|
||||||
startFromBeginning: Boolean,
|
|
||||||
) {
|
|
||||||
val targetResumePositionMs = if (startFromBeginning) 0L else (resumePositionMs ?: 0L)
|
|
||||||
val targetResumeProgressFraction = if (startFromBeginning) null else resumeProgressFraction
|
|
||||||
|
|
||||||
if (!manualSelection) {
|
|
||||||
val downloadedItem = DownloadsRepository.findPlayableDownload(
|
|
||||||
parentMetaId = parentMetaId,
|
|
||||||
seasonNumber = seasonNumber,
|
|
||||||
episodeNumber = episodeNumber,
|
|
||||||
videoId = videoId,
|
|
||||||
)
|
|
||||||
val localSourceUrl = downloadedItem?.localFileUri
|
|
||||||
if (!localSourceUrl.isNullOrBlank()) {
|
|
||||||
val launchId = PlayerLaunchStore.put(
|
|
||||||
PlayerLaunch(
|
|
||||||
title = title,
|
|
||||||
sourceUrl = localSourceUrl,
|
|
||||||
sourceHeaders = emptyMap(),
|
|
||||||
sourceResponseHeaders = emptyMap(),
|
|
||||||
logo = logo,
|
|
||||||
poster = poster,
|
|
||||||
background = background,
|
|
||||||
seasonNumber = seasonNumber,
|
|
||||||
episodeNumber = episodeNumber,
|
|
||||||
episodeTitle = episodeTitle,
|
|
||||||
episodeThumbnail = episodeThumbnail,
|
|
||||||
streamTitle = downloadedItem.streamTitle.ifBlank { title },
|
|
||||||
streamSubtitle = downloadedItem.streamSubtitle,
|
|
||||||
pauseDescription = pauseDescription,
|
|
||||||
providerName = downloadedItem.providerName.ifBlank { downloadedProviderLabel },
|
|
||||||
providerAddonId = downloadedItem.providerAddonId,
|
|
||||||
contentType = type,
|
|
||||||
videoId = videoId,
|
|
||||||
parentMetaId = parentMetaId,
|
|
||||||
parentMetaType = parentMetaType,
|
|
||||||
initialPositionMs = targetResumePositionMs,
|
|
||||||
initialProgressFraction = targetResumeProgressFraction,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
navController.navigate(PlayerRoute(launchId = launchId))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -767,7 +708,7 @@ private fun MainAppContent(
|
||||||
streamTitle = downloadedItem.streamTitle.ifBlank { title },
|
streamTitle = downloadedItem.streamTitle.ifBlank { title },
|
||||||
streamSubtitle = downloadedItem.streamSubtitle,
|
streamSubtitle = downloadedItem.streamSubtitle,
|
||||||
pauseDescription = pauseDescription,
|
pauseDescription = pauseDescription,
|
||||||
providerName = downloadedItem.providerName.ifBlank { "Downloaded" },
|
providerName = downloadedItem.providerName.ifBlank { downloadedProviderLabel },
|
||||||
providerAddonId = downloadedItem.providerAddonId,
|
providerAddonId = downloadedItem.providerAddonId,
|
||||||
contentType = type,
|
contentType = type,
|
||||||
videoId = videoId,
|
videoId = videoId,
|
||||||
|
|
@ -782,22 +723,52 @@ private fun MainAppContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val librarySectionSubtitle = if (libraryUiState.sourceMode == LibrarySourceMode.TRAKT) {
|
val streamLaunchId = StreamLaunchStore.put(
|
||||||
stringResource(Res.string.compose_catalog_subtitle_trakt_library)
|
StreamLaunch(
|
||||||
} else {
|
type = type,
|
||||||
stringResource(Res.string.compose_catalog_subtitle_library)
|
videoId = videoId,
|
||||||
}
|
parentMetaId = parentMetaId,
|
||||||
|
parentMetaType = parentMetaType,
|
||||||
|
title = title,
|
||||||
|
logo = logo,
|
||||||
|
poster = poster,
|
||||||
|
background = background,
|
||||||
|
seasonNumber = seasonNumber,
|
||||||
|
episodeNumber = episodeNumber,
|
||||||
|
episodeTitle = episodeTitle,
|
||||||
|
episodeThumbnail = episodeThumbnail,
|
||||||
|
pauseDescription = pauseDescription,
|
||||||
|
resumePositionMs = if (startFromBeginning) 0L else resumePositionMs,
|
||||||
|
resumeProgressFraction = targetResumeProgressFraction,
|
||||||
|
manualSelection = manualSelection,
|
||||||
|
startFromBeginning = startFromBeginning,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
navController.navigate(
|
||||||
|
StreamRoute(launchId = streamLaunchId),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val onLibrarySectionViewAllClick: (LibrarySection) -> Unit = { section ->
|
val onPlay: (String, String, String, String, String, String?, String?, String?, Int?, Int?, String?, String?, String?, Long?) -> Unit =
|
||||||
navController.navigate(
|
{ type, videoId, parentMetaId, parentMetaType, title, logo, poster, background, seasonNumber, episodeNumber, episodeTitle, episodeThumbnail, pauseDescription, resumePositionMs ->
|
||||||
CatalogRoute(
|
launchPlaybackWithDownloadPreference(
|
||||||
title = section.displayTitle,
|
type = type,
|
||||||
subtitle = librarySectionSubtitle,
|
videoId = videoId,
|
||||||
manifestUrl = INTERNAL_LIBRARY_MANIFEST_URL,
|
parentMetaId = parentMetaId,
|
||||||
type = section.items.firstOrNull()?.type ?: "movie",
|
parentMetaType = parentMetaType,
|
||||||
catalogId = section.type,
|
title = title,
|
||||||
supportsPagination = false,
|
logo = logo,
|
||||||
),
|
poster = poster,
|
||||||
|
background = background,
|
||||||
|
seasonNumber = seasonNumber,
|
||||||
|
episodeNumber = episodeNumber,
|
||||||
|
episodeTitle = episodeTitle,
|
||||||
|
episodeThumbnail = episodeThumbnail,
|
||||||
|
pauseDescription = pauseDescription,
|
||||||
|
resumePositionMs = resumePositionMs,
|
||||||
|
resumeProgressFraction = null,
|
||||||
|
manualSelection = false,
|
||||||
|
startFromBeginning = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -837,15 +808,17 @@ private fun MainAppContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val librarySectionSubtitle = if (libraryUiState.sourceMode == LibrarySourceMode.TRAKT) {
|
||||||
|
stringResource(Res.string.compose_catalog_subtitle_trakt_library)
|
||||||
|
} else {
|
||||||
|
stringResource(Res.string.compose_catalog_subtitle_library)
|
||||||
|
}
|
||||||
|
|
||||||
val onLibrarySectionViewAllClick: (LibrarySection) -> Unit = { section ->
|
val onLibrarySectionViewAllClick: (LibrarySection) -> Unit = { section ->
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
CatalogRoute(
|
CatalogRoute(
|
||||||
title = section.displayTitle,
|
title = section.displayTitle,
|
||||||
subtitle = if (libraryUiState.sourceMode == LibrarySourceMode.TRAKT) {
|
subtitle = librarySectionSubtitle,
|
||||||
"Trakt Library"
|
|
||||||
} else {
|
|
||||||
"Library"
|
|
||||||
},
|
|
||||||
manifestUrl = INTERNAL_LIBRARY_MANIFEST_URL,
|
manifestUrl = INTERNAL_LIBRARY_MANIFEST_URL,
|
||||||
type = section.items.firstOrNull()?.type ?: "movie",
|
type = section.items.firstOrNull()?.type ?: "movie",
|
||||||
catalogId = section.type,
|
catalogId = section.type,
|
||||||
|
|
@ -986,7 +959,7 @@ private fun MainAppContent(
|
||||||
},
|
},
|
||||||
onLibraryPosterLongClick = { item ->
|
onLibraryPosterLongClick = { item ->
|
||||||
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
|
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
selectedPosterForActions = item.toMetaPreview() // reuse existing sheet
|
selectedPosterForActions = item.toMetaPreview()
|
||||||
},
|
},
|
||||||
onLibrarySectionViewAllClick = onLibrarySectionViewAllClick,
|
onLibrarySectionViewAllClick = onLibrarySectionViewAllClick,
|
||||||
onContinueWatchingClick = onContinueWatchingClick,
|
onContinueWatchingClick = onContinueWatchingClick,
|
||||||
|
|
@ -1080,11 +1053,11 @@ private fun MainAppContent(
|
||||||
castAvatarTransitionKey = avatarTransitionKey,
|
castAvatarTransitionKey = avatarTransitionKey,
|
||||||
preferCrew = person.role?.let {
|
preferCrew = person.role?.let {
|
||||||
it.equals("Director", ignoreCase = true) ||
|
it.equals("Director", ignoreCase = true) ||
|
||||||
it.equals(directorRole, ignoreCase = true) ||
|
it.equals(directorRole, ignoreCase = true) ||
|
||||||
it.equals("Writer", ignoreCase = true) ||
|
it.equals("Writer", ignoreCase = true) ||
|
||||||
it.equals(writerRole, ignoreCase = true) ||
|
it.equals(writerRole, ignoreCase = true) ||
|
||||||
it.equals("Creator", ignoreCase = true)
|
it.equals("Creator", ignoreCase = true)
|
||||||
|| it.equals(creatorRole, ignoreCase = true)
|
|| it.equals(creatorRole, ignoreCase = true)
|
||||||
} ?: false,
|
} ?: false,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -1689,34 +1662,15 @@ private fun MainAppContent(
|
||||||
)
|
)
|
||||||
} == true,
|
} == true,
|
||||||
onDismiss = { selectedPosterForActions = null },
|
onDismiss = { selectedPosterForActions = null },
|
||||||
|
// TAP → direct save/remove (watchlist or local library)
|
||||||
onToggleLibrary = {
|
onToggleLibrary = {
|
||||||
selectedPosterForActions?.let { preview ->
|
selectedPosterForActions?.let { preview ->
|
||||||
val libraryItem = preview.toLibraryItem(savedAtEpochMs = 0L)
|
val libraryItem = preview.toLibraryItem(savedAtEpochMs = 0L)
|
||||||
if (!isTraktConnected) {
|
LibraryRepository.toggleSaved(libraryItem)
|
||||||
LibraryRepository.toggleSaved(libraryItem)
|
|
||||||
} else {
|
|
||||||
// Tap = directly toggle watchlist (first tab)
|
|
||||||
coroutineScope.launch {
|
|
||||||
runCatching {
|
|
||||||
val snapshot = LibraryRepository.getMembershipSnapshot(libraryItem)
|
|
||||||
val tabs = LibraryRepository.traktListTabs()
|
|
||||||
val watchlistKey = tabs.firstOrNull()?.key
|
|
||||||
if (watchlistKey != null) {
|
|
||||||
val currentlyIn = snapshot[watchlistKey] == true
|
|
||||||
val desired = tabs.associate { tab ->
|
|
||||||
tab.key to if (tab.key == watchlistKey) !currentlyIn else (snapshot[tab.key] == true)
|
|
||||||
}
|
|
||||||
}.onFailure { error ->
|
|
||||||
pickerError = error.message ?: getString(Res.string.trakt_lists_load_failed)
|
|
||||||
}
|
|
||||||
}.onFailure {
|
|
||||||
LibraryRepository.toggleSaved(libraryItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onOpenListPicker = if (isTraktConnected) {
|
// LONG PRESS → open Trakt list picker (only when Trakt is connected)
|
||||||
|
onToggleLibraryLongClick = if (isTraktConnected) {
|
||||||
{
|
{
|
||||||
selectedPosterForActions?.let { preview ->
|
selectedPosterForActions?.let { preview ->
|
||||||
val libraryItem = preview.toLibraryItem(savedAtEpochMs = 0L)
|
val libraryItem = preview.toLibraryItem(savedAtEpochMs = 0L)
|
||||||
|
|
@ -1736,7 +1690,7 @@ private fun MainAppContent(
|
||||||
tab.key to (snapshot[tab.key] == true)
|
tab.key to (snapshot[tab.key] == true)
|
||||||
}
|
}
|
||||||
}.onFailure { error ->
|
}.onFailure { error ->
|
||||||
pickerError = error.message ?: "Failed to load Trakt lists"
|
pickerError = error.message ?: getString(Res.string.trakt_lists_load_failed)
|
||||||
}
|
}
|
||||||
pickerPending = false
|
pickerPending = false
|
||||||
}
|
}
|
||||||
|
|
@ -1822,45 +1776,27 @@ private fun MainAppContent(
|
||||||
pickerItem = null
|
pickerItem = null
|
||||||
pickerError = null
|
pickerError = null
|
||||||
}.onFailure { error ->
|
}.onFailure { error ->
|
||||||
pickerError = error.message ?: "Failed to update Trakt lists"
|
pickerError = error.message ?: getString(Res.string.trakt_lists_update_failed)
|
||||||
}
|
}
|
||||||
},
|
pickerPending = false
|
||||||
onSave = {
|
}
|
||||||
val item = pickerItem ?: return@TraktListPickerDialog
|
},
|
||||||
coroutineScope.launch {
|
)
|
||||||
pickerPending = true
|
|
||||||
pickerError = null
|
|
||||||
runCatching {
|
|
||||||
LibraryRepository.applyMembershipChanges(
|
|
||||||
item = item,
|
|
||||||
desiredMembership = pickerMembership,
|
|
||||||
)
|
|
||||||
}.onSuccess {
|
|
||||||
showLibraryListPicker = false
|
|
||||||
pickerItem = null
|
|
||||||
pickerError = null
|
|
||||||
}.onFailure { error ->
|
|
||||||
pickerError = error.message ?: getString(Res.string.trakt_lists_update_failed)
|
|
||||||
}
|
|
||||||
pickerPending = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
NuvioStatusModal(
|
NuvioStatusModal(
|
||||||
title = stringResource(Res.string.app_exit_title),
|
title = stringResource(Res.string.app_exit_title),
|
||||||
message = stringResource(Res.string.app_exit_message),
|
message = stringResource(Res.string.app_exit_message),
|
||||||
isVisible = showExitConfirmation,
|
isVisible = showExitConfirmation,
|
||||||
confirmText = stringResource(Res.string.action_yes),
|
confirmText = stringResource(Res.string.action_yes),
|
||||||
dismissText = stringResource(Res.string.action_no),
|
dismissText = stringResource(Res.string.action_no),
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
showExitConfirmation = false
|
showExitConfirmation = false
|
||||||
platformExitApp()
|
platformExitApp()
|
||||||
},
|
},
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
showExitConfirmation = false
|
showExitConfirmation = false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
androidx.compose.animation.AnimatedVisibility(
|
androidx.compose.animation.AnimatedVisibility(
|
||||||
visible = !initialHomeReady || profileSwitchLoading,
|
visible = !initialHomeReady || profileSwitchLoading,
|
||||||
|
|
@ -1879,23 +1815,23 @@ private fun MainAppContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NuvioFloatingPrompt(
|
NuvioFloatingPrompt(
|
||||||
visible = resumePromptItem != null,
|
visible = resumePromptItem != null,
|
||||||
imageUrl = resumePromptItem?.poster ?: resumePromptItem?.imageUrl,
|
imageUrl = resumePromptItem?.poster ?: resumePromptItem?.imageUrl,
|
||||||
title = resumePromptItem?.title.orEmpty(),
|
title = resumePromptItem?.title.orEmpty(),
|
||||||
subtitle = resumePromptItem?.let { localizedContinueWatchingSubtitle(it) }.orEmpty(),
|
subtitle = resumePromptItem?.let { localizedContinueWatchingSubtitle(it) }.orEmpty(),
|
||||||
progressFraction = resumePromptItem?.progressFraction ?: 0f,
|
progressFraction = resumePromptItem?.progressFraction ?: 0f,
|
||||||
actionLabel = stringResource(Res.string.resume_prompt_action),
|
actionLabel = stringResource(Res.string.resume_prompt_action),
|
||||||
onAction = {
|
onAction = {
|
||||||
val item = resumePromptItem ?: return@NuvioFloatingPrompt
|
val item = resumePromptItem ?: return@NuvioFloatingPrompt
|
||||||
resumePromptItem = null
|
resumePromptItem = null
|
||||||
openContinueWatching(item, false, false)
|
openContinueWatching(item, false, false)
|
||||||
},
|
},
|
||||||
onDismiss = { resumePromptItem = null },
|
onDismiss = { resumePromptItem = null },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomCenter)
|
.align(Alignment.BottomCenter)
|
||||||
.zIndex(15f),
|
.zIndex(15f),
|
||||||
)
|
)
|
||||||
|
|
||||||
NuvioToastHost(
|
NuvioToastHost(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -1940,6 +1876,7 @@ private fun AppTabHost(
|
||||||
onPosterClick: ((MetaPreview) -> Unit)? = null,
|
onPosterClick: ((MetaPreview) -> Unit)? = null,
|
||||||
onPosterLongClick: ((MetaPreview) -> Unit)? = null,
|
onPosterLongClick: ((MetaPreview) -> Unit)? = null,
|
||||||
onLibraryPosterClick: ((LibraryItem) -> Unit)? = null,
|
onLibraryPosterClick: ((LibraryItem) -> Unit)? = null,
|
||||||
|
onLibraryPosterLongClick: ((LibraryItem) -> Unit)? = null,
|
||||||
onLibrarySectionViewAllClick: ((LibrarySection) -> Unit)? = null,
|
onLibrarySectionViewAllClick: ((LibrarySection) -> Unit)? = null,
|
||||||
onContinueWatchingClick: ((ContinueWatchingItem) -> Unit)? = null,
|
onContinueWatchingClick: ((ContinueWatchingItem) -> Unit)? = null,
|
||||||
onContinueWatchingLongPress: ((ContinueWatchingItem) -> Unit)? = null,
|
onContinueWatchingLongPress: ((ContinueWatchingItem) -> Unit)? = null,
|
||||||
|
|
@ -1956,7 +1893,6 @@ private fun AppTabHost(
|
||||||
onCollectionsSettingsClick: () -> Unit = {},
|
onCollectionsSettingsClick: () -> Unit = {},
|
||||||
onFolderClick: ((collectionId: String, folderId: String) -> Unit)? = null,
|
onFolderClick: ((collectionId: String, folderId: String) -> Unit)? = null,
|
||||||
onInitialHomeContentRendered: () -> Unit = {},
|
onInitialHomeContentRendered: () -> Unit = {},
|
||||||
onLibraryPosterLongClick: ((LibraryItem) -> Unit)? = null,
|
|
||||||
) {
|
) {
|
||||||
val tabStateHolder = rememberSaveableStateHolder()
|
val tabStateHolder = rememberSaveableStateHolder()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue