diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonModels.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonModels.kt index 1d074109..163861fc 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonModels.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonModels.kt @@ -44,6 +44,7 @@ data class AddonBehaviorHints( data class ManagedAddon( val manifestUrl: String, val manifest: AddonManifest? = null, + val userSetName: String? = null, val isRefreshing: Boolean = false, val errorMessage: String? = null, ) { @@ -51,7 +52,9 @@ data class ManagedAddon( get() = manifest != null val displayTitle: String - get() = manifest?.name ?: manifestUrl.substringBefore("?").substringAfterLast("/").ifBlank { "Addon" } + get() = userSetName?.takeIf { it.isNotBlank() && it != manifest?.name } + ?: manifest?.name + ?: manifestUrl.substringBefore("?").substringAfterLast("/").ifBlank { "Addon" } } data class AddonsUiState( diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonRepository.kt index 09f0a80c..79ce45da 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonRepository.kt @@ -108,6 +108,13 @@ object AddonRepository { } .decodeList() + val namesByUrl = mutableMapOf() + rows.forEach { row -> + if (!row.name.isNullOrBlank()) { + namesByUrl[ensureManifestSuffix(row.url)] = row.name + } + } + val urls = dedupeManifestUrls(rows.map { it.url }) log.i { "pullFromServer() — server returned ${rows.size} addons" } urls.forEachIndexed { i, u -> log.d { " server[$i]: $u" } } @@ -164,7 +171,7 @@ object AddonRepository { val existingByUrl = _uiState.value.addons.associateBy(ManagedAddon::manifestUrl) _uiState.value = AddonsUiState( addons = urls.map { url -> - existingByUrl[url].toPendingAddon(url) + existingByUrl[url].toPendingAddon(url, namesByUrl[url]) }, ) persist() @@ -311,7 +318,7 @@ object AddonRepository { .mapIndexed { index, addon -> AddonPushItem( url = addon.manifestUrl, - name = addon.manifest?.name ?: "", + name = addon.userSetName?.takeIf { it.isNotBlank() } ?: addon.manifest?.name ?: "", enabled = true, sortOrder = index, ) @@ -369,21 +376,27 @@ object AddonRepository { } } -private fun ManagedAddon?.toPendingAddon(manifestUrl: String): ManagedAddon = +private fun ManagedAddon?.toPendingAddon(manifestUrl: String, userSetName: String? = null): ManagedAddon = when { this == null -> ManagedAddon( manifestUrl = manifestUrl, isRefreshing = true, + userSetName = userSetName, ) manifest != null -> copy( manifestUrl = manifestUrl, isRefreshing = false, + userSetName = userSetName ?: this.userSetName, + ) + isRefreshing -> copy( + manifestUrl = manifestUrl, + userSetName = userSetName ?: this.userSetName, ) - isRefreshing -> copy(manifestUrl = manifestUrl) else -> copy( manifestUrl = manifestUrl, isRefreshing = true, errorMessage = null, + userSetName = userSetName ?: this.userSetName, ) } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonsScreen.kt index e4dad82c..6fc1e610 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonsScreen.kt @@ -330,7 +330,7 @@ private fun InstalledAddonCard( Spacer(modifier = Modifier.width(16.dp)) Column(modifier = Modifier.weight(1f)) { Text( - text = manifest?.name ?: addon.displayTitle, + text = addon.displayTitle, style = MaterialTheme.typography.headlineLarge, color = MaterialTheme.colorScheme.onSurface, maxLines = 2, diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt index b25ba7b0..b0da6f13 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt @@ -162,7 +162,7 @@ object CollectionRepository { .map { catalog -> AvailableCatalog( addonId = manifest.id, - addonName = manifest.name, + addonName = addon.displayTitle, type = catalog.type, catalogId = catalog.id, catalogName = catalog.name, diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogDefinitions.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogDefinitions.kt index 557b2fa9..52342795 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogDefinitions.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogDefinitions.kt @@ -24,7 +24,7 @@ fun buildHomeCatalogDefinitions(addons: List): List @@ -186,7 +190,7 @@ object PlayerStreamsRepository { val initialGroups = streamAddons.map { manifest -> AddonStreamGroup( - addonName = manifest.name, + addonName = addonDisplayNames[manifest.id] ?: manifest.name, addonId = manifest.id, streams = emptyList(), isLoading = true, @@ -214,16 +218,17 @@ object PlayerStreamsRepository { .removeSuffix("/manifest.json") val url = "$baseUrl/stream/$type/$encodedId.json" + val displayName = addonDisplayNames[manifest.id] ?: manifest.name runCatching { val payload = httpGetText(url) - StreamParser.parse(payload, manifest.name, manifest.id) + StreamParser.parse(payload, displayName, manifest.id) }.fold( onSuccess = { streams -> - AddonStreamGroup(manifest.name, manifest.id, streams, isLoading = false) + AddonStreamGroup(displayName, manifest.id, streams, isLoading = false) }, onFailure = { err -> - log.w(err) { "Failed: ${manifest.name}" } - AddonStreamGroup(manifest.name, manifest.id, emptyList(), isLoading = false, error = err.message) + log.w(err) { "Failed: ${displayName}" } + AddonStreamGroup(displayName, manifest.id, emptyList(), isLoading = false, error = err.message) }, ) } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/SubtitleRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/SubtitleRepository.kt index 31aa2312..7164d596 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/SubtitleRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/SubtitleRepository.kt @@ -71,7 +71,7 @@ object SubtitleRepository { id = id, url = url, language = lang, - display = "${formatLanguage(lang)} (${manifest.name})", + display = "${formatLanguage(lang)} (${addon.displayTitle})", ) ) } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/search/SearchRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/search/SearchRepository.kt index f2ec8ba3..30d3c5f7 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/search/SearchRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/search/SearchRepository.kt @@ -288,7 +288,7 @@ object SearchRepository { val genreExtra = catalog.genreExtra() DiscoverCatalogOption( key = "${manifest.id}:${catalog.type}:${catalog.id}", - addonName = manifest.name, + addonName = addon.displayTitle, manifestUrl = addon.manifestUrl, type = catalog.type, catalogId = catalog.id, @@ -314,8 +314,8 @@ object SearchRepository { return HomeCatalogSection( key = "${manifest.id}:search:$type:$catalogId:${query.lowercase()}", title = "$catalogName - ${type.displayLabel()}", - subtitle = manifest.name, - addonName = manifest.name, + subtitle = addon.displayTitle, + addonName = addon.displayTitle, type = type, manifestUrl = manifest.transportUrl, catalogId = catalogId, 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 1c5a66e0..246c4e4d 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 @@ -127,6 +127,10 @@ object StreamsRepository { return } + val addonDisplayNames = installedAddons.associate { + (it.manifest?.id ?: it.manifestUrl) to it.displayTitle + } + val streamAddons = installedAddons .mapNotNull { it.manifest } .filter { manifest -> @@ -151,7 +155,7 @@ object StreamsRepository { // Initialise loading placeholders val initialGroups = streamAddons.map { manifest -> AddonStreamGroup( - addonName = manifest.name, + addonName = addonDisplayNames[manifest.id] ?: manifest.name, addonId = manifest.id, streams = emptyList(), isLoading = true, @@ -182,7 +186,7 @@ object StreamsRepository { val totalTasks = streamAddons.size + pluginRemainingByAddonId.values.sum() val installedAddonNames = installedAddons - .mapNotNull { it.manifest?.name } + .map { it.displayTitle } .toSet() var autoSelectTriggered = false var timeoutElapsed = false @@ -237,27 +241,28 @@ object StreamsRepository { val url = "$baseUrl/stream/$type/$encodedId.json" log.d { "Fetching streams from: $url" } + val displayName = addonDisplayNames[manifest.id] ?: manifest.name val group = runCatching { val payload = httpGetText(url) StreamParser.parse( payload = payload, - addonName = manifest.name, + addonName = displayName, addonId = manifest.id, ) }.fold( onSuccess = { streams -> - log.d { "Got ${streams.size} streams from ${manifest.name}" } + log.d { "Got ${streams.size} streams from ${displayName}" } AddonStreamGroup( - addonName = manifest.name, + addonName = displayName, addonId = manifest.id, streams = streams, isLoading = false, ) }, onFailure = { err -> - log.w(err) { "Failed to fetch streams from ${manifest.name}" } + log.w(err) { "Failed to fetch streams from ${displayName}" } AddonStreamGroup( - addonName = manifest.name, + addonName = displayName, addonId = manifest.id, streams = emptyList(), isLoading = false,