From 435116bdae57825d90b81b5161a150c0be672967 Mon Sep 17 00:00:00 2001 From: ryslavy Date: Sat, 4 Jul 2026 22:52:52 +0200 Subject: [PATCH] bypassing unnecessary P2P prompts This PR introduces support for `clientResolve` in JavaScript plugins and allows them to be properly evaluated as Direct Debrid candidates, bypassing unnecessary P2P prompts. Previously, plugins that implemented their own Debrid cache checks (e.g., verifying TorBox/Real-Debrid cache) had no way to pass this metadata to Nuvio. As a result, Nuvio would treat these streams as raw magnets/torrents (`needsLocalDebridResolve`) and trigger a P2P warning prompt, instead of seamlessly routing them through the `DirectDebridResolver`. --- .../com/nuvio/app/features/plugins/PluginModels.kt | 1 + .../app/features/streams/StreamFetchSupport.kt | 1 + .../com/nuvio/app/features/streams/StreamModels.kt | 2 +- .../app/features/plugins/runtime/PluginRuntime.kt | 14 ++++++++++++-- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/plugins/PluginModels.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/plugins/PluginModels.kt index 98769a634..e4d9916f9 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/plugins/PluginModels.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/plugins/PluginModels.kt @@ -79,6 +79,7 @@ data class PluginRuntimeResult( val infoHash: String? = null, val headers: Map? = null, val subtitles: List? = null, + val clientResolve: com.nuvio.app.features.streams.StreamClientResolve? = null, ) @Serializable diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamFetchSupport.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamFetchSupport.kt index 5bfa30be3..b04ce1e56 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamFetchSupport.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamFetchSupport.kt @@ -116,6 +116,7 @@ internal fun PluginRuntimeResult.toStreamItem( addonName = addonName, addonId = addonId, streamType = normalizeStreamType(type), + clientResolve = clientResolve, behaviorHints = if (requestHeaders.isEmpty()) { StreamBehaviorHints() } else { 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 a16a172b2..7e20c6ad9 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 @@ -64,7 +64,7 @@ data class StreamItem( get() = clientResolve?.isDirectDebridCandidate == true val isInstalledAddonStream: Boolean - get() = addonId.startsWith("addon:") + get() = addonId.startsWith("addon:") || addonId.startsWith("plugin:") val isTorrentStream: Boolean get() = !isDirectDebridStream && ( diff --git a/composeApp/src/fullCommonMain/kotlin/com/nuvio/app/features/plugins/runtime/PluginRuntime.kt b/composeApp/src/fullCommonMain/kotlin/com/nuvio/app/features/plugins/runtime/PluginRuntime.kt index fc014ee7b..cf433e7f2 100644 --- a/composeApp/src/fullCommonMain/kotlin/com/nuvio/app/features/plugins/runtime/PluginRuntime.kt +++ b/composeApp/src/fullCommonMain/kotlin/com/nuvio/app/features/plugins/runtime/PluginRuntime.kt @@ -229,10 +229,19 @@ internal object PluginRuntime { ) }?.takeIf { it.isNotEmpty() } + val clientResolve = (item["clientResolve"] as? JsonObject)?.let { resolveObj -> + com.nuvio.app.features.streams.StreamClientResolve( + type = resolveObj["type"]?.jsonPrimitive?.contentOrNull, + infoHash = resolveObj["infoHash"]?.jsonPrimitive?.contentOrNull, + service = resolveObj["service"]?.jsonPrimitive?.contentOrNull, + isCached = resolveObj["isCached"]?.jsonPrimitive?.contentOrNull?.toBoolean() ?: resolveObj["isCached"]?.jsonPrimitive?.booleanOrNull + ) + } + PluginRuntimeResult( title = item.stringOrNull("title") ?: item.stringOrNull("name") ?: runBlocking { getString(Res.string.generic_unknown) }, name = item.stringOrNull("name"), - url = url, + url = url ?: "", // Default to empty if missing but clientResolve is present quality = item.stringOrNull("quality"), size = item.stringOrNull("size"), language = item.stringOrNull("language"), @@ -243,8 +252,9 @@ internal object PluginRuntime { infoHash = item.stringOrNull("infoHash"), headers = headers, subtitles = subtitles, + clientResolve = clientResolve, ) - }.filter { it.url.isNotBlank() } + }.filter { it.url.isNotBlank() || it.clientResolve != null } }.getOrElse { emptyList() } }