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 bdf8f0ece..ef21c1c65 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 @@ -48,6 +48,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -191,6 +192,12 @@ fun StreamsScreen( ) } + DisposableEffect(type, videoId, seasonNumber, episodeNumber, manualSelection) { + onDispose { + StreamsRepository.clear() + } + } + LaunchedEffect(uiState.groups, storedProgress?.providerAddonId, preferredFilterApplied) { if (preferredFilterApplied) return@LaunchedEffect val preferredAddonId = storedProgress?.providerAddonId ?: return@LaunchedEffect diff --git a/composeApp/src/fullCommonMain/kotlin/com/nuvio/app/features/plugins/PluginRuntime.kt b/composeApp/src/fullCommonMain/kotlin/com/nuvio/app/features/plugins/PluginRuntime.kt index 3a003d8ef..c749c63b4 100644 --- a/composeApp/src/fullCommonMain/kotlin/com/nuvio/app/features/plugins/PluginRuntime.kt +++ b/composeApp/src/fullCommonMain/kotlin/com/nuvio/app/features/plugins/PluginRuntime.kt @@ -22,13 +22,13 @@ import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.jsonPrimitive -import kotlinx.coroutines.runBlocking import nuvio.composeapp.generated.resources.Res import nuvio.composeapp.generated.resources.generic_unknown import org.jetbrains.compose.resources.getString import kotlin.random.Random private const val PLUGIN_TIMEOUT_MS = 60_000L +private const val SLOW_PLUGIN_FETCH_MS = 2_000L private const val MAX_FETCH_BODY_CHARS = 256 * 1024 private const val MAX_FETCH_HEADER_VALUE_CHARS = 8 * 1024 private const val FETCH_TRUNCATION_SUFFIX = "\n...[truncated]" @@ -49,7 +49,7 @@ internal object PluginRuntime { episode: Int?, scraperId: String, scraperSettings: Map = emptyMap(), - ): List = withContext(Dispatchers.Default) { + ): List = withContext(Dispatchers.IO) { withTimeout(PLUGIN_TIMEOUT_MS) { executePluginInternal( code = code, @@ -78,7 +78,7 @@ internal object PluginRuntime { var resultJson = "[]" try { - quickJs(Dispatchers.Default) { + quickJs(Dispatchers.IO) { define("console") { function("log") { args -> log.d { "Plugin:$scraperId ${args.joinToString(" ") { it?.toString() ?: "null" }}" } @@ -328,7 +328,8 @@ internal object PluginRuntime { headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" } - val response = runBlocking { + val startedAt = kotlin.time.TimeSource.Monotonic.markNow() + val response = runBlocking(Dispatchers.IO) { httpRequestRaw( method = method, url = url, @@ -337,6 +338,10 @@ internal object PluginRuntime { followRedirects = followRedirects, ) } + val elapsed = startedAt.elapsedNow() + if (elapsed.inWholeMilliseconds >= SLOW_PLUGIN_FETCH_MS) { + log.w { "Slow plugin fetch $method $url status=${response.status} elapsed=$elapsed" } + } val responseHeaders = response.headers.mapValues { (_, value) -> truncateString(value, MAX_FETCH_HEADER_VALUE_CHARS)