Horis 2 лет назад
Родитель
Сommit
dd798012bd

+ 32 - 15
app/src/main/java/io/legado/app/help/JsExtensions.kt

@@ -5,6 +5,7 @@ import android.webkit.WebSettings
 import androidx.annotation.Keep
 import cn.hutool.core.codec.Base64
 import cn.hutool.core.util.HexUtil
+import com.script.rhino.RhinoContext
 import io.legado.app.constant.AppConst
 import io.legado.app.constant.AppConst.dateFormat
 import io.legado.app.constant.AppLog
@@ -50,6 +51,7 @@ import kotlinx.coroutines.runBlocking
 import okio.use
 import org.jsoup.Connection
 import org.jsoup.Jsoup
+import org.mozilla.javascript.Context
 import splitties.init.appCtx
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayOutputStream
@@ -65,6 +67,8 @@ import java.util.SimpleTimeZone
 import java.util.UUID
 import java.util.zip.ZipEntry
 import java.util.zip.ZipInputStream
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
 
 /**
  * js扩展类, 在js中通过java变量调用
@@ -78,6 +82,12 @@ interface JsExtensions : JsEncodeUtils {
 
     fun getSource(): BaseSource?
 
+    private val context: CoroutineContext
+        get() {
+            val rhinoContext = Context.getCurrentContext() as RhinoContext
+            return rhinoContext.coroutineContext ?: EmptyCoroutineContext
+        }
+
     /**
      * 访问网络,返回String
      */
@@ -87,9 +97,9 @@ interface JsExtensions : JsEncodeUtils {
         } else {
             url.toString()
         }
-        return runBlocking {
+        val analyzeUrl = AnalyzeUrl(urlStr, source = getSource(), coroutineContext = context)
+        return runBlocking(context) {
             kotlin.runCatching {
-                val analyzeUrl = AnalyzeUrl(urlStr, source = getSource())
                 analyzeUrl.getStrResponseAwait().body
             }.onFailure {
                 AppLog.put("ajax(${urlStr}) error\n${it.localizedMessage}", it)
@@ -103,7 +113,7 @@ interface JsExtensions : JsEncodeUtils {
      * 并发访问网络
      */
     fun ajaxAll(urlList: Array<String>): Array<StrResponse?> {
-        return runBlocking {
+        return runBlocking(context) {
             val asyncArray = Array(urlList.size) {
                 async(IO) {
                     val url = urlList[it]
@@ -122,7 +132,7 @@ interface JsExtensions : JsEncodeUtils {
      * 访问网络,返回Response<String>
      */
     fun connect(urlStr: String): StrResponse {
-        return runBlocking {
+        return runBlocking(context) {
             val analyzeUrl = AnalyzeUrl(urlStr, source = getSource())
             kotlin.runCatching {
                 analyzeUrl.getStrResponseAwait()
@@ -135,7 +145,7 @@ interface JsExtensions : JsEncodeUtils {
     }
 
     fun connect(urlStr: String, header: String?): StrResponse {
-        return runBlocking {
+        return runBlocking(context) {
             val headerMap = GSON.fromJsonObject<Map<String, String>>(header).getOrNull()
             val analyzeUrl = AnalyzeUrl(urlStr, headerMapF = headerMap, source = getSource())
             kotlin.runCatching {
@@ -156,7 +166,7 @@ interface JsExtensions : JsEncodeUtils {
      * @return 返回js获取的内容
      */
     fun webView(html: String?, url: String?, js: String?): String? {
-        return runBlocking {
+        return runBlocking(context) {
             BackstageWebView(
                 url = url,
                 html = html,
@@ -171,7 +181,7 @@ interface JsExtensions : JsEncodeUtils {
      * 使用webView获取资源url
      */
     fun webViewGetSource(html: String?, url: String?, js: String?, sourceRegex: String): String? {
-        return runBlocking {
+        return runBlocking(context) {
             BackstageWebView(
                 url = url,
                 html = html,
@@ -192,7 +202,7 @@ interface JsExtensions : JsEncodeUtils {
         js: String?,
         overrideUrlRegex: String
     ): String? {
-        return runBlocking {
+        return runBlocking(context) {
             BackstageWebView(
                 url = url,
                 html = html,
@@ -294,7 +304,7 @@ interface JsExtensions : JsEncodeUtils {
      * @return 下载的文件相对路径
      */
     fun downloadFile(url: String): String {
-        val analyzeUrl = AnalyzeUrl(url, source = getSource())
+        val analyzeUrl = AnalyzeUrl(url, source = getSource(), coroutineContext = context)
         val type = UrlUtil.getSuffix(url, analyzeUrl.type)
         val path = FileUtils.getPath(
             File(FileUtils.getCachePath()),
@@ -321,7 +331,8 @@ interface JsExtensions : JsEncodeUtils {
         ReplaceWith("downloadFile(url: String)")
     )
     fun downloadFile(content: String, url: String): String {
-        val type = AnalyzeUrl(url, source = getSource()).type ?: return ""
+        val type = AnalyzeUrl(url, source = getSource(), coroutineContext = context).type
+            ?: return ""
         val path = FileUtils.getPath(
             FileUtils.createFolderIfNotExist(FileUtils.getCachePath()),
             "${MD5Utils.md5Encode16(url)}.${type}"
@@ -689,7 +700,7 @@ interface JsExtensions : JsEncodeUtils {
      */
     fun getZipByteArrayContent(url: String, path: String): ByteArray? {
         val bytes = if (url.isAbsUrl()) {
-            AnalyzeUrl(url, source = getSource()).getByteArray()
+            AnalyzeUrl(url, source = getSource(), coroutineContext = context).getByteArray()
         } else {
             HexUtil.decodeHex(url)
         }
@@ -717,7 +728,7 @@ interface JsExtensions : JsEncodeUtils {
      */
     fun getRarByteArrayContent(url: String, path: String): ByteArray? {
         val bytes = if (url.isAbsUrl()) {
-            AnalyzeUrl(url, source = getSource()).getByteArray()
+            AnalyzeUrl(url, source = getSource(), coroutineContext = context).getByteArray()
         } else {
             HexUtil.decodeHex(url)
         }
@@ -735,7 +746,7 @@ interface JsExtensions : JsEncodeUtils {
      */
     fun get7zByteArrayContent(url: String, path: String): ByteArray? {
         val bytes = if (url.isAbsUrl()) {
-            AnalyzeUrl(url, source = getSource()).getByteArray()
+            AnalyzeUrl(url, source = getSource(), coroutineContext = context).getByteArray()
         } else {
             HexUtil.decodeHex(url)
         }
@@ -769,14 +780,20 @@ interface JsExtensions : JsEncodeUtils {
             when (data) {
                 is String -> {
                     if (useCache) {
-                        key = MessageDigest.getInstance("SHA-256").digest(data.toByteArray()).toHexString()
+                        key = MessageDigest.getInstance("SHA-256").digest(data.toByteArray())
+                            .toHexString()
                         qTTF = CacheManager.getQueryTTF(key)
                         if (qTTF != null) return qTTF
                     }
                     val font: ByteArray? = when {
                         data.isContentScheme() -> Uri.parse(data).readBytes(appCtx)
                         data.startsWith("/storage") -> File(data).readBytes()
-                        data.isAbsUrl() -> AnalyzeUrl(data, source = getSource()).getByteArray()
+                        data.isAbsUrl() -> AnalyzeUrl(
+                            data,
+                            source = getSource(),
+                            coroutineContext = context
+                        ).getByteArray()
+
                         else -> base64DecodeToByteArray(data)
                     }
                     font ?: return null

+ 15 - 8
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt

@@ -32,7 +32,9 @@ import java.util.regex.Pattern
 import kotlin.collections.component1
 import kotlin.collections.component2
 import kotlin.collections.set
+import kotlin.coroutines.ContinuationInterceptor
 import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
 
 /**
  * 解析规则获取结果
@@ -67,7 +69,7 @@ class AnalyzeRule(
 
     private val stringRuleCache = hashMapOf<String, List<SourceRule>>()
 
-    private var coroutineContext: CoroutineContext? = null
+    private var coroutineContext: CoroutineContext = EmptyCoroutineContext
 
     @JvmOverloads
     fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule {
@@ -84,8 +86,8 @@ class AnalyzeRule(
         return this
     }
 
-    fun setCoroutineContext(context: CoroutineContext?): AnalyzeRule {
-        coroutineContext = context
+    fun setCoroutineContext(context: CoroutineContext): AnalyzeRule {
+        coroutineContext = context.minusKey(ContinuationInterceptor)
         return this
     }
 
@@ -777,9 +779,14 @@ class AnalyzeRule(
         } else {
             url.toString()
         }
-        return runBlocking {
+        val analyzeUrl = AnalyzeUrl(
+            urlStr,
+            source = source,
+            ruleData = book,
+            coroutineContext = coroutineContext
+        )
+        return runBlocking(coroutineContext) {
             kotlin.runCatching {
-                val analyzeUrl = AnalyzeUrl(urlStr, source = source, ruleData = book)
                 analyzeUrl.getStrResponseAwait().body
             }.onFailure {
                 log("ajax(${urlStr}) error\n${it.stackTraceToString()}")
@@ -797,7 +804,7 @@ class AnalyzeRule(
         val bookSource = source as? BookSource
         val book = book as? Book
         if (bookSource == null || book == null) return
-        runBlocking {
+        runBlocking(coroutineContext) {
             withTimeout(1800000) {
                 WebBook.preciseSearchAwait(this, bookSource, book.name, book.author)
                     .getOrThrow().let {
@@ -818,7 +825,7 @@ class AnalyzeRule(
         val bookSource = source as? BookSource
         val book = book as? Book
         if (bookSource == null || book == null) return
-        runBlocking {
+        runBlocking(coroutineContext) {
             withTimeout(1800000) {
                 WebBook.getBookInfoAwait(bookSource, book)
             }
@@ -832,7 +839,7 @@ class AnalyzeRule(
         val bookSource = source as? BookSource
         val book = book as? Book
         if (bookSource == null || book == null) return
-        runBlocking {
+        runBlocking(coroutineContext) {
             withTimeout(1800000) {
                 WebBook.getBookInfoAwait(bookSource, book)
             }

+ 10 - 5
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt

@@ -35,6 +35,9 @@ import java.io.InputStream
 import java.net.URLEncoder
 import java.util.concurrent.TimeUnit
 import java.util.regex.Pattern
+import kotlin.coroutines.ContinuationInterceptor
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.math.max
 
 /**
@@ -55,6 +58,7 @@ class AnalyzeUrl(
     private val ruleData: RuleDataInterface? = null,
     private val chapter: BookChapter? = null,
     private val readTimeout: Long? = null,
+    private var coroutineContext: CoroutineContext = EmptyCoroutineContext,
     headerMapF: Map<String, String>? = null,
 ) : JsExtensions {
     companion object {
@@ -89,6 +93,7 @@ class AnalyzeUrl(
         private set
 
     init {
+        coroutineContext = coroutineContext.minusKey(ContinuationInterceptor)
         val urlMatcher = paramPattern.matcher(baseUrl)
         if (urlMatcher.find()) baseUrl = baseUrl.substring(0, urlMatcher.start())
         (headerMapF ?: source?.getHeaderMap(true))?.let {
@@ -278,7 +283,7 @@ class AnalyzeUrl(
         source?.getShareScope()?.let {
             scope.prototype = it
         }
-        return RhinoScriptEngine.eval(jsStr, scope)
+        return RhinoScriptEngine.eval(jsStr, scope, coroutineContext)
     }
 
     fun put(key: String, value: String): String {
@@ -476,7 +481,7 @@ class AnalyzeUrl(
         sourceRegex: String? = null,
         useWebView: Boolean = true,
     ): StrResponse {
-        return runBlocking {
+        return runBlocking(coroutineContext) {
             getStrResponseAwait(jsStr, sourceRegex, useWebView)
         }
     }
@@ -527,7 +532,7 @@ class AnalyzeUrl(
     }
 
     fun getResponse(): Response {
-        return runBlocking {
+        return runBlocking(coroutineContext) {
             getResponseAwait()
         }
     }
@@ -553,7 +558,7 @@ class AnalyzeUrl(
     }
 
     fun getByteArray(): ByteArray {
-        return runBlocking {
+        return runBlocking(coroutineContext) {
             getByteArrayAwait()
         }
     }
@@ -569,7 +574,7 @@ class AnalyzeUrl(
     }
 
     fun getInputStream(): InputStream {
-        return runBlocking {
+        return runBlocking(coroutineContext) {
             getInputStreamAwait()
         }
     }

+ 10 - 6
app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt

@@ -63,12 +63,14 @@ object BookChapterList {
                 var nextUrl = chapterData.second[0]
                 while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) {
                     nextUrlList.add(nextUrl)
-                    val res = AnalyzeUrl(
+                    val analyzeUrl = AnalyzeUrl(
                         mUrl = nextUrl,
                         source = bookSource,
                         ruleData = book,
-                        headerMapF = bookSource.getHeaderMap()
-                    ).getStrResponseAwait() //控制并发访问
+                        headerMapF = bookSource.getHeaderMap(),
+                        coroutineContext = coroutineContext
+                    )
+                    val res = analyzeUrl.getStrResponseAwait() //控制并发访问
                     res.body?.let { nextBody ->
                         chapterData = analyzeChapterList(
                             book, nextUrl, nextUrl,
@@ -91,12 +93,14 @@ object BookChapterList {
                         emit(urlStr)
                     }
                 }.mapAsync(AppConfig.threadCount) { urlStr ->
-                    val res = AnalyzeUrl(
+                    val analyzeUrl = AnalyzeUrl(
                         mUrl = urlStr,
                         source = bookSource,
                         ruleData = book,
-                        headerMapF = bookSource.getHeaderMap()
-                    ).getStrResponseAwait() //控制并发访问
+                        headerMapF = bookSource.getHeaderMap(),
+                        coroutineContext = coroutineContext
+                    )
+                    val res = analyzeUrl.getStrResponseAwait() //控制并发访问
                     analyzeChapterList(
                         book, urlStr, res.url,
                         res.body!!, tocRule, listRule, bookSource, false

+ 10 - 6
app/src/main/java/io/legado/app/model/webBook/BookContent.kt

@@ -86,12 +86,14 @@ object BookContent {
                 ) break
                 nextUrlList.add(nextUrl)
                 coroutineContext.ensureActive()
-                val res = AnalyzeUrl(
+                val analyzeUrl = AnalyzeUrl(
                     mUrl = nextUrl,
                     source = bookSource,
                     ruleData = book,
-                    headerMapF = bookSource.getHeaderMap()
-                ).getStrResponseAwait() //控制并发访问
+                    headerMapF = bookSource.getHeaderMap(),
+                    coroutineContext = coroutineContext
+                )
+                val res = analyzeUrl.getStrResponseAwait() //控制并发访问
                 res.body?.let { nextBody ->
                     contentData = analyzeContent(
                         book, nextUrl, res.url, nextBody, contentRule,
@@ -112,12 +114,14 @@ object BookContent {
                     emit(urlStr)
                 }
             }.mapAsync(AppConfig.threadCount) { urlStr ->
-                val res = AnalyzeUrl(
+                val analyzeUrl = AnalyzeUrl(
                     mUrl = urlStr,
                     source = bookSource,
                     ruleData = book,
-                    headerMapF = bookSource.getHeaderMap()
-                ).getStrResponseAwait() //控制并发访问
+                    headerMapF = bookSource.getHeaderMap(),
+                    coroutineContext = coroutineContext
+                )
+                val res = analyzeUrl.getStrResponseAwait() //控制并发访问
                 analyzeContent(
                     book, urlStr, res.url, res.body!!, contentRule,
                     bookChapter, bookSource, mNextChapterUrl,

+ 9 - 4
app/src/main/java/io/legado/app/model/webBook/WebBook.kt

@@ -61,6 +61,7 @@ object WebBook {
             headerMapF = bookSource.getHeaderMap(true),
             source = bookSource,
             ruleData = ruleData,
+            coroutineContext = coroutineContext
         )
         var res = analyzeUrl.getStrResponseAwait()
         //检测书源是否已登录
@@ -107,7 +108,8 @@ object WebBook {
             baseUrl = bookSource.bookSourceUrl,
             source = bookSource,
             ruleData = ruleData,
-            headerMapF = bookSource.getHeaderMap(true)
+            headerMapF = bookSource.getHeaderMap(true),
+            coroutineContext = coroutineContext
         )
         var res = analyzeUrl.getStrResponseAwait()
         //检测书源是否已登录
@@ -164,7 +166,8 @@ object WebBook {
                 baseUrl = bookSource.bookSourceUrl,
                 source = bookSource,
                 ruleData = book,
-                headerMapF = bookSource.getHeaderMap(true)
+                headerMapF = bookSource.getHeaderMap(true),
+                coroutineContext = coroutineContext
             )
             var res = analyzeUrl.getStrResponseAwait()
             //检测书源是否已登录
@@ -244,7 +247,8 @@ object WebBook {
                     baseUrl = book.bookUrl,
                     source = bookSource,
                     ruleData = book,
-                    headerMapF = bookSource.getHeaderMap(true)
+                    headerMapF = bookSource.getHeaderMap(true),
+                    coroutineContext = coroutineContext
                 )
                 var res = analyzeUrl.getStrResponseAwait()
                 //检测书源是否已登录
@@ -317,7 +321,8 @@ object WebBook {
                 source = bookSource,
                 ruleData = book,
                 chapter = bookChapter,
-                headerMapF = bookSource.getHeaderMap(true)
+                headerMapF = bookSource.getHeaderMap(true),
+                coroutineContext = coroutineContext
             )
             var res = analyzeUrl.getStrResponseAwait(
                 jsStr = bookSource.getContentRule().webJs,

+ 5 - 4
modules/rhino/src/main/java/com/script/rhino/RhinoScriptEngine.kt

@@ -90,10 +90,9 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
         scope: Scriptable,
         coroutineContext: CoroutineContext?
     ): Any? {
-        val cx = Context.enter()
-        if (cx is RhinoContext) {
-            cx.coroutineContext = coroutineContext
-        }
+        val cx = Context.enter() as RhinoContext
+        val previousCoroutineContext = cx.coroutineContext
+        cx.coroutineContext = coroutineContext
         val ret: Any?
         try {
             var filename = this["javax.script.filename"] as? String
@@ -112,6 +111,7 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
         } catch (var14: IOException) {
             throw ScriptException(var14)
         } finally {
+            cx.coroutineContext = previousCoroutineContext
             Context.exit()
         }
         return unwrapReturnValue(ret)
@@ -355,6 +355,7 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
                 args: Array<Any>
             ): Any? {
                 try {
+                    (cx as RhinoContext).ensureActive()
                     return super.doTopCall(callable, cx, scope, thisObj, args)
                 } catch (e: RhinoInterruptError) {
                     throw e.cause