Refactor in cloudflare interceptor and quickjs dependency

This commit is contained in:
2026-05-03 09:18:43 +01:00
parent 9612df3d09
commit 737172fbde
10 changed files with 96 additions and 207 deletions

3
.gitignore vendored
View File

@@ -29,4 +29,5 @@ build/
.vscode/
### Mac OS ###
.DS_Store
.DS_Store
/aniyomi-extensions/

View File

@@ -63,19 +63,7 @@
<version>4.6.0</version>
</dependency>
<!-- JavaScript Engine -->
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>polyglot</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.graalvm.js</groupId>-->
<!-- <artifactId>js-scriptengine</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.graalvm.js</groupId>-->
<!-- <artifactId>js-language</artifactId>-->
<!-- </dependency>-->
<!-- JavaScript Engine (provided by aniyomibridge-core module) -->
<!-- RxJava -->
<dependency>

View File

@@ -135,6 +135,13 @@ public class WebView {
);
webViewClient.onReceivedError(this, request, error);
// Sync cookies back so CloudflareInterceptor can evaluate cf_clearance
syncCookiesFromHtmlUnitToAndroid(url);
// Notify page finished — Android's real WebView calls this even on error,
// and CloudflareInterceptor relies on it to count down the latch.
webViewClient.onPageFinished(this, url);
}
}

View File

@@ -93,33 +93,13 @@
<dependency>
<groupId>io.github.dokar3</groupId>
<artifactId>quickjs-kt-jvm</artifactId>
<version>1.0.0-alpha11</version>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>dev.datlag.jsunpacker</groupId>
<artifactId>jsunpacker-jvm</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>polyglot</artifactId>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-language</artifactId>
</dependency>
<dependency>
<groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId>
</dependency>
<dependency>
<groupId>org.graalvm.truffle</groupId>
<artifactId>truffle-api</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
@@ -178,7 +158,7 @@
<plugin>kotlinx-serialization</plugin>
</compilerPlugins>
<args>
<arg>-Xcontext-receivers</arg>
<arg>-Xcontext-parameters</arg>
</args>
</configuration>
<dependencies>

View File

@@ -1,98 +0,0 @@
package app.cash.quickjs;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.PolyglotAccess;
import org.graalvm.polyglot.Value;
import java.io.Closeable;
import java.math.BigInteger;
import java.util.Arrays;
public final class QuickJs implements Closeable {
private Context context;
public static QuickJs create() {
return new QuickJs();
}
public QuickJs() {
this.context = Context
.newBuilder("js")
.allowHostAccess(HostAccess.ALL)
.allowPolyglotAccess(PolyglotAccess.NONE)
.allowHostClassLoading(false)
.build();
context.enter();
}
public Object evaluate(String script, String ignoredFileName) {
return this.evaluate(script);
}
public Object evaluate(String script) {
Value value = context.eval("js", script);
return translateType(value);
}
private Object translateType(Value obj) {
if (obj.isBoolean()) {
return obj.asBoolean();
} else if (obj.hasArrayElements()) {
if (obj.getArraySize() == 0) {
return new int[0];
} else {
Value element = obj.getArrayElement(0);
if (element.isBoolean()) {
return obj.as(boolean[].class);
} else if (element.isNumber()) {
if (element.fitsInInt()) {
return obj.as(int[].class);
} else if (element.fitsInBigInteger()) {
return Arrays.stream(obj.as(BigInteger[].class)).map(BigInteger::longValue).toArray();
} else {
return obj.as(double[].class);
}
} else if (element.isHostObject()) {
return obj.as(Object[].class);
} else if (element.isString()) {
return obj.as(String[].class);
}
}
} else if (obj.isNumber()) {
if (obj.fitsInInt()) {
return obj.asInt();
} else if (obj.fitsInBigInteger()) {
return obj.asBigInteger().longValue();
} else {
return obj.asDouble();
}
} else if (obj.isHostObject()) {
return obj.asHostObject();
} else if (obj.isString()) {
return obj.asString();
}
return obj;
}
public byte[] compile(String sourceCode, String ignoredFileName) {
return sourceCode.getBytes();
}
public Object execute(byte[] bytecode) {
return this.evaluate(new String(bytecode));
}
public <T> void set(String name, Class<T> ignoredType, T object) {
context.getBindings("js").putMember(name, object);
}
@Override
public void close() {
if (this.context != null) {
this.context.leave();
this.context.close();
this.context = null;
}
}
}

View File

@@ -1,18 +0,0 @@
package dev.k3vinb5.aniyomibridge.loader.interceptors;
import eu.kanade.tachiyomi.multisrc.zorotheme.ZoroThemeFilters;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
public class HiAnimeInterceptor {
public static class ZoroThemeFiltersGetSearchParamsInterceptor {
@RuntimeType
public static Object intercept(@AllArguments Object[] args) {
// Create and return a new FilterSearchParams
return new ZoroThemeFilters.FilterSearchParams();
}
}
}

View File

@@ -0,0 +1,79 @@
package app.cash.quickjs
import com.dokar.quickjs.QuickJs as DokarQuickJs
import com.dokar.quickjs.binding.define
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import java.io.Closeable
/**
* Stub implementation of Cash App's QuickJS that delegates to dokar3's quickjs-kt-jvm
* (real QuickJS via JNI) instead of GraalVM.
*
* Binary-compatible with extensions that import app.cash.quickjs.QuickJs.
*/
class QuickJs private constructor() : Closeable {
private val quickJs: DokarQuickJs = DokarQuickJs.create(Dispatchers.Default)
companion object {
@JvmStatic
fun create(): QuickJs = QuickJs()
}
fun evaluate(script: String): Any? {
val result = runBlocking {
quickJs.evaluate<Any?>(script)
}
return translateValue(result)
}
fun evaluate(script: String, ignoredFileName: String): Any? {
return evaluate(script)
}
fun compile(sourceCode: String, ignoredFileName: String): ByteArray {
return quickJs.compile(sourceCode, ignoredFileName, asModule = false)
}
fun execute(bytecode: ByteArray): Any? {
val result = runBlocking {
quickJs.evaluate<Any?>(bytecode)
}
return translateValue(result)
}
fun <T> set(name: String, ignoredType: Class<T>, `object`: T) {
if (`object` == null) {
runBlocking {
quickJs.evaluate<Any?>("globalThis.$name = null")
}
} else {
quickJs.define(name, ignoredType, `object`)
}
}
override fun close() {
quickJs.close()
}
/**
* Translates dokar3 QuickJS return types to match the legacy GraalVM stub behavior.
* Most importantly: JS arrays were returned as primitive/Object arrays.
*/
private fun translateValue(value: Any?): Any? {
if (value !is List<*>) {
return value
}
if (value.isEmpty()) {
return emptyArray<Any?>()
}
return when {
value.all { it is Int } -> value.map { it as Int }.toIntArray()
value.all { it is Long } -> value.map { it as Long }.toLongArray()
value.all { it is Double } -> value.map { it as Double }.toDoubleArray()
value.all { it is Boolean } -> value.map { it as Boolean }.toBooleanArray()
value.all { it is String } -> value.map { it as String }.toTypedArray()
else -> value.toTypedArray()
}
}
}

View File

@@ -6,7 +6,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.okio.decodeFromBufferedSource
import okhttp3.Call
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaType
@@ -133,34 +133,12 @@ fun OkHttpClient.newCachelessCallWithProgress(request: Request, listener: Progre
}
//context(Json)
//inline fun <reified T> Response.parseAs(): T {
// return decodeFromJsonResponse<T>(serializer(), this)
//}
inline fun <reified T> Response.parseAs(): T {
// val bodyJson = this.body?.string() ?: ""
// return Gson().fromJson(bodyJson, T::class.java)
return json.decodeFromString(this.body?.string() ?: "{}")
}
inline fun <reified T> String.parseAs(): T {
// val bodyJson = this.body?.string() ?: ""
// return Gson().fromJson(bodyJson, T::class.java)
return json.decodeFromString(this ?: "{}")
}
context(Json)
@OptIn(ExperimentalSerializationApi::class)
fun <T> decodeFromJsonResponse(
deserializer: DeserializationStrategy<T>,
response: Response,
): T {
return response.body!!.source().use {
decodeFromBufferedSource(deserializer, it)
}
return json.decodeFromString(this)
}
class HttpException(val code: Int) : IllegalStateException("HTTP error $code")

32
pom.xml
View File

@@ -21,11 +21,10 @@
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.version>2.1.0</kotlin.version>
<kotlin.version>2.3.20</kotlin.version>
<kotlinx.serialization.version>1.7.3</kotlinx.serialization.version>
<kotlinx.coroutines.version>1.8.1</kotlinx.coroutines.version>
<kotlinx.coroutines.version>1.10.2</kotlinx.coroutines.version>
<okhttp.version>5.0.0-alpha.14</okhttp.version>
<graalvm.version>24.2.0</graalvm.version>
</properties>
<dependencyManagement>
@@ -73,33 +72,6 @@
<artifactId>okhttp-brotli</artifactId>
<version>${okhttp.version}</version>
</dependency>
<!-- GraalVM -->
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>polyglot</artifactId>
<version>${graalvm.version}</version>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
<version>${graalvm.version}</version>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-language</artifactId>
<version>${graalvm.version}</version>
</dependency>
<dependency>
<groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId>
<version>${graalvm.version}</version>
</dependency>
<dependency>
<groupId>org.graalvm.truffle</groupId>
<artifactId>truffle-api</artifactId>
<version>${graalvm.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -1 +1 @@
CP_FILE=$(mktemp) && mvn -pl aniyomibridge-core -q dependency:build-classpath -Dmdep.outputFile="$CP_FILE" && java -cp "aniyomibridge-core/target/classes:$(cat "$CP_FILE")" dev.k3vinb5.aniyomibridge.Main; rm -f "$CP_FILE"´
CP_FILE=$(mktemp) && mvn -pl aniyomibridge-core -q dependency:build-classpath -Dmdep.outputFile="$CP_FILE" && java -cp "aniyomibridge-core/target/classes:$(cat "$CP_FILE")" dev.k3vinb5.aniyomibridge.Main; rm -f "$CP_FILE"