From 860e5e4e2c54f987582332a6100f344d0650f33b Mon Sep 17 00:00:00 2001 From: skoruppa Date: Wed, 10 Jun 2026 14:46:48 +0200 Subject: [PATCH] Add option to enable addons logo in stream screen --- .../StreamBadgeSettingsStorage.android.kt | 7 +++++ .../composeResources/values-pl/strings.xml | 3 ++ .../composeResources/values/strings.xml | 3 ++ .../app/features/settings/SettingsSearch.kt | 9 ++++++ .../features/settings/StreamsSettingsPage.kt | 18 +++++++++++ .../streams/StreamBadgeSettingsRepository.kt | 13 ++++++++ .../streams/StreamBadgeSettingsStorage.kt | 2 ++ .../app/features/streams/StreamModels.kt | 1 + .../app/features/streams/StreamParser.kt | 2 ++ .../app/features/streams/StreamsRepository.kt | 1 + .../app/features/streams/StreamsScreen.kt | 31 ++++++++++++++++++- .../streams/StreamBadgeSettingsStorage.ios.kt | 7 +++++ 12 files changed, 96 insertions(+), 1 deletion(-) diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.android.kt index cfab3bd0..64a95118 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.android.kt @@ -16,6 +16,7 @@ actual object StreamBadgeSettingsStorage { private const val legacyDebridPreferencesName = "nuvio_debrid_settings" private const val streamBadgeRulesKey = "stream_badge_rules" private const val showFileSizeBadgesKey = "show_file_size_badges" + private const val showAddonLogoKey = "show_addon_logo" private const val streamBadgePlacementKey = "stream_badge_placement" private const val legacyDebridStreamBadgeRulesKey = "debrid_stream_badge_rules" @@ -41,6 +42,12 @@ actual object StreamBadgeSettingsStorage { saveBoolean(showFileSizeBadgesKey, enabled) } + actual fun loadShowAddonLogo(): Boolean? = loadBoolean(showAddonLogoKey) + + actual fun saveShowAddonLogo(enabled: Boolean) { + saveBoolean(showAddonLogoKey, enabled) + } + actual fun loadStreamBadgePlacement(): String? = loadString(streamBadgePlacementKey) actual fun saveStreamBadgePlacement(placement: String) { diff --git a/composeApp/src/commonMain/composeResources/values-pl/strings.xml b/composeApp/src/commonMain/composeResources/values-pl/strings.xml index 3e487c29..f179fcf5 100644 --- a/composeApp/src/commonMain/composeResources/values-pl/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-pl/strings.xml @@ -1456,4 +1456,7 @@ Torrenty Usenet Web + Logo dodatku + Pokazuj logo i nazwę dodatku obok źródeł. + Wyświetlanie diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index 582b500a..be681d8d 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -681,6 +681,9 @@ Fusion Style Size badges Show file size badges in stream results and player source panels. + Addon logo + Show addon logo and name next to stream sources. + Display Badge position Choose whether Fusion and size badges appear above or below stream cards. Badge position diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt index d5fb310d..5d0f8cf3 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt @@ -463,6 +463,15 @@ internal fun settingsSearchEntries( val playbackSubtitleRendering = stringResource(Res.string.settings_playback_section_subtitle_rendering) val playbackSkipSegments = stringResource(Res.string.settings_playback_section_skip_segments) val playbackNextEpisode = stringResource(Res.string.settings_playback_section_next_episode) + addRow( + page = SettingsPage.Streams, + key = "stream-addon-logo", + title = stringResource(Res.string.settings_stream_addon_logo_title), + description = stringResource(Res.string.settings_stream_addon_logo_description), + pageLabel = streamsPage, + section = stringResource(Res.string.settings_stream_display_section), + icon = Icons.Rounded.Style, + ) addRow( page = SettingsPage.Streams, key = "stream-size-badges", diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/StreamsSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/StreamsSettingsPage.kt index 7075eb1a..7943b55a 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/StreamsSettingsPage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/StreamsSettingsPage.kt @@ -87,6 +87,9 @@ import nuvio.composeapp.generated.resources.settings_stream_badge_urls_title import nuvio.composeapp.generated.resources.settings_stream_badges_section import nuvio.composeapp.generated.resources.settings_stream_size_badges_description import nuvio.composeapp.generated.resources.settings_stream_size_badges_title +import nuvio.composeapp.generated.resources.settings_stream_addon_logo_title +import nuvio.composeapp.generated.resources.settings_stream_addon_logo_description +import nuvio.composeapp.generated.resources.settings_stream_display_section import org.jetbrains.compose.resources.stringResource internal fun LazyListScope.streamsSettingsContent(isTablet: Boolean) { @@ -129,6 +132,21 @@ internal fun LazyListScope.streamsSettingsContent(isTablet: Boolean) { } } + SettingsSection( + title = stringResource(Res.string.settings_stream_display_section), + isTablet = isTablet, + ) { + SettingsGroup(isTablet = isTablet) { + SettingsSwitchRow( + title = stringResource(Res.string.settings_stream_addon_logo_title), + description = stringResource(Res.string.settings_stream_addon_logo_description), + checked = currentSettings.showAddonLogo, + isTablet = isTablet, + onCheckedChange = StreamBadgeSettingsRepository::setShowAddonLogo, + ) + } + } + if (showBadgeImportDialog) { BadgeUrlManagerDialog( currentRules = currentRules, diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsRepository.kt index c48690d4..397d26ad 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsRepository.kt @@ -15,6 +15,7 @@ import kotlinx.serialization.json.Json data class StreamBadgeSettingsUiState( val rules: StreamBadgeRules = StreamBadgeRules(), val showFileSizeBadges: Boolean = true, + val showAddonLogo: Boolean = false, val badgePlacement: StreamBadgePlacement = StreamBadgePlacement.BOTTOM, ) @@ -36,6 +37,7 @@ object StreamBadgeSettingsRepository { private var hasLoaded = false private var streamBadgeRules = StreamBadgeRules() private var showFileSizeBadges = true + private var showAddonLogo = false private var badgePlacement = StreamBadgePlacement.BOTTOM fun ensureLoaded() { @@ -51,6 +53,7 @@ object StreamBadgeSettingsRepository { hasLoaded = false streamBadgeRules = StreamBadgeRules() showFileSizeBadges = true + showAddonLogo = false badgePlacement = StreamBadgePlacement.BOTTOM _uiState.value = StreamBadgeSettingsUiState() } @@ -133,6 +136,14 @@ object StreamBadgeSettingsRepository { StreamBadgeSettingsStorage.saveShowFileSizeBadges(enabled) } + fun setShowAddonLogo(enabled: Boolean) { + ensureLoaded() + if (showAddonLogo == enabled) return + showAddonLogo = enabled + publish() + StreamBadgeSettingsStorage.saveShowAddonLogo(enabled) + } + fun setBadgePlacement(placement: StreamBadgePlacement) { ensureLoaded() if (badgePlacement == placement) return @@ -151,6 +162,7 @@ object StreamBadgeSettingsRepository { } streamBadgeRules = storedRules ?: legacyRules ?: StreamBadgeRules() showFileSizeBadges = StreamBadgeSettingsStorage.loadShowFileSizeBadges() ?: true + showAddonLogo = StreamBadgeSettingsStorage.loadShowAddonLogo() ?: false badgePlacement = StreamBadgeSettingsStorage.loadStreamBadgePlacement() ?.let { storedPlacement -> StreamBadgePlacement.entries.firstOrNull { placement -> @@ -169,6 +181,7 @@ object StreamBadgeSettingsRepository { _uiState.value = StreamBadgeSettingsUiState( rules = streamBadgeRules, showFileSizeBadges = showFileSizeBadges, + showAddonLogo = showAddonLogo, badgePlacement = badgePlacement, ) } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.kt index 5df55453..abd6dffe 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.kt @@ -7,6 +7,8 @@ internal expect object StreamBadgeSettingsStorage { fun saveStreamBadgeRules(rules: String) fun loadShowFileSizeBadges(): Boolean? fun saveShowFileSizeBadges(enabled: Boolean) + fun loadShowAddonLogo(): Boolean? + fun saveShowAddonLogo(enabled: Boolean) fun loadStreamBadgePlacement(): String? fun saveStreamBadgePlacement(placement: String) fun loadLegacyDebridStreamBadgeRules(): String? diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamModels.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamModels.kt index 7a8406c3..58a70141 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamModels.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamModels.kt @@ -17,6 +17,7 @@ data class StreamItem( val sourceName: String? = null, val addonName: String, val addonId: String, + val addonLogo: String? = null, val behaviorHints: StreamBehaviorHints = StreamBehaviorHints(), val clientResolve: StreamClientResolve? = null, val debridCacheStatus: StreamDebridCacheStatus? = null, diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamParser.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamParser.kt index 9a6aa866..a27033ed 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamParser.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamParser.kt @@ -18,6 +18,7 @@ object StreamParser { payload: String, addonName: String, addonId: String, + addonLogo: String? = null, ): List { val root = json.parseToJsonElement(payload).jsonObject val streamsArray = root["streams"] as? JsonArray ?: return emptyList() @@ -46,6 +47,7 @@ object StreamParser { sources = obj.stringList("sources"), addonName = addonName, addonId = addonId, + addonLogo = addonLogo, clientResolve = clientResolve, behaviorHints = StreamBehaviorHints( bingeGroup = hintsObj?.string("bingeGroup"), diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsRepository.kt index bc7bc787..49fc361a 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsRepository.kt @@ -456,6 +456,7 @@ object StreamsRepository { payload = payload, addonName = displayName, addonId = addon.addonId, + addonLogo = addon.manifest.logoUrl, ) }.fold( onSuccess = { streams -> diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt index 0f6281e2..c31ff887 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt @@ -833,6 +833,7 @@ internal fun StreamList( debridEnabled = debridEnabled, appendInstantServiceToDefaultName = appendInstantServiceToDefaultName, showFileSizeBadges = streamBadgeSettings.showFileSizeBadges, + showAddonLogo = streamBadgeSettings.showAddonLogo, badgePlacement = streamBadgeSettings.badgePlacement, onStreamSelected = onStreamSelected, onStreamLongPress = onStreamLongPress, @@ -860,6 +861,7 @@ private fun LazyListScope.streamSection( debridEnabled: Boolean, appendInstantServiceToDefaultName: Boolean, showFileSizeBadges: Boolean, + showAddonLogo: Boolean, badgePlacement: StreamBadgePlacement, onStreamSelected: (stream: StreamItem, resumePositionMs: Long?, resumeProgressFraction: Float?) -> Unit, onStreamLongPress: (StreamItem) -> Unit, @@ -907,6 +909,7 @@ private fun LazyListScope.streamSection( enabled = stream.isSelectableForPlayback(debridEnabled), appendInstantServiceToDefaultName = appendInstantServiceToDefaultName, showFileSizeBadges = showFileSizeBadges, + showAddonLogo = showAddonLogo, badgePlacement = badgePlacement, onClick = { if (stream.isSelectableForPlayback(debridEnabled)) { @@ -1014,6 +1017,7 @@ private fun StreamCard( enabled: Boolean, appendInstantServiceToDefaultName: Boolean, showFileSizeBadges: Boolean, + showAddonLogo: Boolean, badgePlacement: StreamBadgePlacement, onClick: () -> Unit, onLongClick: (() -> Unit)? = null, @@ -1040,7 +1044,7 @@ private fun StreamCard( onLongClick = onLongClick, ) .padding(14.dp), - verticalAlignment = Alignment.Top, + verticalAlignment = Alignment.CenterVertically, ) { Column(modifier = Modifier.weight(1f)) { if (hasBadges && badgePlacement == StreamBadgePlacement.TOP) { @@ -1079,6 +1083,31 @@ private fun StreamCard( ) } } + + if (showAddonLogo) { + Spacer(modifier = Modifier.width(12.dp)) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + if (!stream.addonLogo.isNullOrBlank()) { + AsyncImage( + model = stream.addonLogo, + contentDescription = stream.addonName, + modifier = Modifier + .size(28.dp) + .clip(RoundedCornerShape(6.dp)), + contentScale = ContentScale.Fit, + ) + } + Spacer(modifier = Modifier.height(2.dp)) + Text( + text = stream.addonName, + style = MaterialTheme.typography.labelSmall.copy(fontSize = 10.sp), + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + ) + } + } } } diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.ios.kt index 716304d8..8dbfc035 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/streams/StreamBadgeSettingsStorage.ios.kt @@ -13,6 +13,7 @@ import platform.Foundation.NSUserDefaults actual object StreamBadgeSettingsStorage { private const val streamBadgeRulesKey = "stream_badge_rules" private const val showFileSizeBadgesKey = "show_file_size_badges" + private const val showAddonLogoKey = "show_addon_logo" private const val streamBadgePlacementKey = "stream_badge_placement" private const val legacyDebridStreamBadgeRulesKey = "debrid_stream_badge_rules" private val syncKeys = listOf(streamBadgeRulesKey, showFileSizeBadgesKey, streamBadgePlacementKey) @@ -29,6 +30,12 @@ actual object StreamBadgeSettingsStorage { saveBoolean(showFileSizeBadgesKey, enabled) } + actual fun loadShowAddonLogo(): Boolean? = loadBoolean(showAddonLogoKey) + + actual fun saveShowAddonLogo(enabled: Boolean) { + saveBoolean(showAddonLogoKey, enabled) + } + actual fun loadStreamBadgePlacement(): String? = loadString(streamBadgePlacementKey) actual fun saveStreamBadgePlacement(placement: String) {