From dd798012bdaadd31c2197ece2c1f431c7b85ea1c Mon Sep 17 00:00:00 2001 From: Horis <8674809+821938089@users.noreply.github.com> Date: Mon, 8 Jul 2024 08:48:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/help/JsExtensions.kt | 47 +++++++++++++------ .../app/model/analyzeRule/AnalyzeRule.kt | 23 +++++---- .../app/model/analyzeRule/AnalyzeUrl.kt | 15 ++++-- .../app/model/webBook/BookChapterList.kt | 16 ++++--- .../legado/app/model/webBook/BookContent.kt | 16 ++++--- .../io/legado/app/model/webBook/WebBook.kt | 13 +++-- .../com/script/rhino/RhinoScriptEngine.kt | 9 ++-- 7 files changed, 91 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index adec5f955..e71d18e8d 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/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): Array { - return runBlocking { + return runBlocking(context) { val asyncArray = Array(urlList.size) { async(IO) { val url = urlList[it] @@ -122,7 +132,7 @@ interface JsExtensions : JsEncodeUtils { * 访问网络,返回Response */ 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>(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 diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index 7bb96778b..02721ff7b 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/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>() - 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) } diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index 9ac49ded1..7220194e7 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/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? = 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() } } diff --git a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt index 6f1b758fe..31f075d14 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt +++ b/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 diff --git a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt index fcf27c884..6f4d348f2 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt +++ b/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, diff --git a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt index b8cd5566b..3f42b797a 100644 --- a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt +++ b/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, diff --git a/modules/rhino/src/main/java/com/script/rhino/RhinoScriptEngine.kt b/modules/rhino/src/main/java/com/script/rhino/RhinoScriptEngine.kt index 690639725..4b3b62371 100644 --- a/modules/rhino/src/main/java/com/script/rhino/RhinoScriptEngine.kt +++ b/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? { try { + (cx as RhinoContext).ensureActive() return super.doTopCall(callable, cx, scope, thisObj, args) } catch (e: RhinoInterruptError) { throw e.cause