mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-04 09:19:06 +00:00
feat: support for multiple addon with same id
This commit is contained in:
parent
19b51c38d3
commit
a33558cbae
4 changed files with 66 additions and 34 deletions
|
|
@ -27,6 +27,7 @@ import androidx.compose.foundation.layout.widthIn
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
|
@ -535,7 +536,10 @@ private fun EpisodeStreamsSubView(
|
|||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
contentPadding = androidx.compose.foundation.layout.PaddingValues(bottom = 16.dp),
|
||||
) {
|
||||
items(streams, key = { "${it.addonId}::${it.url ?: it.name}" }) { stream ->
|
||||
itemsIndexed(
|
||||
items = streams,
|
||||
key = { index, stream -> "${stream.addonId}::${index}::${stream.url ?: stream.infoHash ?: stream.name}" },
|
||||
) { _, stream ->
|
||||
EpisodeSourceStreamRow(
|
||||
stream = stream,
|
||||
onClick = { onStreamSelected(stream, episode) },
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
|
@ -195,7 +196,10 @@ fun PlayerSourcesPanel(
|
|||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
contentPadding = androidx.compose.foundation.layout.PaddingValues(bottom = 16.dp),
|
||||
) {
|
||||
items(streams, key = { "${it.addonId}::${it.url ?: it.name}" }) { stream ->
|
||||
itemsIndexed(
|
||||
items = streams,
|
||||
key = { index, stream -> "${stream.addonId}::${index}::${stream.url ?: stream.infoHash ?: stream.name}" },
|
||||
) { _, stream ->
|
||||
val isCurrent = isCurrentStream(
|
||||
stream = stream,
|
||||
currentUrl = currentStreamUrl,
|
||||
|
|
|
|||
|
|
@ -165,19 +165,22 @@ object PlayerStreamsRepository {
|
|||
return
|
||||
}
|
||||
|
||||
val addonDisplayNames = installedAddons.associate {
|
||||
(it.manifest?.id ?: it.manifestUrl) to it.displayTitle
|
||||
}
|
||||
|
||||
val streamAddons = installedAddons
|
||||
.mapNotNull { it.manifest }
|
||||
.filter { manifest ->
|
||||
manifest.resources.any { resource ->
|
||||
.mapNotNull { addon ->
|
||||
val manifest = addon.manifest ?: return@mapNotNull null
|
||||
val supportsRequestedStream = manifest.resources.any { resource ->
|
||||
resource.name == "stream" &&
|
||||
resource.types.contains(type) &&
|
||||
(resource.idPrefixes.isEmpty() ||
|
||||
resource.idPrefixes.any { videoId.startsWith(it) })
|
||||
}
|
||||
if (!supportsRequestedStream) return@mapNotNull null
|
||||
|
||||
PlayerInstalledStreamAddonTarget(
|
||||
addonName = addon.displayTitle.ifBlank { manifest.name },
|
||||
addonId = addon.streamAddonInstanceId(manifest.id),
|
||||
manifest = manifest,
|
||||
)
|
||||
}
|
||||
|
||||
if (streamAddons.isEmpty() && pluginScrapers.isEmpty()) {
|
||||
|
|
@ -188,10 +191,10 @@ object PlayerStreamsRepository {
|
|||
return
|
||||
}
|
||||
|
||||
val initialGroups = streamAddons.map { manifest ->
|
||||
val initialGroups = streamAddons.map { addon ->
|
||||
AddonStreamGroup(
|
||||
addonName = addonDisplayNames[manifest.id] ?: manifest.name,
|
||||
addonId = manifest.id,
|
||||
addonName = addon.addonName,
|
||||
addonId = addon.addonId,
|
||||
streams = emptyList(),
|
||||
isLoading = true,
|
||||
)
|
||||
|
|
@ -210,25 +213,25 @@ object PlayerStreamsRepository {
|
|||
)
|
||||
|
||||
val job = scope.launch {
|
||||
val addonJobs = streamAddons.map { manifest ->
|
||||
val addonJobs = streamAddons.map { addon ->
|
||||
async {
|
||||
val encodedId = videoId.replace("%", "%25").replace(" ", "%20")
|
||||
val baseUrl = manifest.transportUrl
|
||||
val baseUrl = addon.manifest.transportUrl
|
||||
.substringBefore("?")
|
||||
.removeSuffix("/manifest.json")
|
||||
val url = "$baseUrl/stream/$type/$encodedId.json"
|
||||
|
||||
val displayName = addonDisplayNames[manifest.id] ?: manifest.name
|
||||
val displayName = addon.addonName
|
||||
runCatching {
|
||||
val payload = httpGetText(url)
|
||||
StreamParser.parse(payload, displayName, manifest.id)
|
||||
StreamParser.parse(payload, displayName, addon.addonId)
|
||||
}.fold(
|
||||
onSuccess = { streams ->
|
||||
AddonStreamGroup(displayName, manifest.id, streams, isLoading = false)
|
||||
AddonStreamGroup(displayName, addon.addonId, streams, isLoading = false)
|
||||
},
|
||||
onFailure = { err ->
|
||||
log.w(err) { "Failed: ${displayName}" }
|
||||
AddonStreamGroup(displayName, manifest.id, emptyList(), isLoading = false, error = err.message)
|
||||
AddonStreamGroup(displayName, addon.addonId, emptyList(), isLoading = false, error = err.message)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -289,6 +292,15 @@ object PlayerStreamsRepository {
|
|||
}
|
||||
}
|
||||
|
||||
private data class PlayerInstalledStreamAddonTarget(
|
||||
val addonName: String,
|
||||
val addonId: String,
|
||||
val manifest: com.nuvio.app.features.addons.AddonManifest,
|
||||
)
|
||||
|
||||
private fun com.nuvio.app.features.addons.ManagedAddon.streamAddonInstanceId(manifestId: String): String =
|
||||
"addon:$manifestId:$manifestUrl"
|
||||
|
||||
private fun PluginRuntimeResult.toStreamItem(scraper: PluginScraper): StreamItem {
|
||||
val subtitleParts = listOfNotNull(
|
||||
quality?.takeIf { it.isNotBlank() },
|
||||
|
|
|
|||
|
|
@ -127,19 +127,22 @@ object StreamsRepository {
|
|||
return
|
||||
}
|
||||
|
||||
val addonDisplayNames = installedAddons.associate {
|
||||
(it.manifest?.id ?: it.manifestUrl) to it.displayTitle
|
||||
}
|
||||
|
||||
val streamAddons = installedAddons
|
||||
.mapNotNull { it.manifest }
|
||||
.filter { manifest ->
|
||||
manifest.resources.any { resource ->
|
||||
.mapNotNull { addon ->
|
||||
val manifest = addon.manifest ?: return@mapNotNull null
|
||||
val supportsRequestedStream = manifest.resources.any { resource ->
|
||||
resource.name == "stream" &&
|
||||
resource.types.contains(type) &&
|
||||
(resource.idPrefixes.isEmpty() ||
|
||||
resource.idPrefixes.any { videoId.startsWith(it) })
|
||||
}
|
||||
if (!supportsRequestedStream) return@mapNotNull null
|
||||
|
||||
InstalledStreamAddonTarget(
|
||||
addonName = addon.displayTitle.ifBlank { manifest.name },
|
||||
addonId = addon.streamAddonInstanceId(manifest.id),
|
||||
manifest = manifest,
|
||||
)
|
||||
}
|
||||
|
||||
log.d { "Found ${streamAddons.size} addons for stream type=$type id=$videoId" }
|
||||
|
|
@ -153,10 +156,10 @@ object StreamsRepository {
|
|||
}
|
||||
|
||||
// Initialise loading placeholders
|
||||
val initialGroups = streamAddons.map { manifest ->
|
||||
val initialGroups = streamAddons.map { addon ->
|
||||
AddonStreamGroup(
|
||||
addonName = addonDisplayNames[manifest.id] ?: manifest.name,
|
||||
addonId = manifest.id,
|
||||
addonName = addon.addonName,
|
||||
addonId = addon.addonId,
|
||||
streams = emptyList(),
|
||||
isLoading = true,
|
||||
)
|
||||
|
|
@ -232,29 +235,29 @@ object StreamsRepository {
|
|||
null
|
||||
}
|
||||
|
||||
streamAddons.forEach { manifest ->
|
||||
streamAddons.forEach { addon ->
|
||||
launch {
|
||||
val encodedId = videoId.encodeForPath()
|
||||
val baseUrl = manifest.transportUrl
|
||||
val baseUrl = addon.manifest.transportUrl
|
||||
.substringBefore("?")
|
||||
.removeSuffix("/manifest.json")
|
||||
val url = "$baseUrl/stream/$type/$encodedId.json"
|
||||
log.d { "Fetching streams from: $url" }
|
||||
|
||||
val displayName = addonDisplayNames[manifest.id] ?: manifest.name
|
||||
val displayName = addon.addonName
|
||||
val group = runCatching {
|
||||
val payload = httpGetText(url)
|
||||
StreamParser.parse(
|
||||
payload = payload,
|
||||
addonName = displayName,
|
||||
addonId = manifest.id,
|
||||
addonId = addon.addonId,
|
||||
)
|
||||
}.fold(
|
||||
onSuccess = { streams ->
|
||||
log.d { "Got ${streams.size} streams from ${displayName}" }
|
||||
AddonStreamGroup(
|
||||
addonName = displayName,
|
||||
addonId = manifest.id,
|
||||
addonId = addon.addonId,
|
||||
streams = streams,
|
||||
isLoading = false,
|
||||
)
|
||||
|
|
@ -263,7 +266,7 @@ object StreamsRepository {
|
|||
log.w(err) { "Failed to fetch streams from ${displayName}" }
|
||||
AddonStreamGroup(
|
||||
addonName = displayName,
|
||||
addonId = manifest.id,
|
||||
addonId = addon.addonId,
|
||||
streams = emptyList(),
|
||||
isLoading = false,
|
||||
error = err.message,
|
||||
|
|
@ -423,6 +426,15 @@ object StreamsRepository {
|
|||
replace("%", "%25").replace(" ", "%20")
|
||||
}
|
||||
|
||||
private data class InstalledStreamAddonTarget(
|
||||
val addonName: String,
|
||||
val addonId: String,
|
||||
val manifest: com.nuvio.app.features.addons.AddonManifest,
|
||||
)
|
||||
|
||||
private fun com.nuvio.app.features.addons.ManagedAddon.streamAddonInstanceId(manifestId: String): String =
|
||||
"addon:$manifestId:$manifestUrl"
|
||||
|
||||
private data class PluginProviderGroup(
|
||||
val addonId: String,
|
||||
val addonName: String,
|
||||
|
|
|
|||
Loading…
Reference in a new issue