优化
This commit is contained in:
@@ -12,6 +12,7 @@ import android.os.Build
|
|||||||
import com.github.liuyueyi.quick.transfer.constants.TransType
|
import com.github.liuyueyi.quick.transfer.constants.TransType
|
||||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||||
import com.jeremyliao.liveeventbus.logger.DefaultLogger
|
import com.jeremyliao.liveeventbus.logger.DefaultLogger
|
||||||
|
import com.script.rhino.RhinoScriptEngine
|
||||||
import io.legado.app.base.AppContextWrapper
|
import io.legado.app.base.AppContextWrapper
|
||||||
import io.legado.app.constant.AppConst.channelIdDownload
|
import io.legado.app.constant.AppConst.channelIdDownload
|
||||||
import io.legado.app.constant.AppConst.channelIdReadAloud
|
import io.legado.app.constant.AppConst.channelIdReadAloud
|
||||||
@@ -77,6 +78,7 @@ class App : Application() {
|
|||||||
Coroutine.async {
|
Coroutine.async {
|
||||||
URL.setURLStreamHandlerFactory(ObsoleteUrlFactory(okHttpClient))
|
URL.setURLStreamHandlerFactory(ObsoleteUrlFactory(okHttpClient))
|
||||||
launch { installGmsTlsProvider(appCtx) }
|
launch { installGmsTlsProvider(appCtx) }
|
||||||
|
RhinoScriptEngine
|
||||||
//初始化封面
|
//初始化封面
|
||||||
BookCover.toString()
|
BookCover.toString()
|
||||||
//清除过期数据
|
//清除过期数据
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import androidx.room.Entity
|
|||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeRule
|
import io.legado.app.model.analyzeRule.AnalyzeRule
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||||
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字典规则
|
* 字典规则
|
||||||
@@ -36,12 +37,12 @@ data class DictRule(
|
|||||||
* 搜索字典
|
* 搜索字典
|
||||||
*/
|
*/
|
||||||
suspend fun search(word: String): String {
|
suspend fun search(word: String): String {
|
||||||
val analyzeUrl = AnalyzeUrl(urlRule, key = word)
|
val analyzeUrl = AnalyzeUrl(urlRule, key = word, coroutineContext = coroutineContext)
|
||||||
val body = analyzeUrl.getStrResponseAwait().body
|
val body = analyzeUrl.getStrResponseAwait().body
|
||||||
if (showRule.isBlank()) {
|
if (showRule.isBlank()) {
|
||||||
return body!!
|
return body!!
|
||||||
}
|
}
|
||||||
val analyzeRule = AnalyzeRule()
|
val analyzeRule = AnalyzeRule().setCoroutineContext(coroutineContext)
|
||||||
return analyzeRule.getString(showRule, mContent = body)
|
return analyzeRule.getString(showRule, mContent = body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import io.legado.app.utils.fromJsonArray
|
|||||||
import io.legado.app.utils.fromJsonObject
|
import io.legado.app.utils.fromJsonObject
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
object DirectLinkUpload {
|
object DirectLinkUpload {
|
||||||
@@ -60,6 +61,7 @@ object DirectLinkUpload {
|
|||||||
mFile.delete()
|
mFile.delete()
|
||||||
}
|
}
|
||||||
val analyzeRule = AnalyzeRule().setContent(res.body, res.url)
|
val analyzeRule = AnalyzeRule().setContent(res.body, res.url)
|
||||||
|
.setCoroutineContext(coroutineContext)
|
||||||
val downloadUrl = analyzeRule.getString(downloadUrlRule)
|
val downloadUrl = analyzeRule.getString(downloadUrlRule)
|
||||||
if (downloadUrl.isBlank()) {
|
if (downloadUrl.isBlank()) {
|
||||||
throw NoStackTraceException("上传失败,${res.body}")
|
throw NoStackTraceException("上传失败,${res.body}")
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import io.legado.app.utils.getFile
|
|||||||
import io.legado.app.utils.isContentScheme
|
import io.legado.app.utils.isContentScheme
|
||||||
import io.legado.app.utils.onEachParallel
|
import io.legado.app.utils.onEachParallel
|
||||||
import io.legado.app.utils.postEvent
|
import io.legado.app.utils.postEvent
|
||||||
|
import io.legado.app.utils.runScriptWithContext
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.ensureActive
|
import kotlinx.coroutines.ensureActive
|
||||||
@@ -232,12 +233,16 @@ object BookHelp {
|
|||||||
if (isImageExist(book, src)) {
|
if (isImageExist(book, src)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val analyzeUrl = AnalyzeUrl(src, source = bookSource)
|
val analyzeUrl = AnalyzeUrl(
|
||||||
|
src, source = bookSource, coroutineContext = coroutineContext
|
||||||
|
)
|
||||||
val bytes = analyzeUrl.getByteArrayAwait()
|
val bytes = analyzeUrl.getByteArrayAwait()
|
||||||
//某些图片被加密,需要进一步解密
|
//某些图片被加密,需要进一步解密
|
||||||
ImageUtils.decode(
|
runScriptWithContext {
|
||||||
src, bytes, isCover = false, bookSource, book
|
ImageUtils.decode(
|
||||||
)?.let {
|
src, bytes, isCover = false, bookSource, book
|
||||||
|
)
|
||||||
|
}?.let {
|
||||||
if (!checkImage(it)) {
|
if (!checkImage(it)) {
|
||||||
// 如果部分图片失效,每次进入正文都会花很长时间再次获取图片数据
|
// 如果部分图片失效,每次进入正文都会花很长时间再次获取图片数据
|
||||||
// 所以无论如何都要将数据写入到文件里
|
// 所以无论如何都要将数据写入到文件里
|
||||||
@@ -546,12 +551,16 @@ object BookHelp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val chapterNamePattern1 by lazy {
|
private val chapterNamePattern1 by lazy {
|
||||||
Pattern.compile(".*?第([\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+)[章节篇回集话]")
|
Pattern.compile(
|
||||||
|
".*?第([\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+)[章节篇回集话]"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("RegExpSimplifiable")
|
@Suppress("RegExpSimplifiable")
|
||||||
private val chapterNamePattern2 by lazy {
|
private val chapterNamePattern2 by lazy {
|
||||||
Pattern.compile("^(?:[\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+[,:、])*([\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+)(?:[,:、]|\\.[^\\d])")
|
Pattern.compile(
|
||||||
|
"^(?:[\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+[,:、])*([\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+)(?:[,:、]|\\.[^\\d])"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val regexA by lazy {
|
private val regexA by lazy {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import io.legado.app.exception.NoStackTraceException
|
|||||||
import io.legado.app.model.ReadManga
|
import io.legado.app.model.ReadManga
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||||
import io.legado.app.utils.ImageUtils
|
import io.legado.app.utils.ImageUtils
|
||||||
|
import io.legado.app.utils.runScriptWithContext
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
class LegadoDataUrlLoader : ModelLoader<String, InputStream> {
|
class LegadoDataUrlLoader : ModelLoader<String, InputStream> {
|
||||||
@@ -33,15 +35,23 @@ class LegadoDataUrlLoader : ModelLoader<String, InputStream> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LegadoDataUrlFetcher(private val model: String) : DataFetcher<InputStream> {
|
class LegadoDataUrlFetcher(private val model: String) : DataFetcher<InputStream> {
|
||||||
|
|
||||||
|
private val coroutineContext = Job()
|
||||||
|
|
||||||
override fun loadData(
|
override fun loadData(
|
||||||
priority: Priority,
|
priority: Priority,
|
||||||
callback: DataFetcher.DataCallback<in InputStream>
|
callback: DataFetcher.DataCallback<in InputStream>
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
val bytes = AnalyzeUrl(model, source = ReadManga.bookSource).getByteArray()
|
val bytes = AnalyzeUrl(
|
||||||
val decoded = ImageUtils.decode(
|
model, source = ReadManga.bookSource,
|
||||||
model, bytes, isCover = false, ReadManga.bookSource, ReadManga.book
|
coroutineContext = coroutineContext
|
||||||
)?.inputStream()
|
).getByteArray()
|
||||||
|
val decoded = runScriptWithContext(coroutineContext) {
|
||||||
|
ImageUtils.decode(
|
||||||
|
model, bytes, isCover = false, ReadManga.bookSource, ReadManga.book
|
||||||
|
)?.inputStream()
|
||||||
|
}
|
||||||
if (decoded == null) {
|
if (decoded == null) {
|
||||||
throw NoStackTraceException("漫画图片解密失败")
|
throw NoStackTraceException("漫画图片解密失败")
|
||||||
}
|
}
|
||||||
@@ -56,7 +66,7 @@ class LegadoDataUrlLoader : ModelLoader<String, InputStream> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun cancel() {
|
override fun cancel() {
|
||||||
// do nothing
|
coroutineContext.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDataClass(): Class<InputStream> {
|
override fun getDataClass(): Class<InputStream> {
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import io.legado.app.help.source.SourceHelp
|
|||||||
import io.legado.app.model.ReadManga
|
import io.legado.app.model.ReadManga
|
||||||
import io.legado.app.utils.ImageUtils
|
import io.legado.app.utils.ImageUtils
|
||||||
import io.legado.app.utils.isWifiConnect
|
import io.legado.app.utils.isWifiConnect
|
||||||
|
import io.legado.app.utils.runScriptWithContext
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import okhttp3.Call
|
import okhttp3.Call
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
@@ -38,6 +40,7 @@ class OkHttpStreamFetcher(
|
|||||||
private var callback: DataFetcher.DataCallback<in InputStream>? = null
|
private var callback: DataFetcher.DataCallback<in InputStream>? = null
|
||||||
private var source: BaseSource? = null
|
private var source: BaseSource? = null
|
||||||
private val manga = options.get(OkHttpModelLoader.mangaOption) == true
|
private val manga = options.get(OkHttpModelLoader.mangaOption) == true
|
||||||
|
private val coroutineContext = Job()
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var call: Call? = null
|
private var call: Call? = null
|
||||||
@@ -89,6 +92,7 @@ class OkHttpStreamFetcher(
|
|||||||
|
|
||||||
override fun cancel() {
|
override fun cancel() {
|
||||||
call?.cancel()
|
call?.cancel()
|
||||||
|
coroutineContext.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDataClass(): Class<InputStream> {
|
override fun getDataClass(): Class<InputStream> {
|
||||||
@@ -106,19 +110,21 @@ class OkHttpStreamFetcher(
|
|||||||
override fun onResponse(call: Call, response: Response) {
|
override fun onResponse(call: Call, response: Response) {
|
||||||
responseBody = response.body
|
responseBody = response.body
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
val decodeResult = if (manga) {
|
val decodeResult = runScriptWithContext(coroutineContext) {
|
||||||
ImageUtils.decode(
|
if (manga) {
|
||||||
oldUrl.toString(),
|
ImageUtils.decode(
|
||||||
responseBody!!.bytes(),
|
oldUrl.toString(),
|
||||||
isCover = false,
|
responseBody!!.bytes(),
|
||||||
source,
|
isCover = false,
|
||||||
ReadManga.book
|
source,
|
||||||
)?.inputStream()
|
ReadManga.book
|
||||||
} else {
|
)?.inputStream()
|
||||||
ImageUtils.decode(
|
} else {
|
||||||
url.toStringUrl(), responseBody!!.byteStream(),
|
ImageUtils.decode(
|
||||||
isCover = true, source
|
url.toStringUrl(), responseBody!!.byteStream(),
|
||||||
)
|
isCover = true, source
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (decodeResult == null) {
|
if (decodeResult == null) {
|
||||||
callback?.onLoadFailed(NoStackTraceException("封面二次解密失败"))
|
callback?.onLoadFailed(NoStackTraceException("封面二次解密失败"))
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import io.legado.app.utils.MD5Utils
|
|||||||
import io.legado.app.utils.fromJsonArray
|
import io.legado.app.utils.fromJsonArray
|
||||||
import io.legado.app.utils.isJsonArray
|
import io.legado.app.utils.isJsonArray
|
||||||
import io.legado.app.utils.printOnDebug
|
import io.legado.app.utils.printOnDebug
|
||||||
|
import io.legado.app.utils.runScriptWithContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
@@ -61,7 +62,9 @@ suspend fun BookSource.exploreKinds(): List<ExploreKind> {
|
|||||||
} else {
|
} else {
|
||||||
exploreUrl.substring(4, exploreUrl.lastIndexOf("<"))
|
exploreUrl.substring(4, exploreUrl.lastIndexOf("<"))
|
||||||
}
|
}
|
||||||
ruleStr = evalJS(jsStr).toString().trim()
|
ruleStr = runScriptWithContext {
|
||||||
|
evalJS(jsStr).toString().trim()
|
||||||
|
}
|
||||||
aCache.put(exploreKindsKey, ruleStr)
|
aCache.put(exploreKindsKey, ruleStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import io.legado.app.data.entities.RssSource
|
|||||||
import io.legado.app.utils.ACache
|
import io.legado.app.utils.ACache
|
||||||
import io.legado.app.utils.MD5Utils
|
import io.legado.app.utils.MD5Utils
|
||||||
import io.legado.app.utils.NetworkUtils
|
import io.legado.app.utils.NetworkUtils
|
||||||
|
import io.legado.app.utils.runScriptWithContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@@ -29,7 +30,9 @@ suspend fun RssSource.sortUrls(): List<Pair<String, String>> {
|
|||||||
} else {
|
} else {
|
||||||
sortUrl!!.substring(4, sortUrl!!.lastIndexOf("<"))
|
sortUrl!!.substring(4, sortUrl!!.lastIndexOf("<"))
|
||||||
}
|
}
|
||||||
str = evalJS(jsStr).toString()
|
str = runScriptWithContext {
|
||||||
|
evalJS(jsStr).toString()
|
||||||
|
}
|
||||||
aCache.put(sortUrlsKey, str)
|
aCache.put(sortUrlsKey, str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ object SourceVerificationHelp {
|
|||||||
putExtra("sourceName", source.getTag())
|
putExtra("sourceName", source.getTag())
|
||||||
putExtra("sourceVerificationEnable", saveResult)
|
putExtra("sourceVerificationEnable", saveResult)
|
||||||
putExtra("refetchAfterSuccess", refetchAfterSuccess)
|
putExtra("refetchAfterSuccess", refetchAfterSuccess)
|
||||||
IntentData.put(url, source.getHeaderMap(true))
|
|
||||||
IntentData.put(getVerificationResultKey(source), Thread.currentThread())
|
IntentData.put(getVerificationResultKey(source), Thread.currentThread())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import io.legado.app.utils.getPrefBoolean
|
|||||||
import io.legado.app.utils.getPrefString
|
import io.legado.app.utils.getPrefString
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
object BookCover {
|
object BookCover {
|
||||||
@@ -174,10 +175,12 @@ object BookCover {
|
|||||||
config.searchUrl,
|
config.searchUrl,
|
||||||
book.name,
|
book.name,
|
||||||
source = config,
|
source = config,
|
||||||
headerMapF = config.getHeaderMap()
|
coroutineContext = coroutineContext,
|
||||||
|
hasLoginHeader = false
|
||||||
)
|
)
|
||||||
val res = analyzeUrl.getStrResponseAwait()
|
val res = analyzeUrl.getStrResponseAwait()
|
||||||
val analyzeRule = AnalyzeRule(book)
|
val analyzeRule = AnalyzeRule(book)
|
||||||
|
analyzeRule.setCoroutineContext(coroutineContext)
|
||||||
analyzeRule.setContent(res.body)
|
analyzeRule.setContent(res.body)
|
||||||
analyzeRule.setRedirectUrl(res.url)
|
analyzeRule.setRedirectUrl(res.url)
|
||||||
return analyzeRule.getString(config.coverRule, isUrl = true)
|
return analyzeRule.getString(config.coverRule, isUrl = true)
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import io.legado.app.utils.isJson
|
|||||||
import io.legado.app.utils.isJsonArray
|
import io.legado.app.utils.isJsonArray
|
||||||
import io.legado.app.utils.isJsonObject
|
import io.legado.app.utils.isJsonObject
|
||||||
import io.legado.app.utils.isXml
|
import io.legado.app.utils.isXml
|
||||||
|
import io.legado.app.utils.runScriptWithContext
|
||||||
import io.legado.app.utils.splitNotBlank
|
import io.legado.app.utils.splitNotBlank
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
@@ -81,6 +82,7 @@ class AnalyzeUrl(
|
|||||||
private val readTimeout: Long? = null,
|
private val readTimeout: Long? = null,
|
||||||
private var coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
private var coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||||
headerMapF: Map<String, String>? = null,
|
headerMapF: Map<String, String>? = null,
|
||||||
|
hasLoginHeader: Boolean = true
|
||||||
) : JsExtensions {
|
) : JsExtensions {
|
||||||
companion object {
|
companion object {
|
||||||
val paramPattern: Pattern = Pattern.compile("\\s*,\\s*(?=\\{)")
|
val paramPattern: Pattern = Pattern.compile("\\s*,\\s*(?=\\{)")
|
||||||
@@ -118,7 +120,9 @@ class AnalyzeUrl(
|
|||||||
coroutineContext = coroutineContext.minusKey(ContinuationInterceptor)
|
coroutineContext = coroutineContext.minusKey(ContinuationInterceptor)
|
||||||
val urlMatcher = paramPattern.matcher(baseUrl)
|
val urlMatcher = paramPattern.matcher(baseUrl)
|
||||||
if (urlMatcher.find()) baseUrl = baseUrl.substring(0, urlMatcher.start())
|
if (urlMatcher.find()) baseUrl = baseUrl.substring(0, urlMatcher.start())
|
||||||
(headerMapF ?: source?.getHeaderMap(true))?.let {
|
(headerMapF ?: runScriptWithContext(coroutineContext) {
|
||||||
|
source?.getHeaderMap(hasLoginHeader)
|
||||||
|
})?.let {
|
||||||
headerMap.putAll(it)
|
headerMap.putAll(it)
|
||||||
if (it.containsKey("proxy")) {
|
if (it.containsKey("proxy")) {
|
||||||
proxy = it["proxy"]
|
proxy = it["proxy"]
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import io.legado.app.utils.NetworkUtils
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
object Rss {
|
object Rss {
|
||||||
@@ -41,7 +42,8 @@ object Rss {
|
|||||||
page = page,
|
page = page,
|
||||||
source = rssSource,
|
source = rssSource,
|
||||||
ruleData = ruleData,
|
ruleData = ruleData,
|
||||||
headerMapF = rssSource.getHeaderMap()
|
coroutineContext = coroutineContext,
|
||||||
|
hasLoginHeader = false
|
||||||
)
|
)
|
||||||
val res = analyzeUrl.getStrResponseAwait()
|
val res = analyzeUrl.getStrResponseAwait()
|
||||||
checkRedirect(rssSource, res)
|
checkRedirect(rssSource, res)
|
||||||
@@ -70,7 +72,8 @@ object Rss {
|
|||||||
baseUrl = rssArticle.origin,
|
baseUrl = rssArticle.origin,
|
||||||
source = rssSource,
|
source = rssSource,
|
||||||
ruleData = rssArticle,
|
ruleData = rssArticle,
|
||||||
headerMapF = rssSource.getHeaderMap()
|
coroutineContext = coroutineContext,
|
||||||
|
hasLoginHeader = false
|
||||||
)
|
)
|
||||||
val res = analyzeUrl.getStrResponseAwait()
|
val res = analyzeUrl.getStrResponseAwait()
|
||||||
checkRedirect(rssSource, res)
|
checkRedirect(rssSource, res)
|
||||||
@@ -79,6 +82,7 @@ object Rss {
|
|||||||
val analyzeRule = AnalyzeRule(rssArticle, rssSource)
|
val analyzeRule = AnalyzeRule(rssArticle, rssSource)
|
||||||
analyzeRule.setContent(res.body)
|
analyzeRule.setContent(res.body)
|
||||||
.setBaseUrl(NetworkUtils.getAbsoluteURL(rssArticle.origin, rssArticle.link))
|
.setBaseUrl(NetworkUtils.getAbsoluteURL(rssArticle.origin, rssArticle.link))
|
||||||
|
.setCoroutineContext(coroutineContext)
|
||||||
.setRedirectUrl(res.url)
|
.setRedirectUrl(res.url)
|
||||||
return analyzeRule.getString(ruleContent)
|
return analyzeRule.getString(ruleContent)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,13 @@ import io.legado.app.model.analyzeRule.RuleData
|
|||||||
import io.legado.app.utils.NetworkUtils
|
import io.legado.app.utils.NetworkUtils
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
object RssParserByRule {
|
object RssParserByRule {
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun parseXML(
|
suspend fun parseXML(
|
||||||
sortName: String,
|
sortName: String,
|
||||||
sortUrl: String,
|
sortUrl: String,
|
||||||
redirectUrl: String,
|
redirectUrl: String,
|
||||||
@@ -40,6 +41,7 @@ object RssParserByRule {
|
|||||||
} else {
|
} else {
|
||||||
val articleList = mutableListOf<RssArticle>()
|
val articleList = mutableListOf<RssArticle>()
|
||||||
val analyzeRule = AnalyzeRule(ruleData, rssSource)
|
val analyzeRule = AnalyzeRule(ruleData, rssSource)
|
||||||
|
analyzeRule.setCoroutineContext(coroutineContext)
|
||||||
analyzeRule.setContent(body).setBaseUrl(sortUrl)
|
analyzeRule.setContent(body).setBaseUrl(sortUrl)
|
||||||
analyzeRule.setRedirectUrl(redirectUrl)
|
analyzeRule.setRedirectUrl(redirectUrl)
|
||||||
var reverse = false
|
var reverse = false
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ object BookChapterList {
|
|||||||
mUrl = nextUrl,
|
mUrl = nextUrl,
|
||||||
source = bookSource,
|
source = bookSource,
|
||||||
ruleData = book,
|
ruleData = book,
|
||||||
headerMapF = bookSource.getHeaderMap(),
|
|
||||||
coroutineContext = coroutineContext
|
coroutineContext = coroutineContext
|
||||||
)
|
)
|
||||||
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
|
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
|
||||||
@@ -100,7 +99,6 @@ object BookChapterList {
|
|||||||
mUrl = urlStr,
|
mUrl = urlStr,
|
||||||
source = bookSource,
|
source = bookSource,
|
||||||
ruleData = book,
|
ruleData = book,
|
||||||
headerMapF = bookSource.getHeaderMap(),
|
|
||||||
coroutineContext = coroutineContext
|
coroutineContext = coroutineContext
|
||||||
)
|
)
|
||||||
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
|
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ object BookContent {
|
|||||||
mUrl = nextUrl,
|
mUrl = nextUrl,
|
||||||
source = bookSource,
|
source = bookSource,
|
||||||
ruleData = book,
|
ruleData = book,
|
||||||
headerMapF = bookSource.getHeaderMap(),
|
|
||||||
coroutineContext = coroutineContext
|
coroutineContext = coroutineContext
|
||||||
)
|
)
|
||||||
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
|
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
|
||||||
@@ -118,7 +117,6 @@ object BookContent {
|
|||||||
mUrl = urlStr,
|
mUrl = urlStr,
|
||||||
source = bookSource,
|
source = bookSource,
|
||||||
ruleData = book,
|
ruleData = book,
|
||||||
headerMapF = bookSource.getHeaderMap(),
|
|
||||||
coroutineContext = coroutineContext
|
coroutineContext = coroutineContext
|
||||||
)
|
)
|
||||||
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
|
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ object WebBook {
|
|||||||
key = key,
|
key = key,
|
||||||
page = page,
|
page = page,
|
||||||
baseUrl = bookSource.bookSourceUrl,
|
baseUrl = bookSource.bookSourceUrl,
|
||||||
headerMapF = bookSource.getHeaderMap(true),
|
|
||||||
source = bookSource,
|
source = bookSource,
|
||||||
ruleData = ruleData,
|
ruleData = ruleData,
|
||||||
coroutineContext = coroutineContext
|
coroutineContext = coroutineContext
|
||||||
@@ -115,7 +114,6 @@ object WebBook {
|
|||||||
baseUrl = bookSource.bookSourceUrl,
|
baseUrl = bookSource.bookSourceUrl,
|
||||||
source = bookSource,
|
source = bookSource,
|
||||||
ruleData = ruleData,
|
ruleData = ruleData,
|
||||||
headerMapF = bookSource.getHeaderMap(true),
|
|
||||||
coroutineContext = coroutineContext
|
coroutineContext = coroutineContext
|
||||||
)
|
)
|
||||||
var res = analyzeUrl.getStrResponseAwait()
|
var res = analyzeUrl.getStrResponseAwait()
|
||||||
@@ -173,7 +171,6 @@ object WebBook {
|
|||||||
baseUrl = bookSource.bookSourceUrl,
|
baseUrl = bookSource.bookSourceUrl,
|
||||||
source = bookSource,
|
source = bookSource,
|
||||||
ruleData = book,
|
ruleData = book,
|
||||||
headerMapF = bookSource.getHeaderMap(true),
|
|
||||||
coroutineContext = coroutineContext
|
coroutineContext = coroutineContext
|
||||||
)
|
)
|
||||||
var res = analyzeUrl.getStrResponseAwait()
|
var res = analyzeUrl.getStrResponseAwait()
|
||||||
@@ -249,7 +246,6 @@ object WebBook {
|
|||||||
baseUrl = book.bookUrl,
|
baseUrl = book.bookUrl,
|
||||||
source = bookSource,
|
source = bookSource,
|
||||||
ruleData = book,
|
ruleData = book,
|
||||||
headerMapF = bookSource.getHeaderMap(true),
|
|
||||||
coroutineContext = coroutineContext
|
coroutineContext = coroutineContext
|
||||||
)
|
)
|
||||||
var res = analyzeUrl.getStrResponseAwait()
|
var res = analyzeUrl.getStrResponseAwait()
|
||||||
@@ -328,7 +324,6 @@ object WebBook {
|
|||||||
source = bookSource,
|
source = bookSource,
|
||||||
ruleData = book,
|
ruleData = book,
|
||||||
chapter = bookChapter,
|
chapter = bookChapter,
|
||||||
headerMapF = bookSource.getHeaderMap(true),
|
|
||||||
coroutineContext = coroutineContext
|
coroutineContext = coroutineContext
|
||||||
)
|
)
|
||||||
var res = analyzeUrl.getStrResponseAwait(
|
var res = analyzeUrl.getStrResponseAwait(
|
||||||
|
|||||||
@@ -225,6 +225,7 @@ class AudioPlayService : BaseService(),
|
|||||||
source = AudioPlay.bookSource,
|
source = AudioPlay.bookSource,
|
||||||
ruleData = AudioPlay.book,
|
ruleData = AudioPlay.book,
|
||||||
chapter = AudioPlay.durChapter,
|
chapter = AudioPlay.durChapter,
|
||||||
|
coroutineContext = coroutineContext
|
||||||
)
|
)
|
||||||
exoPlayer.setMediaItem(analyzeUrl.getMediaItem())
|
exoPlayer.setMediaItem(analyzeUrl.getMediaItem())
|
||||||
exoPlayer.playWhenReady = true
|
exoPlayer.playWhenReady = true
|
||||||
|
|||||||
@@ -328,7 +328,6 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|||||||
speakText = speakText,
|
speakText = speakText,
|
||||||
speakSpeed = speechRate,
|
speakSpeed = speechRate,
|
||||||
source = httpTts,
|
source = httpTts,
|
||||||
headerMapF = httpTts.getHeaderMap(true),
|
|
||||||
readTimeout = 300 * 1000L,
|
readTimeout = 300 * 1000L,
|
||||||
coroutineContext = coroutineContext
|
coroutineContext = coroutineContext
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -274,7 +274,10 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
val fileNameNoExtension = if (book.author.isBlank()) book.name
|
val fileNameNoExtension = if (book.author.isBlank()) book.name
|
||||||
else "${book.name} 作者:${book.author}"
|
else "${book.name} 作者:${book.author}"
|
||||||
book.downloadUrls!!.map {
|
book.downloadUrls!!.map {
|
||||||
val analyzeUrl = AnalyzeUrl(it, source = bookSource)
|
val analyzeUrl = AnalyzeUrl(
|
||||||
|
it, source = bookSource,
|
||||||
|
coroutineContext = coroutineContext
|
||||||
|
)
|
||||||
val mFileName = UrlUtil.getFileName(analyzeUrl)
|
val mFileName = UrlUtil.getFileName(analyzeUrl)
|
||||||
?: "${fileNameNoExtension}.${analyzeUrl.type}"
|
?: "${fileNameNoExtension}.${analyzeUrl.type}"
|
||||||
WebFile(it, mFileName)
|
WebFile(it, mFileName)
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ import androidx.core.view.isGone
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.databinding.ViewMangaMenuBinding
|
import io.legado.app.databinding.ViewMangaMenuBinding
|
||||||
import io.legado.app.help.IntentData
|
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.lib.dialogs.alert
|
import io.legado.app.lib.dialogs.alert
|
||||||
import io.legado.app.lib.theme.bottomBackground
|
import io.legado.app.lib.theme.bottomBackground
|
||||||
|
import io.legado.app.model.ReadBook
|
||||||
import io.legado.app.model.ReadManga
|
import io.legado.app.model.ReadManga
|
||||||
import io.legado.app.ui.browser.WebViewActivity
|
import io.legado.app.ui.browser.WebViewActivity
|
||||||
import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
|
import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
|
||||||
@@ -176,9 +176,11 @@ class MangaMenu @JvmOverloads constructor(
|
|||||||
} else {
|
} else {
|
||||||
context.startActivity<WebViewActivity> {
|
context.startActivity<WebViewActivity> {
|
||||||
val url = tvChapterUrl.text.toString()
|
val url = tvChapterUrl.text.toString()
|
||||||
|
val bookSource = ReadBook.bookSource
|
||||||
putExtra("title", tvChapterName.text)
|
putExtra("title", tvChapterName.text)
|
||||||
putExtra("url", url)
|
putExtra("url", url)
|
||||||
IntentData.put(url, ReadManga.bookSource?.getHeaderMap(true))
|
putExtra("sourceOrigin", bookSource?.bookSourceUrl)
|
||||||
|
putExtra("sourceName", bookSource?.bookSourceName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1281,7 +1281,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
|||||||
alert(R.string.chapter_pay) {
|
alert(R.string.chapter_pay) {
|
||||||
setMessage(chapter.title)
|
setMessage(chapter.title)
|
||||||
yesButton {
|
yesButton {
|
||||||
Coroutine.async {
|
Coroutine.async(lifecycleScope) {
|
||||||
val source =
|
val source =
|
||||||
ReadBook.bookSource ?: throw NoStackTraceException("no book source")
|
ReadBook.bookSource ?: throw NoStackTraceException("no book source")
|
||||||
val payAction = source.getContentRule().payAction
|
val payAction = source.getContentRule().payAction
|
||||||
@@ -1289,6 +1289,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
|||||||
throw NoStackTraceException("no pay action")
|
throw NoStackTraceException("no pay action")
|
||||||
}
|
}
|
||||||
val analyzeRule = AnalyzeRule(book, source)
|
val analyzeRule = AnalyzeRule(book, source)
|
||||||
|
analyzeRule.setCoroutineContext(coroutineContext)
|
||||||
analyzeRule.setBaseUrl(chapter.url)
|
analyzeRule.setBaseUrl(chapter.url)
|
||||||
analyzeRule.chapter = chapter
|
analyzeRule.chapter = chapter
|
||||||
analyzeRule.evalJS(payAction).toString()
|
analyzeRule.evalJS(payAction).toString()
|
||||||
@@ -1297,7 +1298,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
|||||||
startActivity<WebViewActivity> {
|
startActivity<WebViewActivity> {
|
||||||
putExtra("title", getString(R.string.chapter_pay))
|
putExtra("title", getString(R.string.chapter_pay))
|
||||||
putExtra("url", it)
|
putExtra("url", it)
|
||||||
IntentData.put(it, ReadBook.bookSource?.getHeaderMap(true))
|
putExtra("sourceOrigin", ReadBook.bookSource?.bookSourceUrl)
|
||||||
}
|
}
|
||||||
} else if (it.isTrue()) {
|
} else if (it.isTrue()) {
|
||||||
//购买成功后刷新目录
|
//购买成功后刷新目录
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import androidx.core.view.isVisible
|
|||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.constant.PreferKey
|
import io.legado.app.constant.PreferKey
|
||||||
import io.legado.app.databinding.ViewReadMenuBinding
|
import io.legado.app.databinding.ViewReadMenuBinding
|
||||||
import io.legado.app.help.IntentData
|
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.help.config.LocalConfig
|
import io.legado.app.help.config.LocalConfig
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
@@ -350,9 +349,11 @@ class ReadMenu @JvmOverloads constructor(
|
|||||||
Coroutine.async {
|
Coroutine.async {
|
||||||
context.startActivity<WebViewActivity> {
|
context.startActivity<WebViewActivity> {
|
||||||
val url = tvChapterUrl.text.toString()
|
val url = tvChapterUrl.text.toString()
|
||||||
|
val bookSource = ReadBook.bookSource
|
||||||
putExtra("title", tvChapterName.text)
|
putExtra("title", tvChapterName.text)
|
||||||
putExtra("url", url)
|
putExtra("url", url)
|
||||||
IntentData.put(url, ReadBook.bookSource?.getHeaderMap(true))
|
putExtra("sourceOrigin", bookSource?.bookSourceUrl)
|
||||||
|
putExtra("sourceName", bookSource?.bookSourceName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import io.legado.app.base.BaseViewModel
|
|||||||
import io.legado.app.constant.AppConst
|
import io.legado.app.constant.AppConst
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.exception.NoStackTraceException
|
import io.legado.app.exception.NoStackTraceException
|
||||||
import io.legado.app.help.IntentData
|
|
||||||
import io.legado.app.help.http.newCallResponseBody
|
import io.legado.app.help.http.newCallResponseBody
|
||||||
import io.legado.app.help.http.okHttpClient
|
import io.legado.app.help.http.okHttpClient
|
||||||
import io.legado.app.help.source.SourceVerificationHelp
|
import io.legado.app.help.source.SourceVerificationHelp
|
||||||
@@ -48,8 +47,8 @@ class WebViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
sourceOrigin = intent.getStringExtra("sourceOrigin") ?: ""
|
sourceOrigin = intent.getStringExtra("sourceOrigin") ?: ""
|
||||||
sourceVerificationEnable = intent.getBooleanExtra("sourceVerificationEnable", false)
|
sourceVerificationEnable = intent.getBooleanExtra("sourceVerificationEnable", false)
|
||||||
refetchAfterSuccess = intent.getBooleanExtra("refetchAfterSuccess", true)
|
refetchAfterSuccess = intent.getBooleanExtra("refetchAfterSuccess", true)
|
||||||
val headerMapF = IntentData.get<Map<String, String>>(url)
|
val source = appDb.bookSourceDao.getBookSource(sourceOrigin)
|
||||||
val analyzeUrl = AnalyzeUrl(url, headerMapF = headerMapF)
|
val analyzeUrl = AnalyzeUrl(url, source = source, coroutineContext = coroutineContext)
|
||||||
baseUrl = analyzeUrl.url
|
baseUrl = analyzeUrl.url
|
||||||
headerMap.putAll(analyzeUrl.headerMap)
|
headerMap.putAll(analyzeUrl.headerMap)
|
||||||
if (analyzeUrl.isPost()) {
|
if (analyzeUrl.isPost()) {
|
||||||
@@ -107,7 +106,8 @@ class WebViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
html = AnalyzeUrl(
|
html = AnalyzeUrl(
|
||||||
url,
|
url,
|
||||||
headerMapF = headerMap,
|
headerMapF = headerMap,
|
||||||
source = source
|
source = source,
|
||||||
|
coroutineContext = coroutineContext
|
||||||
).getStrResponseAwait(useWebView = false).body
|
).getStrResponseAwait(useWebView = false).body
|
||||||
SourceVerificationHelp.setResult(sourceOrigin, html ?: "")
|
SourceVerificationHelp.setResult(sourceOrigin, html ?: "")
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import io.legado.app.data.entities.rule.RowUi
|
|||||||
import io.legado.app.databinding.DialogLoginBinding
|
import io.legado.app.databinding.DialogLoginBinding
|
||||||
import io.legado.app.databinding.ItemFilletTextBinding
|
import io.legado.app.databinding.ItemFilletTextBinding
|
||||||
import io.legado.app.databinding.ItemSourceEditBinding
|
import io.legado.app.databinding.ItemSourceEditBinding
|
||||||
import io.legado.app.help.coroutine.Coroutine
|
|
||||||
import io.legado.app.lib.dialogs.alert
|
import io.legado.app.lib.dialogs.alert
|
||||||
import io.legado.app.lib.theme.primaryColor
|
import io.legado.app.lib.theme.primaryColor
|
||||||
import io.legado.app.ui.about.AppLogDialog
|
import io.legado.app.ui.about.AppLogDialog
|
||||||
@@ -26,6 +25,7 @@ import io.legado.app.utils.dpToPx
|
|||||||
import io.legado.app.utils.isAbsUrl
|
import io.legado.app.utils.isAbsUrl
|
||||||
import io.legado.app.utils.openUrl
|
import io.legado.app.utils.openUrl
|
||||||
import io.legado.app.utils.printOnDebug
|
import io.legado.app.utils.printOnDebug
|
||||||
|
import io.legado.app.utils.runScriptWithContext
|
||||||
import io.legado.app.utils.setLayout
|
import io.legado.app.utils.setLayout
|
||||||
import io.legado.app.utils.showDialogFragment
|
import io.legado.app.utils.showDialogFragment
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
@@ -119,16 +119,18 @@ class SourceLoginDialog : BaseDialogFragment(R.layout.dialog_login, true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleButtonClick(source: BaseSource, rowUi: RowUi, loginUi: List<RowUi>) {
|
private fun handleButtonClick(source: BaseSource, rowUi: RowUi, loginUi: List<RowUi>) {
|
||||||
Coroutine.async {
|
lifecycleScope.launch(IO) {
|
||||||
if (rowUi.action.isAbsUrl()) {
|
if (rowUi.action.isAbsUrl()) {
|
||||||
context?.openUrl(rowUi.action!!)
|
context?.openUrl(rowUi.action!!)
|
||||||
} else if (rowUi.action != null) {
|
} else if (rowUi.action != null) {
|
||||||
// JavaScript
|
// JavaScript
|
||||||
val buttonFunctionJS = rowUi.action!!
|
val buttonFunctionJS = rowUi.action!!
|
||||||
val loginJS = source.getLoginJs() ?: return@async
|
val loginJS = source.getLoginJs() ?: return@launch
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
source.evalJS("$loginJS\n$buttonFunctionJS") {
|
runScriptWithContext {
|
||||||
put("result", getLoginData(loginUi))
|
source.evalJS("$loginJS\n$buttonFunctionJS") {
|
||||||
|
put("result", getLoginData(loginUi))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.onFailure { e ->
|
}.onFailure { e ->
|
||||||
AppLog.put("LoginUI Button ${rowUi.name} JavaScript error", e)
|
AppLog.put("LoginUI Button ${rowUi.name} JavaScript error", e)
|
||||||
@@ -161,7 +163,9 @@ class SourceLoginDialog : BaseDialogFragment(R.layout.dialog_login, true) {
|
|||||||
}
|
}
|
||||||
} else if (source.putLoginInfo(GSON.toJson(loginData))) {
|
} else if (source.putLoginInfo(GSON.toJson(loginData))) {
|
||||||
try {
|
try {
|
||||||
source.login()
|
runScriptWithContext {
|
||||||
|
source.login()
|
||||||
|
}
|
||||||
context?.toastOnUi(R.string.success)
|
context?.toastOnUi(R.string.success)
|
||||||
withContext(Main) {
|
withContext(Main) {
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import io.legado.app.base.BaseViewModel
|
|||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.BaseSource
|
import io.legado.app.data.entities.BaseSource
|
||||||
import io.legado.app.exception.NoStackTraceException
|
import io.legado.app.exception.NoStackTraceException
|
||||||
|
import io.legado.app.utils.runScriptWithContext
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
|
|
||||||
class SourceLoginViewModel(application: Application) : BaseViewModel(application) {
|
class SourceLoginViewModel(application: Application) : BaseViewModel(application) {
|
||||||
|
|
||||||
var source: BaseSource? = null
|
var source: BaseSource? = null
|
||||||
|
var headerMap: Map<String, String> = emptyMap()
|
||||||
|
|
||||||
fun initData(intent: Intent, success: (bookSource: BaseSource) -> Unit) {
|
fun initData(intent: Intent, success: (bookSource: BaseSource) -> Unit) {
|
||||||
execute {
|
execute {
|
||||||
@@ -21,6 +23,9 @@ class SourceLoginViewModel(application: Application) : BaseViewModel(application
|
|||||||
"rssSource" -> source = appDb.rssSourceDao.getByKey(sourceKey)
|
"rssSource" -> source = appDb.rssSourceDao.getByKey(sourceKey)
|
||||||
"httpTts" -> source = appDb.httpTTSDao.get(sourceKey.toLong())
|
"httpTts" -> source = appDb.httpTTSDao.get(sourceKey.toLong())
|
||||||
}
|
}
|
||||||
|
headerMap = runScriptWithContext {
|
||||||
|
source?.getHeaderMap(true) ?: emptyMap()
|
||||||
|
}
|
||||||
source
|
source
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import android.webkit.WebSettings
|
|||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.webkit.WebViewClient
|
import android.webkit.WebViewClient
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.BaseFragment
|
import io.legado.app.base.BaseFragment
|
||||||
import io.legado.app.constant.AppConst
|
import io.legado.app.constant.AppConst
|
||||||
@@ -29,6 +30,7 @@ import io.legado.app.utils.longSnackbar
|
|||||||
import io.legado.app.utils.openUrl
|
import io.legado.app.utils.openUrl
|
||||||
import io.legado.app.utils.snackbar
|
import io.legado.app.utils.snackbar
|
||||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class WebViewLoginFragment : BaseFragment(R.layout.fragment_web_view_login) {
|
class WebViewLoginFragment : BaseFragment(R.layout.fragment_web_view_login) {
|
||||||
|
|
||||||
@@ -55,11 +57,8 @@ class WebViewLoginFragment : BaseFragment(R.layout.fragment_web_view_login) {
|
|||||||
if (!checking) {
|
if (!checking) {
|
||||||
checking = true
|
checking = true
|
||||||
binding.titleBar.snackbar(R.string.check_host_cookie)
|
binding.titleBar.snackbar(R.string.check_host_cookie)
|
||||||
viewModel.source?.let { source ->
|
viewModel.source?.let {
|
||||||
source.loginUrl?.let {
|
loadUrl(it)
|
||||||
val absoluteUrl = NetworkUtils.getAbsoluteURL(source.getKey(), it)
|
|
||||||
binding.webView.loadUrl(absoluteUrl, source.getHeaderMap(true))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,7 +76,7 @@ class WebViewLoginFragment : BaseFragment(R.layout.fragment_web_view_login) {
|
|||||||
builtInZoomControls = true
|
builtInZoomControls = true
|
||||||
javaScriptEnabled = true
|
javaScriptEnabled = true
|
||||||
displayZoomControls = false
|
displayZoomControls = false
|
||||||
source.getHeaderMap()[AppConst.UA_NAME]?.let {
|
viewModel.headerMap[AppConst.UA_NAME]?.let {
|
||||||
userAgentString = it
|
userAgentString = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,9 +142,14 @@ class WebViewLoginFragment : BaseFragment(R.layout.fragment_web_view_login) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
source.loginUrl?.let {
|
loadUrl(source)
|
||||||
val absoluteUrl = NetworkUtils.getAbsoluteURL(source.getKey(), it)
|
}
|
||||||
binding.webView.loadUrl(absoluteUrl, source.getHeaderMap(true))
|
|
||||||
|
private fun loadUrl(source: BaseSource) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val loginUrl = source.loginUrl ?: return@launch
|
||||||
|
val absoluteUrl = NetworkUtils.getAbsoluteURL(source.getKey(), loginUrl)
|
||||||
|
binding.webView.loadUrl(absoluteUrl, viewModel.headerMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.app.Application
|
|||||||
import io.legado.app.base.BaseViewModel
|
import io.legado.app.base.BaseViewModel
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.RssSource
|
import io.legado.app.data.entities.RssSource
|
||||||
|
import io.legado.app.utils.runScriptWithContext
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
|
|
||||||
class RssViewModel(application: Application) : BaseViewModel(application) {
|
class RssViewModel(application: Application) : BaseViewModel(application) {
|
||||||
@@ -58,7 +59,9 @@ class RssViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
} else {
|
} else {
|
||||||
sortUrl.substring(4, sortUrl.lastIndexOf("<"))
|
sortUrl.substring(4, sortUrl.lastIndexOf("<"))
|
||||||
}
|
}
|
||||||
val result = rssSource.evalJS(jsStr)?.toString()
|
val result = runScriptWithContext {
|
||||||
|
rssSource.evalJS(jsStr)?.toString()
|
||||||
|
}
|
||||||
if (!result.isNullOrBlank()) {
|
if (!result.isNullOrBlank()) {
|
||||||
sortUrl = result
|
sortUrl = result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ import io.legado.app.ui.login.SourceLoginActivity
|
|||||||
import io.legado.app.ui.rss.favorites.RssFavoritesDialog
|
import io.legado.app.ui.rss.favorites.RssFavoritesDialog
|
||||||
import io.legado.app.utils.ACache
|
import io.legado.app.utils.ACache
|
||||||
import io.legado.app.utils.NetworkUtils
|
import io.legado.app.utils.NetworkUtils
|
||||||
import io.legado.app.utils.get
|
|
||||||
import io.legado.app.utils.gone
|
import io.legado.app.utils.gone
|
||||||
import io.legado.app.utils.invisible
|
import io.legado.app.utils.invisible
|
||||||
import io.legado.app.utils.isTrue
|
import io.legado.app.utils.isTrue
|
||||||
@@ -187,7 +186,7 @@ class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun updateFavorite(title: String?, group: String?) {
|
override fun updateFavorite(title: String?, group: String?) {
|
||||||
viewModel.rssArticle?.let{
|
viewModel.rssArticle?.let {
|
||||||
if (title != null) {
|
if (title != null) {
|
||||||
it.title = title
|
it.title = title
|
||||||
}
|
}
|
||||||
@@ -294,8 +293,7 @@ class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>
|
|||||||
val url = NetworkUtils.getAbsoluteURL(it.origin, it.link)
|
val url = NetworkUtils.getAbsoluteURL(it.origin, it.link)
|
||||||
val html = viewModel.clHtml(content)
|
val html = viewModel.clHtml(content)
|
||||||
binding.webView.settings.userAgentString =
|
binding.webView.settings.userAgentString =
|
||||||
viewModel.rssSource?.getHeaderMap()?.get(AppConst.UA_NAME, true)
|
viewModel.headerMap[AppConst.UA_NAME] ?: AppConfig.userAgent
|
||||||
?: AppConfig.userAgent
|
|
||||||
if (viewModel.rssSource?.loadWithBaseUrl == true) {
|
if (viewModel.rssSource?.loadWithBaseUrl == true) {
|
||||||
binding.webView
|
binding.webView
|
||||||
.loadDataWithBaseURL(url, html, "text/html", "utf-8", url)//不想用baseUrl进else
|
.loadDataWithBaseURL(url, html, "text/html", "utf-8", url)//不想用baseUrl进else
|
||||||
|
|||||||
@@ -21,11 +21,13 @@ import io.legado.app.help.http.newCallResponseBody
|
|||||||
import io.legado.app.help.http.okHttpClient
|
import io.legado.app.help.http.okHttpClient
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||||
import io.legado.app.model.rss.Rss
|
import io.legado.app.model.rss.Rss
|
||||||
|
import io.legado.app.utils.runScriptWithContext
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
import io.legado.app.utils.writeBytes
|
import io.legado.app.utils.writeBytes
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
|
|
||||||
class ReadRssViewModel(application: Application) : BaseViewModel(application), JsExtensions {
|
class ReadRssViewModel(application: Application) : BaseViewModel(application), JsExtensions {
|
||||||
@@ -37,6 +39,7 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application), J
|
|||||||
var rssStar: RssStar? = null
|
var rssStar: RssStar? = null
|
||||||
val upTtsMenuData = MutableLiveData<Boolean>()
|
val upTtsMenuData = MutableLiveData<Boolean>()
|
||||||
val upStarMenuData = MutableLiveData<Boolean>()
|
val upStarMenuData = MutableLiveData<Boolean>()
|
||||||
|
var headerMap: Map<String, String> = emptyMap()
|
||||||
|
|
||||||
override fun getSource(): BaseSource? {
|
override fun getSource(): BaseSource? {
|
||||||
return rssSource
|
return rssSource
|
||||||
@@ -47,6 +50,9 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application), J
|
|||||||
val origin = intent.getStringExtra("origin") ?: return@execute
|
val origin = intent.getStringExtra("origin") ?: return@execute
|
||||||
val link = intent.getStringExtra("link")
|
val link = intent.getStringExtra("link")
|
||||||
rssSource = appDb.rssSourceDao.getByKey(origin)
|
rssSource = appDb.rssSourceDao.getByKey(origin)
|
||||||
|
headerMap = runScriptWithContext {
|
||||||
|
rssSource?.getHeaderMap() ?: emptyMap()
|
||||||
|
}
|
||||||
if (link != null) {
|
if (link != null) {
|
||||||
rssStar = appDb.rssStarDao.get(origin, link)
|
rssStar = appDb.rssStarDao.get(origin, link)
|
||||||
rssArticle = rssStar?.toRssArticle() ?: appDb.rssArticleDao.get(origin, link)
|
rssArticle = rssStar?.toRssArticle() ?: appDb.rssArticleDao.get(origin, link)
|
||||||
@@ -80,11 +86,13 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application), J
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadUrl(url: String, baseUrl: String) {
|
private suspend fun loadUrl(url: String, baseUrl: String) {
|
||||||
val analyzeUrl = AnalyzeUrl(
|
val analyzeUrl = AnalyzeUrl(
|
||||||
mUrl = url,
|
mUrl = url,
|
||||||
baseUrl = baseUrl,
|
baseUrl = baseUrl,
|
||||||
headerMapF = rssSource?.getHeaderMap()
|
source = rssSource,
|
||||||
|
coroutineContext = coroutineContext,
|
||||||
|
hasLoginHeader = false
|
||||||
)
|
)
|
||||||
urlLiveData.postValue(analyzeUrl)
|
urlLiveData.postValue(analyzeUrl)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package io.legado.app.utils
|
package io.legado.app.utils
|
||||||
|
|
||||||
|
import com.script.rhino.RhinoContext
|
||||||
|
import com.script.rhino.RhinoScriptEngine
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.supervisorScope
|
import kotlinx.coroutines.supervisorScope
|
||||||
@@ -7,6 +9,9 @@ import org.mozilla.javascript.Context
|
|||||||
import kotlin.contracts.ExperimentalContracts
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.contracts.InvocationKind
|
import kotlin.contracts.InvocationKind
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
import kotlin.coroutines.ContinuationInterceptor
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
|
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
|
||||||
@OptIn(ExperimentalContracts::class)
|
@OptIn(ExperimentalContracts::class)
|
||||||
@@ -29,3 +34,28 @@ inline fun <T> suspendContinuation(crossinline block: suspend CoroutineScope.()
|
|||||||
Context.exit()
|
Context.exit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <T> runScriptWithContext(context: CoroutineContext, block: () -> T): T {
|
||||||
|
RhinoScriptEngine
|
||||||
|
val rhinoContext = Context.enter() as RhinoContext
|
||||||
|
val previousCoroutineContext = rhinoContext.coroutineContext
|
||||||
|
rhinoContext.coroutineContext = context.minusKey(ContinuationInterceptor)
|
||||||
|
try {
|
||||||
|
return block()
|
||||||
|
} finally {
|
||||||
|
rhinoContext.coroutineContext = previousCoroutineContext
|
||||||
|
Context.exit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend inline fun <T> runScriptWithContext(block: () -> T): T {
|
||||||
|
val rhinoContext = Context.enter() as RhinoContext
|
||||||
|
val previousCoroutineContext = rhinoContext.coroutineContext
|
||||||
|
rhinoContext.coroutineContext = coroutineContext.minusKey(ContinuationInterceptor)
|
||||||
|
try {
|
||||||
|
return block()
|
||||||
|
} finally {
|
||||||
|
rhinoContext.coroutineContext = previousCoroutineContext
|
||||||
|
Context.exit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -93,7 +93,9 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
|
|||||||
): Any? {
|
): Any? {
|
||||||
val cx = Context.enter() as RhinoContext
|
val cx = Context.enter() as RhinoContext
|
||||||
val previousCoroutineContext = cx.coroutineContext
|
val previousCoroutineContext = cx.coroutineContext
|
||||||
cx.coroutineContext = coroutineContext
|
if (coroutineContext != null) {
|
||||||
|
cx.coroutineContext = coroutineContext
|
||||||
|
}
|
||||||
cx.allowScriptRun = true
|
cx.allowScriptRun = true
|
||||||
val ret: Any?
|
val ret: Any?
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user