flavouring yt extraction

This commit is contained in:
tapframe 2026-04-04 21:30:19 +05:30
parent 49a178c7f9
commit f65f934acd
20 changed files with 225 additions and 84 deletions

View file

@ -210,6 +210,15 @@ android {
versionCode = 1
versionName = "1.0"
}
flavorDimensions += "distribution"
productFlavors {
create("full") {
dimension = "distribution"
}
create("playstore") {
dimension = "distribution"
}
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"

View file

@ -0,0 +1,9 @@
package com.nuvio.app.features.player
import androidx.media3.datasource.DataSource
import com.nuvio.app.features.trailer.YoutubeChunkedDataSourceFactory
internal object PlatformPlaybackDataSourceFactory {
fun create(defaultRequestHeaders: Map<String, String>): DataSource.Factory =
YoutubeChunkedDataSourceFactory(defaultRequestHeaders = defaultRequestHeaders)
}

View file

@ -41,7 +41,6 @@ import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView
import androidx.media3.ui.SubtitleView
import androidx.media3.ui.CaptionStyleCompat
import com.nuvio.app.features.trailer.YoutubeChunkedDataSourceFactory
import kotlinx.coroutines.delay
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -112,7 +111,7 @@ actual fun PlatformPlayerSurface(
.setTsExtractorTimestampSearchBytes(1500 * TsExtractor.TS_PACKET_SIZE)
val mediaSourceFactory = DefaultMediaSourceFactory(
YoutubeChunkedDataSourceFactory(defaultRequestHeaders = sanitizedSourceHeaders),
PlatformPlaybackDataSourceFactory.create(defaultRequestHeaders = sanitizedSourceHeaders),
extractorsFactory,
)

View file

@ -0,0 +1,9 @@
package com.nuvio.app.features.player
import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DefaultHttpDataSource
internal object PlatformPlaybackDataSourceFactory {
fun create(defaultRequestHeaders: Map<String, String>): DataSource.Factory =
DefaultHttpDataSource.Factory().setDefaultRequestProperties(defaultRequestHeaders)
}

View file

@ -0,0 +1,5 @@
package com.nuvio.app.features.trailer
actual object TrailerPlaybackResolver {
actual suspend fun resolveFromYouTubeUrl(youtubeUrl: String): TrailerPlaybackSource? = null
}

View file

@ -66,6 +66,7 @@ import coil3.ImageLoader
import coil3.compose.setSingletonImageLoaderFactory
import coil3.request.CachePolicy
import coil3.request.crossfade
import com.nuvio.app.core.build.AppFeaturePolicy
import com.nuvio.app.core.auth.AuthRepository
import com.nuvio.app.core.auth.AuthState
import com.nuvio.app.core.deeplink.AppDeepLink
@ -609,7 +610,11 @@ private fun MainAppContent(
onMetaScreenSettingsClick = { navController.navigate(MetaScreenSettingsRoute) },
onContinueWatchingSettingsClick = { navController.navigate(ContinueWatchingSettingsRoute) },
onAddonsSettingsClick = { navController.navigate(AddonsSettingsRoute) },
onPluginsSettingsClick = { navController.navigate(PluginsSettingsRoute) },
onPluginsSettingsClick = {
if (AppFeaturePolicy.pluginsEnabled) {
navController.navigate(PluginsSettingsRoute)
}
},
onAccountSettingsClick = { navController.navigate(AccountSettingsRoute) },
onInitialHomeContentRendered = { initialHomeReady = true },
)
@ -1084,10 +1089,12 @@ private fun MainAppContent(
onBack = { navController.popBackStack() },
)
}
composable<PluginsSettingsRoute> {
PluginsSettingsScreen(
onBack = { navController.popBackStack() },
)
if (AppFeaturePolicy.pluginsEnabled) {
composable<PluginsSettingsRoute> {
PluginsSettingsScreen(
onBack = { navController.popBackStack() },
)
}
}
composable<AccountSettingsRoute> {
AccountSettingsScreen(

View file

@ -1,5 +1,6 @@
package com.nuvio.app.core.storage
import com.nuvio.app.core.build.AppFeaturePolicy
import com.nuvio.app.features.addons.AddonRepository
import com.nuvio.app.features.catalog.CatalogRepository
import com.nuvio.app.features.details.MetaDetailsRepository
@ -28,7 +29,9 @@ internal object LocalAccountDataCleaner {
ProfileRepository.clearInMemory()
AddonRepository.clearLocalState()
PluginRepository.clearLocalState()
if (AppFeaturePolicy.pluginsEnabled) {
PluginRepository.clearLocalState()
}
HomeRepository.clear()
HomeCatalogSettingsRepository.clearLocalState()
MetaScreenSettingsRepository.clearLocalState()

View file

@ -1,6 +1,7 @@
package com.nuvio.app.core.sync
import co.touchlab.kermit.Logger
import com.nuvio.app.core.build.AppFeaturePolicy
import com.nuvio.app.core.auth.AuthRepository
import com.nuvio.app.core.auth.AuthState
import com.nuvio.app.features.addons.AddonRepository
@ -31,10 +32,12 @@ object SyncManager {
.onSuccess { log.i { "pullAllForProfile — addons pull completed" } }
.onFailure { log.e(it) { "Addon pull failed" } }
log.i { "pullAllForProfile — pulling plugins (await)..." }
runCatching { PluginRepository.pullFromServer(profileId) }
.onSuccess { log.i { "pullAllForProfile — plugins pull completed" } }
.onFailure { log.e(it) { "Plugin pull failed" } }
if (AppFeaturePolicy.pluginsEnabled) {
log.i { "pullAllForProfile — pulling plugins (await)..." }
runCatching { PluginRepository.pullFromServer(profileId) }
.onSuccess { log.i { "pullAllForProfile — plugins pull completed" } }
.onFailure { log.e(it) { "Plugin pull failed" } }
}
log.i { "pullAllForProfile — launching remaining pulls in parallel" }
launch {

View file

@ -44,12 +44,15 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil3.compose.AsyncImage
import com.nuvio.app.core.build.AppFeaturePolicy
import com.nuvio.app.core.build.TrailerPlaybackMode
import com.nuvio.app.core.ui.NuvioBackButton
import com.nuvio.app.core.ui.TraktListPickerDialog
import com.nuvio.app.core.ui.nuvioPlatformExtraBottomPadding
@ -295,37 +298,43 @@ fun MetaDetailsScreen(
val hasTrailersSection = remember(meta) {
meta.trailers.isNotEmpty()
}
val uriHandler = LocalUriHandler.current
val inAppTrailerPlaybackEnabled = AppFeaturePolicy.trailerPlaybackMode == TrailerPlaybackMode.IN_APP
val trailerScope = rememberCoroutineScope()
var selectedTrailer by remember(meta.id) { mutableStateOf<MetaTrailer?>(null) }
var trailerPlaybackSource by remember(meta.id) { mutableStateOf<TrailerPlaybackSource?>(null) }
var trailerLoading by remember(meta.id) { mutableStateOf(false) }
var trailerErrorMessage by remember(meta.id) { mutableStateOf<String?>(null) }
var trailerRequestToken by remember(meta.id) { mutableIntStateOf(0) }
val resolveTrailer: (MetaTrailer) -> Unit = remember(meta.id) {
val resolveTrailer: (MetaTrailer) -> Unit = remember(meta.id, inAppTrailerPlaybackEnabled, uriHandler) {
{ trailer ->
selectedTrailer = trailer
trailerPlaybackSource = null
trailerErrorMessage = null
trailerLoading = true
trailerRequestToken += 1
val currentRequestToken = trailerRequestToken
trailerScope.launch {
val youtubeUrl = trailer.key.takeIf {
it.startsWith("http://") || it.startsWith("https://")
} ?: "https://www.youtube.com/watch?v=${trailer.key}"
val resolvedSource = runCatching {
TrailerPlaybackResolver.resolveFromYouTubeUrl(youtubeUrl)
}.getOrNull()
if (currentRequestToken != trailerRequestToken) {
return@launch
val youtubeUrl = trailer.key.takeIf {
it.startsWith("http://") || it.startsWith("https://")
} ?: "https://www.youtube.com/watch?v=${trailer.key}"
if (!inAppTrailerPlaybackEnabled) {
runCatching { uriHandler.openUri(youtubeUrl) }
} else {
selectedTrailer = trailer
trailerPlaybackSource = null
trailerErrorMessage = null
trailerLoading = true
trailerRequestToken += 1
val currentRequestToken = trailerRequestToken
trailerScope.launch {
val resolvedSource = runCatching {
TrailerPlaybackResolver.resolveFromYouTubeUrl(youtubeUrl)
}.getOrNull()
if (currentRequestToken != trailerRequestToken) {
return@launch
}
trailerPlaybackSource = resolvedSource
trailerErrorMessage = if (resolvedSource == null) {
"No playable trailer stream found."
} else {
null
}
trailerLoading = false
}
trailerPlaybackSource = resolvedSource
trailerErrorMessage = if (resolvedSource == null) {
"No playable trailer stream found."
} else {
null
}
trailerLoading = false
}
}
}
@ -654,25 +663,27 @@ fun MetaDetailsScreen(
)
}
TrailerPlayerPopup(
visible = selectedTrailer != null,
trailerTitle = selectedTrailer?.displayName ?: selectedTrailer?.name.orEmpty(),
trailerType = selectedTrailer?.type.orEmpty(),
contentTitle = meta.name,
playbackSource = trailerPlaybackSource,
isLoading = trailerLoading,
errorMessage = trailerErrorMessage,
onDismiss = {
trailerRequestToken += 1
trailerLoading = false
trailerPlaybackSource = null
trailerErrorMessage = null
selectedTrailer = null
},
onRetry = selectedTrailer?.let { trailer ->
{ resolveTrailer(trailer) }
},
)
if (inAppTrailerPlaybackEnabled) {
TrailerPlayerPopup(
visible = selectedTrailer != null,
trailerTitle = selectedTrailer?.displayName ?: selectedTrailer?.name.orEmpty(),
trailerType = selectedTrailer?.type.orEmpty(),
contentTitle = meta.name,
playbackSource = trailerPlaybackSource,
isLoading = trailerLoading,
errorMessage = trailerErrorMessage,
onDismiss = {
trailerRequestToken += 1
trailerLoading = false
trailerPlaybackSource = null
trailerErrorMessage = null
selectedTrailer = null
},
onRetry = selectedTrailer?.let { trailer ->
{ resolveTrailer(trailer) }
},
)
}
TraktListPickerDialog(
visible = showLibraryListPicker,

View file

@ -1,5 +1,6 @@
package com.nuvio.app.features.player
import com.nuvio.app.core.build.AppFeaturePolicy
import com.nuvio.app.features.player.skip.NextEpisodeThresholdMode
import com.nuvio.app.features.streams.StreamAutoPlayMode
import com.nuvio.app.features.streams.StreamAutoPlaySource
@ -141,6 +142,17 @@ object PlayerSettingsRepository {
?: StreamAutoPlaySource.ALL_SOURCES
streamAutoPlaySelectedAddons = PlayerSettingsStorage.loadStreamAutoPlaySelectedAddons() ?: emptySet()
streamAutoPlaySelectedPlugins = PlayerSettingsStorage.loadStreamAutoPlaySelectedPlugins() ?: emptySet()
if (!AppFeaturePolicy.pluginsEnabled) {
val normalizedSource = normalizeStreamAutoPlaySource(streamAutoPlaySource)
if (normalizedSource != streamAutoPlaySource) {
streamAutoPlaySource = normalizedSource
PlayerSettingsStorage.saveStreamAutoPlaySource(normalizedSource.name)
}
if (streamAutoPlaySelectedPlugins.isNotEmpty()) {
streamAutoPlaySelectedPlugins = emptySet()
PlayerSettingsStorage.saveStreamAutoPlaySelectedPlugins(emptySet())
}
}
streamAutoPlayRegex = PlayerSettingsStorage.loadStreamAutoPlayRegex() ?: ""
streamAutoPlayTimeoutSeconds = PlayerSettingsStorage.loadStreamAutoPlayTimeoutSeconds() ?: 3
skipIntroEnabled = PlayerSettingsStorage.loadSkipIntroEnabled() ?: true
@ -261,10 +273,11 @@ object PlayerSettingsRepository {
fun setStreamAutoPlaySource(source: StreamAutoPlaySource) {
ensureLoaded()
if (streamAutoPlaySource == source) return
streamAutoPlaySource = source
val normalizedSource = normalizeStreamAutoPlaySource(source)
if (streamAutoPlaySource == normalizedSource) return
streamAutoPlaySource = normalizedSource
publish()
PlayerSettingsStorage.saveStreamAutoPlaySource(source.name)
PlayerSettingsStorage.saveStreamAutoPlaySource(normalizedSource.name)
}
fun setStreamAutoPlaySelectedAddons(addons: Set<String>) {
@ -277,10 +290,11 @@ object PlayerSettingsRepository {
fun setStreamAutoPlaySelectedPlugins(plugins: Set<String>) {
ensureLoaded()
if (streamAutoPlaySelectedPlugins == plugins) return
streamAutoPlaySelectedPlugins = plugins
val normalizedPlugins = if (AppFeaturePolicy.pluginsEnabled) plugins else emptySet()
if (streamAutoPlaySelectedPlugins == normalizedPlugins) return
streamAutoPlaySelectedPlugins = normalizedPlugins
publish()
PlayerSettingsStorage.saveStreamAutoPlaySelectedPlugins(plugins)
PlayerSettingsStorage.saveStreamAutoPlaySelectedPlugins(normalizedPlugins)
}
fun setStreamAutoPlayRegex(regex: String) {
@ -392,4 +406,12 @@ object PlayerSettingsRepository {
nextEpisodeThresholdMinutesBeforeEnd = nextEpisodeThresholdMinutesBeforeEnd,
)
}
private fun normalizeStreamAutoPlaySource(source: StreamAutoPlaySource): StreamAutoPlaySource {
return if (!AppFeaturePolicy.pluginsEnabled && source == StreamAutoPlaySource.ENABLED_PLUGINS_ONLY) {
StreamAutoPlaySource.ALL_SOURCES
} else {
source
}
}
}

View file

@ -1,6 +1,7 @@
package com.nuvio.app.features.player
import co.touchlab.kermit.Logger
import com.nuvio.app.core.build.AppFeaturePolicy
import com.nuvio.app.features.addons.AddonRepository
import com.nuvio.app.features.addons.httpGetText
import com.nuvio.app.features.details.MetaDetailsRepository
@ -149,8 +150,12 @@ object PlayerStreamsRepository {
}
val installedAddons = AddonRepository.uiState.value.addons
PluginRepository.initialize()
val pluginScrapers = PluginRepository.getEnabledScrapersForType(type)
val pluginScrapers = if (AppFeaturePolicy.pluginsEnabled) {
PluginRepository.initialize()
PluginRepository.getEnabledScrapersForType(type)
} else {
emptyList()
}
if (installedAddons.isEmpty() && pluginScrapers.isEmpty()) {
stateFlow.value = StreamsUiState(

View file

@ -132,7 +132,9 @@ object ProfileRepository {
LibraryRepository.onProfileChanged(profileIndex)
WatchProgressRepository.onProfileChanged(profileIndex)
AddonRepository.onProfileChanged(profileIndex)
PluginRepository.onProfileChanged(profileIndex)
if (com.nuvio.app.core.build.AppFeaturePolicy.pluginsEnabled) {
PluginRepository.onProfileChanged(profileIndex)
}
ThemeSettingsRepository.onProfileChanged()
PlayerSettingsRepository.onProfileChanged()
HomeCatalogSettingsRepository.onProfileChanged()

View file

@ -8,6 +8,7 @@ import androidx.compose.material.icons.rounded.Tune
internal fun LazyListScope.contentDiscoveryContent(
isTablet: Boolean,
showPluginsEntry: Boolean,
onAddonsClick: () -> Unit,
onPluginsClick: () -> Unit,
onHomescreenClick: () -> Unit,
@ -26,13 +27,15 @@ internal fun LazyListScope.contentDiscoveryContent(
isTablet = isTablet,
onClick = onAddonsClick,
)
SettingsNavigationRow(
title = "Plugins",
description = "Install JavaScript scraper repositories and test providers internally.",
icon = Icons.Rounded.Hub,
isTablet = isTablet,
onClick = onPluginsClick,
)
if (showPluginsEntry) {
SettingsNavigationRow(
title = "Plugins",
description = "Install JavaScript scraper repositories and test providers internally.",
icon = Icons.Rounded.Hub,
isTablet = isTablet,
onClick = onPluginsClick,
)
}
}
}
}

View file

@ -1,5 +1,6 @@
package com.nuvio.app.features.settings
import com.nuvio.app.core.build.AppFeaturePolicy
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@ -51,6 +52,7 @@ import com.nuvio.app.features.player.AvailableLanguageOptions
import com.nuvio.app.features.player.PlayerSettingsRepository
import com.nuvio.app.features.player.SubtitleLanguageOption
import com.nuvio.app.features.player.languageLabelForCode
import com.nuvio.app.features.plugins.PluginsUiState
import com.nuvio.app.features.plugins.PluginRepository
import com.nuvio.app.features.streams.StreamAutoPlayMode
import com.nuvio.app.features.streams.StreamAutoPlaySource
@ -111,9 +113,15 @@ private fun PlaybackSettingsSection(
var showAutoPlayAddonSelectionDialog by remember { mutableStateOf(false) }
var showAutoPlayPluginSelectionDialog by remember { mutableStateOf(false) }
var showAutoPlayRegexDialog by remember { mutableStateOf(false) }
val pluginsEnabled = AppFeaturePolicy.pluginsEnabled
val autoPlayPlayerSettings by PlayerSettingsRepository.uiState.collectAsStateWithLifecycle()
val addonUiState by AddonRepository.uiState.collectAsStateWithLifecycle()
val pluginUiState by PluginRepository.uiState.collectAsStateWithLifecycle()
val pluginUiState = if (pluginsEnabled) {
val state by PluginRepository.uiState.collectAsStateWithLifecycle()
state
} else {
PluginsUiState(pluginsEnabled = false)
}
val hapticFeedback = LocalHapticFeedback.current
val sectionSpacing = if (isTablet) 18.dp else 12.dp
@ -292,7 +300,7 @@ private fun PlaybackSettingsSection(
SettingsNavigationRow(
title = "Source Scope",
description = when (autoPlayPlayerSettings.streamAutoPlaySource) {
StreamAutoPlaySource.ALL_SOURCES -> "All Sources"
StreamAutoPlaySource.ALL_SOURCES -> if (pluginsEnabled) "All Sources" else "All Addons"
StreamAutoPlaySource.INSTALLED_ADDONS_ONLY -> "Installed Addons Only"
StreamAutoPlaySource.ENABLED_PLUGINS_ONLY -> "Enabled Plugins Only"
},
@ -313,7 +321,7 @@ private fun PlaybackSettingsSection(
onClick = { showAutoPlayAddonSelectionDialog = true },
)
}
if (autoPlayPlayerSettings.streamAutoPlaySource != StreamAutoPlaySource.INSTALLED_ADDONS_ONLY) {
if (pluginsEnabled && autoPlayPlayerSettings.streamAutoPlaySource != StreamAutoPlaySource.INSTALLED_ADDONS_ONLY) {
SettingsGroupDivider(isTablet = isTablet)
val pluginSubtitle = if (autoPlayPlayerSettings.streamAutoPlaySelectedPlugins.isEmpty()) {
"All Plugins"
@ -678,6 +686,7 @@ private fun PlaybackSettingsSection(
if (showAutoPlaySourceDialog) {
StreamAutoPlaySourceDialog(
pluginsEnabled = pluginsEnabled,
selectedSource = autoPlayPlayerSettings.streamAutoPlaySource,
onSourceSelected = {
PlayerSettingsRepository.setStreamAutoPlaySource(it)
@ -707,7 +716,7 @@ private fun PlaybackSettingsSection(
)
}
if (showAutoPlayPluginSelectionDialog) {
if (pluginsEnabled && showAutoPlayPluginSelectionDialog) {
val pluginNames = pluginUiState.scrapers
.filter { it.enabled }
.map { it.name }
@ -1121,15 +1130,40 @@ private fun StreamAutoPlayModeDialog(
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun StreamAutoPlaySourceDialog(
pluginsEnabled: Boolean,
selectedSource: StreamAutoPlaySource,
onSourceSelected: (StreamAutoPlaySource) -> Unit,
onDismiss: () -> Unit,
) {
val options = listOf(
Triple(StreamAutoPlaySource.ALL_SOURCES, "All Sources", "Consider streams from both addons and plugins."),
Triple(StreamAutoPlaySource.INSTALLED_ADDONS_ONLY, "Installed Addons Only", "Only consider streams from installed addons."),
Triple(StreamAutoPlaySource.ENABLED_PLUGINS_ONLY, "Enabled Plugins Only", "Only consider streams from enabled plugins."),
)
val options = buildList {
add(
Triple(
StreamAutoPlaySource.ALL_SOURCES,
if (pluginsEnabled) "All Sources" else "All Addons",
if (pluginsEnabled) {
"Consider streams from both addons and plugins."
} else {
"Consider streams from all installed addons."
},
),
)
add(
Triple(
StreamAutoPlaySource.INSTALLED_ADDONS_ONLY,
"Installed Addons Only",
"Only consider streams from installed addons.",
),
)
if (pluginsEnabled) {
add(
Triple(
StreamAutoPlaySource.ENABLED_PLUGINS_ONLY,
"Enabled Plugins Only",
"Only consider streams from enabled plugins.",
),
)
}
}
BasicAlertDialog(
onDismissRequest = onDismiss,

View file

@ -1,5 +1,6 @@
package com.nuvio.app.features.settings
import com.nuvio.app.core.build.AppFeaturePolicy
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@ -135,6 +136,11 @@ fun AddonsSettingsScreen(
fun PluginsSettingsScreen(
onBack: () -> Unit,
) {
if (!AppFeaturePolicy.pluginsEnabled) {
AddonsSettingsScreen(onBack = onBack)
return
}
LaunchedEffect(Unit) {
PluginRepository.initialize()
}

View file

@ -1,5 +1,7 @@
package com.nuvio.app.features.settings
import com.nuvio.app.core.build.AppFeaturePolicy
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
@ -290,13 +292,14 @@ private fun MobileSettingsScreen(
)
SettingsPage.ContentDiscovery -> contentDiscoveryContent(
isTablet = false,
showPluginsEntry = AppFeaturePolicy.pluginsEnabled,
onAddonsClick = onAddonsClick,
onPluginsClick = onPluginsClick,
onHomescreenClick = onHomescreenClick,
onMetaScreenClick = onMetaScreenClick,
)
SettingsPage.Addons -> addonsSettingsContent()
SettingsPage.Plugins -> pluginsSettingsContent()
SettingsPage.Plugins -> if (AppFeaturePolicy.pluginsEnabled) pluginsSettingsContent() else addonsSettingsContent()
SettingsPage.Homescreen -> homescreenSettingsContent(
isTablet = false,
heroEnabled = homescreenHeroEnabled,
@ -484,13 +487,14 @@ private fun TabletSettingsScreen(
)
SettingsPage.ContentDiscovery -> contentDiscoveryContent(
isTablet = true,
showPluginsEntry = AppFeaturePolicy.pluginsEnabled,
onAddonsClick = { openInlinePage(SettingsPage.Addons) },
onPluginsClick = { openInlinePage(SettingsPage.Plugins) },
onHomescreenClick = { openInlinePage(SettingsPage.Homescreen) },
onMetaScreenClick = { openInlinePage(SettingsPage.MetaScreen) },
)
SettingsPage.Addons -> addonsSettingsContent()
SettingsPage.Plugins -> pluginsSettingsContent()
SettingsPage.Plugins -> if (AppFeaturePolicy.pluginsEnabled) pluginsSettingsContent() else addonsSettingsContent()
SettingsPage.Homescreen -> homescreenSettingsContent(
isTablet = true,
heroEnabled = homescreenHeroEnabled,

View file

@ -1,11 +1,13 @@
package com.nuvio.app.features.streams
import co.touchlab.kermit.Logger
import com.nuvio.app.core.build.AppFeaturePolicy
import com.nuvio.app.features.addons.AddonRepository
import com.nuvio.app.features.addons.httpGetText
import com.nuvio.app.features.details.MetaDetailsRepository
import com.nuvio.app.features.player.PlayerSettingsRepository
import com.nuvio.app.features.plugins.PluginRepository
import com.nuvio.app.features.plugins.PluginsUiState
import com.nuvio.app.features.plugins.PluginRepositoryItem
import com.nuvio.app.features.plugins.PluginRuntimeResult
import com.nuvio.app.features.plugins.PluginScraper
@ -39,8 +41,12 @@ object StreamsRepository {
}
private fun load(type: String, videoId: String, season: Int?, episode: Int?, forceRefresh: Boolean) {
PluginRepository.initialize()
val pluginUiState = PluginRepository.uiState.value
val pluginUiState = if (AppFeaturePolicy.pluginsEnabled) {
PluginRepository.initialize()
PluginRepository.uiState.value
} else {
PluginsUiState(pluginsEnabled = false)
}
val requestKey = "$type::$videoId::$season::$episode::pluginsGrouped=${pluginUiState.groupStreamsByRepository}"
val currentState = _uiState.value
if (
@ -89,7 +95,11 @@ object StreamsRepository {
}
val installedAddons = AddonRepository.uiState.value.addons
val pluginScrapers = PluginRepository.getEnabledScrapersForType(type)
val pluginScrapers = if (AppFeaturePolicy.pluginsEnabled) {
PluginRepository.getEnabledScrapersForType(type)
} else {
emptyList()
}
val pluginProviderGroups = pluginScrapers.toPluginProviderGroups(
repositories = pluginUiState.repositories,
groupByRepository = pluginUiState.groupStreamsByRepository,