mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-25 10:33:07 +00:00
feat: add support for custom addon names
This commit is contained in:
parent
6c17efdbef
commit
ebcccb9b4c
10 changed files with 51 additions and 25 deletions
|
|
@ -44,6 +44,7 @@ data class AddonBehaviorHints(
|
||||||
data class ManagedAddon(
|
data class ManagedAddon(
|
||||||
val manifestUrl: String,
|
val manifestUrl: String,
|
||||||
val manifest: AddonManifest? = null,
|
val manifest: AddonManifest? = null,
|
||||||
|
val userSetName: String? = null,
|
||||||
val isRefreshing: Boolean = false,
|
val isRefreshing: Boolean = false,
|
||||||
val errorMessage: String? = null,
|
val errorMessage: String? = null,
|
||||||
) {
|
) {
|
||||||
|
|
@ -51,7 +52,9 @@ data class ManagedAddon(
|
||||||
get() = manifest != null
|
get() = manifest != null
|
||||||
|
|
||||||
val displayTitle: String
|
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(
|
data class AddonsUiState(
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,13 @@ object AddonRepository {
|
||||||
}
|
}
|
||||||
.decodeList<AddonRow>()
|
.decodeList<AddonRow>()
|
||||||
|
|
||||||
|
val namesByUrl = mutableMapOf<String, String>()
|
||||||
|
rows.forEach { row ->
|
||||||
|
if (!row.name.isNullOrBlank()) {
|
||||||
|
namesByUrl[ensureManifestSuffix(row.url)] = row.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val urls = dedupeManifestUrls(rows.map { it.url })
|
val urls = dedupeManifestUrls(rows.map { it.url })
|
||||||
log.i { "pullFromServer() — server returned ${rows.size} addons" }
|
log.i { "pullFromServer() — server returned ${rows.size} addons" }
|
||||||
urls.forEachIndexed { i, u -> log.d { " server[$i]: $u" } }
|
urls.forEachIndexed { i, u -> log.d { " server[$i]: $u" } }
|
||||||
|
|
@ -164,7 +171,7 @@ object AddonRepository {
|
||||||
val existingByUrl = _uiState.value.addons.associateBy(ManagedAddon::manifestUrl)
|
val existingByUrl = _uiState.value.addons.associateBy(ManagedAddon::manifestUrl)
|
||||||
_uiState.value = AddonsUiState(
|
_uiState.value = AddonsUiState(
|
||||||
addons = urls.map { url ->
|
addons = urls.map { url ->
|
||||||
existingByUrl[url].toPendingAddon(url)
|
existingByUrl[url].toPendingAddon(url, namesByUrl[url])
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
persist()
|
persist()
|
||||||
|
|
@ -311,7 +318,7 @@ object AddonRepository {
|
||||||
.mapIndexed { index, addon ->
|
.mapIndexed { index, addon ->
|
||||||
AddonPushItem(
|
AddonPushItem(
|
||||||
url = addon.manifestUrl,
|
url = addon.manifestUrl,
|
||||||
name = addon.manifest?.name ?: "",
|
name = addon.userSetName?.takeIf { it.isNotBlank() } ?: addon.manifest?.name ?: "",
|
||||||
enabled = true,
|
enabled = true,
|
||||||
sortOrder = index,
|
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 {
|
when {
|
||||||
this == null -> ManagedAddon(
|
this == null -> ManagedAddon(
|
||||||
manifestUrl = manifestUrl,
|
manifestUrl = manifestUrl,
|
||||||
isRefreshing = true,
|
isRefreshing = true,
|
||||||
|
userSetName = userSetName,
|
||||||
)
|
)
|
||||||
manifest != null -> copy(
|
manifest != null -> copy(
|
||||||
manifestUrl = manifestUrl,
|
manifestUrl = manifestUrl,
|
||||||
isRefreshing = false,
|
isRefreshing = false,
|
||||||
|
userSetName = userSetName ?: this.userSetName,
|
||||||
|
)
|
||||||
|
isRefreshing -> copy(
|
||||||
|
manifestUrl = manifestUrl,
|
||||||
|
userSetName = userSetName ?: this.userSetName,
|
||||||
)
|
)
|
||||||
isRefreshing -> copy(manifestUrl = manifestUrl)
|
|
||||||
else -> copy(
|
else -> copy(
|
||||||
manifestUrl = manifestUrl,
|
manifestUrl = manifestUrl,
|
||||||
isRefreshing = true,
|
isRefreshing = true,
|
||||||
errorMessage = null,
|
errorMessage = null,
|
||||||
|
userSetName = userSetName ?: this.userSetName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -330,7 +330,7 @@ private fun InstalledAddonCard(
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text(
|
Text(
|
||||||
text = manifest?.name ?: addon.displayTitle,
|
text = addon.displayTitle,
|
||||||
style = MaterialTheme.typography.headlineLarge,
|
style = MaterialTheme.typography.headlineLarge,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
maxLines = 2,
|
maxLines = 2,
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ object CollectionRepository {
|
||||||
.map { catalog ->
|
.map { catalog ->
|
||||||
AvailableCatalog(
|
AvailableCatalog(
|
||||||
addonId = manifest.id,
|
addonId = manifest.id,
|
||||||
addonName = manifest.name,
|
addonName = addon.displayTitle,
|
||||||
type = catalog.type,
|
type = catalog.type,
|
||||||
catalogId = catalog.id,
|
catalogId = catalog.id,
|
||||||
catalogName = catalog.name,
|
catalogName = catalog.name,
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ fun buildHomeCatalogDefinitions(addons: List<ManagedAddon>): List<HomeCatalogDef
|
||||||
HomeCatalogDefinition(
|
HomeCatalogDefinition(
|
||||||
key = "${manifest.id}:${catalog.type}:${catalog.id}",
|
key = "${manifest.id}:${catalog.type}:${catalog.id}",
|
||||||
defaultTitle = "${catalog.name} - ${catalog.type.displayLabel()}",
|
defaultTitle = "${catalog.name} - ${catalog.type.displayLabel()}",
|
||||||
addonName = manifest.name,
|
addonName = addon.displayTitle,
|
||||||
manifestUrl = addon.manifestUrl,
|
manifestUrl = addon.manifestUrl,
|
||||||
type = catalog.type,
|
type = catalog.type,
|
||||||
catalogId = catalog.id,
|
catalogId = catalog.id,
|
||||||
|
|
|
||||||
|
|
@ -629,7 +629,7 @@ fun PlayerScreen(
|
||||||
)
|
)
|
||||||
|
|
||||||
val installedAddonNames = AddonRepository.uiState.value.addons
|
val installedAddonNames = AddonRepository.uiState.value.addons
|
||||||
.mapNotNull { it.manifest?.name }
|
.map { it.displayTitle }
|
||||||
.toSet()
|
.toSet()
|
||||||
|
|
||||||
val timeoutMs = settings.streamAutoPlayTimeoutSeconds * 1000L
|
val timeoutMs = settings.streamAutoPlayTimeoutSeconds * 1000L
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,10 @@ object PlayerStreamsRepository {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val addonDisplayNames = installedAddons.associate {
|
||||||
|
(it.manifest?.id ?: it.manifestUrl) to it.displayTitle
|
||||||
|
}
|
||||||
|
|
||||||
val streamAddons = installedAddons
|
val streamAddons = installedAddons
|
||||||
.mapNotNull { it.manifest }
|
.mapNotNull { it.manifest }
|
||||||
.filter { manifest ->
|
.filter { manifest ->
|
||||||
|
|
@ -186,7 +190,7 @@ object PlayerStreamsRepository {
|
||||||
|
|
||||||
val initialGroups = streamAddons.map { manifest ->
|
val initialGroups = streamAddons.map { manifest ->
|
||||||
AddonStreamGroup(
|
AddonStreamGroup(
|
||||||
addonName = manifest.name,
|
addonName = addonDisplayNames[manifest.id] ?: manifest.name,
|
||||||
addonId = manifest.id,
|
addonId = manifest.id,
|
||||||
streams = emptyList(),
|
streams = emptyList(),
|
||||||
isLoading = true,
|
isLoading = true,
|
||||||
|
|
@ -214,16 +218,17 @@ object PlayerStreamsRepository {
|
||||||
.removeSuffix("/manifest.json")
|
.removeSuffix("/manifest.json")
|
||||||
val url = "$baseUrl/stream/$type/$encodedId.json"
|
val url = "$baseUrl/stream/$type/$encodedId.json"
|
||||||
|
|
||||||
|
val displayName = addonDisplayNames[manifest.id] ?: manifest.name
|
||||||
runCatching {
|
runCatching {
|
||||||
val payload = httpGetText(url)
|
val payload = httpGetText(url)
|
||||||
StreamParser.parse(payload, manifest.name, manifest.id)
|
StreamParser.parse(payload, displayName, manifest.id)
|
||||||
}.fold(
|
}.fold(
|
||||||
onSuccess = { streams ->
|
onSuccess = { streams ->
|
||||||
AddonStreamGroup(manifest.name, manifest.id, streams, isLoading = false)
|
AddonStreamGroup(displayName, manifest.id, streams, isLoading = false)
|
||||||
},
|
},
|
||||||
onFailure = { err ->
|
onFailure = { err ->
|
||||||
log.w(err) { "Failed: ${manifest.name}" }
|
log.w(err) { "Failed: ${displayName}" }
|
||||||
AddonStreamGroup(manifest.name, manifest.id, emptyList(), isLoading = false, error = err.message)
|
AddonStreamGroup(displayName, manifest.id, emptyList(), isLoading = false, error = err.message)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ object SubtitleRepository {
|
||||||
id = id,
|
id = id,
|
||||||
url = url,
|
url = url,
|
||||||
language = lang,
|
language = lang,
|
||||||
display = "${formatLanguage(lang)} (${manifest.name})",
|
display = "${formatLanguage(lang)} (${addon.displayTitle})",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,7 @@ object SearchRepository {
|
||||||
val genreExtra = catalog.genreExtra()
|
val genreExtra = catalog.genreExtra()
|
||||||
DiscoverCatalogOption(
|
DiscoverCatalogOption(
|
||||||
key = "${manifest.id}:${catalog.type}:${catalog.id}",
|
key = "${manifest.id}:${catalog.type}:${catalog.id}",
|
||||||
addonName = manifest.name,
|
addonName = addon.displayTitle,
|
||||||
manifestUrl = addon.manifestUrl,
|
manifestUrl = addon.manifestUrl,
|
||||||
type = catalog.type,
|
type = catalog.type,
|
||||||
catalogId = catalog.id,
|
catalogId = catalog.id,
|
||||||
|
|
@ -314,8 +314,8 @@ object SearchRepository {
|
||||||
return HomeCatalogSection(
|
return HomeCatalogSection(
|
||||||
key = "${manifest.id}:search:$type:$catalogId:${query.lowercase()}",
|
key = "${manifest.id}:search:$type:$catalogId:${query.lowercase()}",
|
||||||
title = "$catalogName - ${type.displayLabel()}",
|
title = "$catalogName - ${type.displayLabel()}",
|
||||||
subtitle = manifest.name,
|
subtitle = addon.displayTitle,
|
||||||
addonName = manifest.name,
|
addonName = addon.displayTitle,
|
||||||
type = type,
|
type = type,
|
||||||
manifestUrl = manifest.transportUrl,
|
manifestUrl = manifest.transportUrl,
|
||||||
catalogId = catalogId,
|
catalogId = catalogId,
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,10 @@ object StreamsRepository {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val addonDisplayNames = installedAddons.associate {
|
||||||
|
(it.manifest?.id ?: it.manifestUrl) to it.displayTitle
|
||||||
|
}
|
||||||
|
|
||||||
val streamAddons = installedAddons
|
val streamAddons = installedAddons
|
||||||
.mapNotNull { it.manifest }
|
.mapNotNull { it.manifest }
|
||||||
.filter { manifest ->
|
.filter { manifest ->
|
||||||
|
|
@ -151,7 +155,7 @@ object StreamsRepository {
|
||||||
// Initialise loading placeholders
|
// Initialise loading placeholders
|
||||||
val initialGroups = streamAddons.map { manifest ->
|
val initialGroups = streamAddons.map { manifest ->
|
||||||
AddonStreamGroup(
|
AddonStreamGroup(
|
||||||
addonName = manifest.name,
|
addonName = addonDisplayNames[manifest.id] ?: manifest.name,
|
||||||
addonId = manifest.id,
|
addonId = manifest.id,
|
||||||
streams = emptyList(),
|
streams = emptyList(),
|
||||||
isLoading = true,
|
isLoading = true,
|
||||||
|
|
@ -182,7 +186,7 @@ object StreamsRepository {
|
||||||
val totalTasks = streamAddons.size + pluginRemainingByAddonId.values.sum()
|
val totalTasks = streamAddons.size + pluginRemainingByAddonId.values.sum()
|
||||||
|
|
||||||
val installedAddonNames = installedAddons
|
val installedAddonNames = installedAddons
|
||||||
.mapNotNull { it.manifest?.name }
|
.map { it.displayTitle }
|
||||||
.toSet()
|
.toSet()
|
||||||
var autoSelectTriggered = false
|
var autoSelectTriggered = false
|
||||||
var timeoutElapsed = false
|
var timeoutElapsed = false
|
||||||
|
|
@ -237,27 +241,28 @@ object StreamsRepository {
|
||||||
val url = "$baseUrl/stream/$type/$encodedId.json"
|
val url = "$baseUrl/stream/$type/$encodedId.json"
|
||||||
log.d { "Fetching streams from: $url" }
|
log.d { "Fetching streams from: $url" }
|
||||||
|
|
||||||
|
val displayName = addonDisplayNames[manifest.id] ?: manifest.name
|
||||||
val group = runCatching {
|
val group = runCatching {
|
||||||
val payload = httpGetText(url)
|
val payload = httpGetText(url)
|
||||||
StreamParser.parse(
|
StreamParser.parse(
|
||||||
payload = payload,
|
payload = payload,
|
||||||
addonName = manifest.name,
|
addonName = displayName,
|
||||||
addonId = manifest.id,
|
addonId = manifest.id,
|
||||||
)
|
)
|
||||||
}.fold(
|
}.fold(
|
||||||
onSuccess = { streams ->
|
onSuccess = { streams ->
|
||||||
log.d { "Got ${streams.size} streams from ${manifest.name}" }
|
log.d { "Got ${streams.size} streams from ${displayName}" }
|
||||||
AddonStreamGroup(
|
AddonStreamGroup(
|
||||||
addonName = manifest.name,
|
addonName = displayName,
|
||||||
addonId = manifest.id,
|
addonId = manifest.id,
|
||||||
streams = streams,
|
streams = streams,
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onFailure = { err ->
|
onFailure = { err ->
|
||||||
log.w(err) { "Failed to fetch streams from ${manifest.name}" }
|
log.w(err) { "Failed to fetch streams from ${displayName}" }
|
||||||
AddonStreamGroup(
|
AddonStreamGroup(
|
||||||
addonName = manifest.name,
|
addonName = displayName,
|
||||||
addonId = manifest.id,
|
addonId = manifest.id,
|
||||||
streams = emptyList(),
|
streams = emptyList(),
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue