From 1a33ede27987f3df71678ca59bf8d3b1ab205201 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 20:04:50 -0600 Subject: [PATCH 01/11] Further simplification --- .../com/lagradost/cloudstream3/extractors/StreamTape.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt index fb11e334de7..94f1dfdc2c6 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt @@ -3,8 +3,8 @@ package com.lagradost.cloudstream3.extractors import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.JsContext import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.evalJs import com.lagradost.cloudstream3.utils.newExtractorLink class Watchadsontape : StreamTape() { @@ -33,11 +33,8 @@ open class StreamTape : ExtractorApi() { val result = this.document.select("script").firstOrNull { it.html().contains("botlink').innerHTML") } ?.html()?.lines()?.firstOrNull { it.contains("botlink').innerHTML") }?.let { - val scriptContent = - it.substringAfter(").innerHTML").replaceFirst("=", "var url =") - val ctx = JsContext() - ctx.eval(scriptContent) - ctx["url"]?.toString() ?: "" + val scriptContent = it.substringAfter(").innerHTML").replaceFirst("=", "var url =") + evalJs(scriptContent)?.toString() } if (!result.isNullOrEmpty()) { From 259e96d5b9ea1e6ac9d82f5e9468269ca69f233b Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 20:06:50 -0600 Subject: [PATCH 02/11] Fix --- .../kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt index 94f1dfdc2c6..33ef150ecad 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt @@ -33,7 +33,7 @@ open class StreamTape : ExtractorApi() { val result = this.document.select("script").firstOrNull { it.html().contains("botlink').innerHTML") } ?.html()?.lines()?.firstOrNull { it.contains("botlink').innerHTML") }?.let { - val scriptContent = it.substringAfter(").innerHTML").replaceFirst("=", "var url =") + val scriptContent = it.substringAfter(").innerHTML").replaceFirst("=", "var url =") + "; url" evalJs(scriptContent)?.toString() } From cb693335ae841dbda3e3153275534e239230939c Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 20:32:02 -0600 Subject: [PATCH 03/11] Update --- .../cloudstream3/utils/JsInterpreter.kt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt index e20dd00beb6..3da2169df23 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt @@ -64,11 +64,22 @@ class JsContext { } /** - * Evaluate [js] and return its last value. Convenience wrapper for one-shot - * evaluations, equivalent to the old `rhino.evaluateString(scope, js, ...)`. + * Evaluate [js] and return its last value, or the value of [variable] if specified. + * Convenience wrapper for one-shot evaluations, equivalent to the old + * `rhino.evaluateString(scope, js, ...)`. + * + * @param js The JavaScript code to evaluate. + * @param variable Optional variable name to retrieve from the scope after evaluation. + * @return A [Result] wrapping the last expression value, or the named variable value. + * Use [Result.getOrNull] to get the value or null on failure, + * or [Result.getOrThrow] to rethrow any exception. */ @Prerelease -fun evalJs(js: String): Any? = JsInterpreter().eval(js) +fun evalJs(js: String, variable: String? = null): Result = runCatching { + val interpreter = JsInterpreter() + val result = interpreter.eval(js) + if (variable != null) interpreter.getVar(variable) else result +} private enum class TT { NUMBER, STRING, IDENT, PLUS, MINUS, STAR, SLASH, PERCENT, EQ, EQEQ, EQEQEQ, From a8b1226f696e854b66623a9692c8d06128970e00 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 20:32:48 -0600 Subject: [PATCH 04/11] Update --- .../com/lagradost/cloudstream3/extractors/StreamTape.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt index 33ef150ecad..1bf6a45da35 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt @@ -33,8 +33,8 @@ open class StreamTape : ExtractorApi() { val result = this.document.select("script").firstOrNull { it.html().contains("botlink').innerHTML") } ?.html()?.lines()?.firstOrNull { it.contains("botlink').innerHTML") }?.let { - val scriptContent = it.substringAfter(").innerHTML").replaceFirst("=", "var url =") + "; url" - evalJs(scriptContent)?.toString() + val scriptContent = it.substringAfter(").innerHTML").replaceFirst("=", "var url =") + evalJs(scriptContent, "url").getOrNull()?.toString() } if (!result.isNullOrEmpty()) { From 7c29fa7c4dead9f5c27ec9d9162ff73bafb248b9 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 20:36:29 -0600 Subject: [PATCH 05/11] Update --- .../kotlin/com/lagradost/cloudstream3/extractors/Userload.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Userload.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Userload.kt index 5068b169b2d..ee8c95bf874 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Userload.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Userload.kt @@ -30,7 +30,7 @@ open class Userload : ExtractorApi() { } private fun evaluateMath(mathExpression: String): String { - return jsValueToString(evalJs("eval($mathExpression)")) + return jsValueToString(evalJs("eval($mathExpression)").getOrNull()) } private fun decodeVideoJs(text: String): List { From 36eaad34d744eb1162bc12791476044929cbfd92 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 21:07:20 -0600 Subject: [PATCH 06/11] - --- .../kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt index 1bf6a45da35..97967486372 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/StreamTape.kt @@ -34,7 +34,7 @@ open class StreamTape : ExtractorApi() { this.document.select("script").firstOrNull { it.html().contains("botlink').innerHTML") } ?.html()?.lines()?.firstOrNull { it.contains("botlink').innerHTML") }?.let { val scriptContent = it.substringAfter(").innerHTML").replaceFirst("=", "var url =") - evalJs(scriptContent, "url").getOrNull()?.toString() + evalJs(scriptContent, "url")?.toString() } if (!result.isNullOrEmpty()) { From 8cfb5c74b9f4ac7f951a7baf4f8b035d86a4f16f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 21:07:55 -0600 Subject: [PATCH 07/11] - --- .../kotlin/com/lagradost/cloudstream3/extractors/Userload.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Userload.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Userload.kt index ee8c95bf874..5068b169b2d 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Userload.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Userload.kt @@ -30,7 +30,7 @@ open class Userload : ExtractorApi() { } private fun evaluateMath(mathExpression: String): String { - return jsValueToString(evalJs("eval($mathExpression)").getOrNull()) + return jsValueToString(evalJs("eval($mathExpression)")) } private fun decodeVideoJs(text: String): List { From 7fa4502909ffb80580b0d8daf54a9d67a3763bbf Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 21:08:33 -0600 Subject: [PATCH 08/11] Update --- .../com/lagradost/cloudstream3/utils/JsInterpreter.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt index 3da2169df23..9ce0c1460eb 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt @@ -70,12 +70,12 @@ class JsContext { * * @param js The JavaScript code to evaluate. * @param variable Optional variable name to retrieve from the scope after evaluation. - * @return A [Result] wrapping the last expression value, or the named variable value. - * Use [Result.getOrNull] to get the value or null on failure, - * or [Result.getOrThrow] to rethrow any exception. + * @return The last expression value, or the named variable value if [variable] is specified. + * Returns [Unit] on evaluation failure. Note that JS null is represented as Kotlin null, + * and JS undefined is represented as [Unit]. */ @Prerelease -fun evalJs(js: String, variable: String? = null): Result = runCatching { +fun evalJs(js: String, variable: String? = null): Any? { val interpreter = JsInterpreter() val result = interpreter.eval(js) if (variable != null) interpreter.getVar(variable) else result From 2869f3e0ad53d05c18731710cb66c4c03b9eaca0 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 21:11:37 -0600 Subject: [PATCH 09/11] Comment --- .../com/lagradost/cloudstream3/utils/JsInterpreter.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt index 9ce0c1460eb..1dfb0906d5a 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsInterpreter.kt @@ -71,14 +71,14 @@ class JsContext { * @param js The JavaScript code to evaluate. * @param variable Optional variable name to retrieve from the scope after evaluation. * @return The last expression value, or the named variable value if [variable] is specified. - * Returns [Unit] on evaluation failure. Note that JS null is represented as Kotlin null, - * and JS undefined is represented as [Unit]. + * Returns [Unit] on evaluation failure or when the result is JS undefined. + * JS null is represented as Kotlin null. Use [jsValueToString] to convert to a JS string. */ @Prerelease fun evalJs(js: String, variable: String? = null): Any? { val interpreter = JsInterpreter() val result = interpreter.eval(js) - if (variable != null) interpreter.getVar(variable) else result + return if (variable != null) interpreter.getVar(variable) else result } private enum class TT { From 98cc3b0ef92c6f25be2ac973fe47643edff738f7 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 25 May 2026 10:50:11 -0600 Subject: [PATCH 10/11] Add tests --- .../cloudstream3/utils/JsInterpreterTest.kt | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt index f4ff6c274a7..9c39bcbaa76 100644 --- a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt +++ b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.utils +import kotlin.math.abs import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull @@ -13,7 +14,7 @@ class JsInterpreterTest { private fun bool(code: String) = evalJs(code) as? Boolean ?: false private fun assertApprox(expected: Double, actual: Double, tol: Double = 1e-9) { - assertTrue(kotlin.math.abs(actual - expected) <= tol, "Expected $expected ± $tol but was $actual") + assertTrue(abs(actual - expected) <= tol, "Expected $expected ± $tol but was $actual") } @Test @@ -726,7 +727,7 @@ class JsInterpreterTest { } @Test - fun urlExtraction() { + fun jsContextUrlExtractionPattern() { val scriptContent = "var url = '/e/abc123?t=' + (1000+337) + '&s=xyz'" val ctx = JsContext() ctx.eval(scriptContent) @@ -749,6 +750,48 @@ class JsInterpreterTest { assertEquals(65.0, (evalJs(code) as? Double) ?: 0.0) } + @Test + fun evalJsWithVariableReturnsNamedVar() { + val result = evalJs("var x = 42", "x") + assertEquals(42.0, result as? Double) + } + + @Test + fun evalJsWithVariableReturnsNullForUndefined() { + val result = evalJs("var x = 42", "y") + assertNull(result) + } + + @Test + fun evalJsWithVariableAfterComputation() { + val result = evalJs("var x = 1 + 2 * 3", "x") + assertEquals(7.0, result as? Double) + } + + @Test + fun evalJsWithVariableStringValue() { + val result = evalJs("var url = 'https://example.com'", "url") + assertEquals("https://example.com", result as? String) + } + + @Test + fun evalJsWithVariableNullValue() { + val result = evalJs("var x = null", "x") + assertNull(result) + } + + @Test + fun evalJsWithVariableUnitWhenNoVariable() { + val result = evalJs("var x = 42") + assertEquals(Unit, result) + } + + @Test + fun evalJsWithVariableAfterMultipleStatements() { + val result = evalJs("var x = 0; for(var i=1;i<=5;i++){x+=i}", "x") + assertEquals(15.0, result as? Double) + } + @Test fun hexEncodedStringDecoding() { val code = """ From 0c069ca7868733549b16eff12cd19c4eb623ca49 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 25 May 2026 11:06:25 -0600 Subject: [PATCH 11/11] Update --- .../cloudstream3/utils/JsInterpreterTest.kt | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt index 9c39bcbaa76..20b46acb93d 100644 --- a/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt +++ b/library/src/commonTest/kotlin/com/lagradost/cloudstream3/utils/JsInterpreterTest.kt @@ -9,9 +9,9 @@ import kotlin.test.assertFalse class JsInterpreterTest { - private fun num(code: String) = (evalJs(code) as? Double) ?: Double.NaN - private fun str(code: String) = jsValueToString(evalJs(code)) - private fun bool(code: String) = evalJs(code) as? Boolean ?: false + private fun num(code: String, variable: String? = null) = (evalJs(code, variable) as? Double) ?: Double.NaN + private fun str(code: String, variable: String? = null) = jsValueToString(evalJs(code, variable)) + private fun bool(code: String, variable: String? = null) = evalJs(code, variable) as? Boolean ?: false private fun assertApprox(expected: Double, actual: Double, tol: Double = 1e-9) { assertTrue(abs(actual - expected) <= tol, "Expected $expected ± $tol but was $actual") @@ -752,44 +752,37 @@ class JsInterpreterTest { @Test fun evalJsWithVariableReturnsNamedVar() { - val result = evalJs("var x = 42", "x") - assertEquals(42.0, result as? Double) - } - - @Test - fun evalJsWithVariableReturnsNullForUndefined() { - val result = evalJs("var x = 42", "y") - assertNull(result) + assertEquals(42.0, num("var x = 42", "x")) } @Test fun evalJsWithVariableAfterComputation() { - val result = evalJs("var x = 1 + 2 * 3", "x") - assertEquals(7.0, result as? Double) + assertEquals(7.0, num("var x = 1 + 2 * 3", "x")) } @Test fun evalJsWithVariableStringValue() { - val result = evalJs("var url = 'https://example.com'", "url") - assertEquals("https://example.com", result as? String) + assertEquals("https://example.com", str("var url = 'https://example.com'", "url")) } @Test fun evalJsWithVariableNullValue() { - val result = evalJs("var x = null", "x") - assertNull(result) + assertNull(evalJs("var x = null", "x")) + } + + @Test + fun evalJsWithVariableReturnsNullForUndefined() { + assertNull(evalJs("var x = 42", "y")) } @Test fun evalJsWithVariableUnitWhenNoVariable() { - val result = evalJs("var x = 42") - assertEquals(Unit, result) + assertEquals(Unit, evalJs("var x = 42")) } @Test fun evalJsWithVariableAfterMultipleStatements() { - val result = evalJs("var x = 0; for(var i=1;i<=5;i++){x+=i}", "x") - assertEquals(15.0, result as? Double) + assertEquals(15.0, num("var x = 0; for(var i=1;i<=5;i++){x+=i}", "x")) } @Test