优化
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
[
|
||||
{
|
||||
"articleStyle": 0,
|
||||
"customOrder": 1,
|
||||
"enableJs": true,
|
||||
"enabled": true,
|
||||
"loadWithBaseUrl": true,
|
||||
"singleUrl": true,
|
||||
"sourceGroup": "legado",
|
||||
"sourceIcon": "https://funimg.pddpic.com/ktt/762ee1c9-a117-46e4-b0c7-9d820420c08b.png.slim.png",
|
||||
"sourceName": "我的小店",
|
||||
"sourceUrl": "https://ktt.pinduoduo.com/t/tlQQsofbQM"
|
||||
"sourceUrl": "https://ktt.pinduoduo.com/t/tlQQsofbQM",
|
||||
"sortUrl": "@js:java.webViewGetOverrideUrl(null, source.sourceUrl, null, \"weixin:.*\")"
|
||||
},
|
||||
{
|
||||
"customOrder": 2,
|
||||
|
||||
@@ -104,6 +104,9 @@ interface RssSourceDao {
|
||||
@Query("delete from rssSources where sourceUrl = :sourceUrl")
|
||||
fun delete(sourceUrl: String)
|
||||
|
||||
@Query("delete from rssSources where sourceGroup like 'legado'")
|
||||
fun deleteDefault()
|
||||
|
||||
@get:Query("select * from rssSources where sourceGroup is null or sourceGroup = ''")
|
||||
val noGroup: List<RssSource>
|
||||
|
||||
|
||||
@@ -2,7 +2,11 @@ package io.legado.app.help
|
||||
|
||||
import io.legado.app.constant.AppConst
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.data.entities.*
|
||||
import io.legado.app.data.entities.DictRule
|
||||
import io.legado.app.data.entities.HttpTTS
|
||||
import io.legado.app.data.entities.KeyboardAssist
|
||||
import io.legado.app.data.entities.RssSource
|
||||
import io.legado.app.data.entities.TxtTocRule
|
||||
import io.legado.app.help.config.LocalConfig
|
||||
import io.legado.app.help.config.ReadBookConfig
|
||||
import io.legado.app.help.config.ThemeConfig
|
||||
@@ -117,6 +121,7 @@ object DefaultData {
|
||||
}
|
||||
|
||||
fun importDefaultRssSources() {
|
||||
appDb.rssSourceDao.deleteDefault()
|
||||
appDb.rssSourceDao.insert(*rssSources.toTypedArray())
|
||||
}
|
||||
|
||||
|
||||
@@ -145,6 +145,43 @@ interface JsExtensions : JsEncodeUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用webView获取资源url
|
||||
*/
|
||||
fun webViewGetSource(html: String?, url: String?, js: String?, sourceRegex: String): String? {
|
||||
return runBlocking {
|
||||
BackstageWebView(
|
||||
url = url,
|
||||
html = html,
|
||||
javaScript = js,
|
||||
headerMap = getSource()?.getHeaderMap(true),
|
||||
tag = getSource()?.getKey(),
|
||||
sourceRegex = sourceRegex
|
||||
).getStrResponse().body
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用webView获取跳转url
|
||||
*/
|
||||
fun webViewGetOverrideUrl(
|
||||
html: String?,
|
||||
url: String?,
|
||||
js: String?,
|
||||
overrideUrlRegex: String
|
||||
): String? {
|
||||
return runBlocking {
|
||||
BackstageWebView(
|
||||
url = url,
|
||||
html = html,
|
||||
javaScript = js,
|
||||
headerMap = getSource()?.getHeaderMap(true),
|
||||
tag = getSource()?.getKey(),
|
||||
overrideUrlRegex = overrideUrlRegex
|
||||
).getStrResponse().body
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用内置浏览器打开链接,手动验证网站防爬
|
||||
* @param url 要打开的链接
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.os.Looper
|
||||
import android.util.AndroidRuntimeException
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.SslErrorHandler
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
@@ -33,6 +34,7 @@ class BackstageWebView(
|
||||
private val tag: String? = null,
|
||||
private val headerMap: Map<String, String>? = null,
|
||||
private val sourceRegex: String? = null,
|
||||
private val overrideUrlRegex: String? = null,
|
||||
private val javaScript: String? = null,
|
||||
) {
|
||||
|
||||
@@ -102,7 +104,7 @@ class BackstageWebView(
|
||||
settings.blockNetworkImage = true
|
||||
settings.userAgentString = headerMap?.get(AppConst.UA_NAME) ?: AppConfig.userAgent
|
||||
settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
|
||||
if (sourceRegex.isNullOrEmpty()) {
|
||||
if (sourceRegex.isNullOrBlank() && overrideUrlRegex.isNullOrBlank()) {
|
||||
webView.webViewClient = HtmlWebViewClient()
|
||||
} else {
|
||||
webView.webViewClient = SnifferWebClient()
|
||||
@@ -153,50 +155,84 @@ class BackstageWebView(
|
||||
handler?.proceed()
|
||||
}
|
||||
|
||||
}
|
||||
private inner class EvalJsRunnable(
|
||||
webView: WebView,
|
||||
private val url: String,
|
||||
private val mJavaScript: String
|
||||
) : Runnable {
|
||||
var retry = 0
|
||||
private val mWebView: WeakReference<WebView> = WeakReference(webView)
|
||||
override fun run() {
|
||||
mWebView.get()?.evaluateJavascript(mJavaScript) {
|
||||
handleResult(it)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class EvalJsRunnable(
|
||||
webView: WebView,
|
||||
private val url: String,
|
||||
private val mJavaScript: String
|
||||
) : Runnable {
|
||||
var retry = 0
|
||||
private val mWebView: WeakReference<WebView> = WeakReference(webView)
|
||||
override fun run() {
|
||||
mWebView.get()?.evaluateJavascript(mJavaScript) {
|
||||
handleResult(it)
|
||||
private fun handleResult(result: String) = Coroutine.async {
|
||||
if (result.isNotEmpty() && result != "null") {
|
||||
val content = StringEscapeUtils.unescapeJson(result)
|
||||
.replace(quoteRegex, "")
|
||||
try {
|
||||
val response = StrResponse(url, content)
|
||||
callback?.onResult(response)
|
||||
} catch (e: Exception) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
mHandler.post {
|
||||
destroy()
|
||||
}
|
||||
return@async
|
||||
}
|
||||
if (retry > 30) {
|
||||
callback?.onError(NoStackTraceException("js执行超时"))
|
||||
mHandler.post {
|
||||
destroy()
|
||||
}
|
||||
return@async
|
||||
}
|
||||
retry++
|
||||
mHandler.postDelayed(this@EvalJsRunnable, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleResult(result: String) = Coroutine.async {
|
||||
if (result.isNotEmpty() && result != "null") {
|
||||
val content = StringEscapeUtils.unescapeJson(result)
|
||||
.replace(quoteRegex, "")
|
||||
try {
|
||||
val response = StrResponse(url, content)
|
||||
callback?.onResult(response)
|
||||
} catch (e: Exception) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
mHandler.post {
|
||||
destroy()
|
||||
}
|
||||
return@async
|
||||
}
|
||||
if (retry > 30) {
|
||||
callback?.onError(NoStackTraceException("js执行超时"))
|
||||
mHandler.post {
|
||||
destroy()
|
||||
}
|
||||
return@async
|
||||
}
|
||||
retry++
|
||||
mHandler.postDelayed(this@EvalJsRunnable, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class SnifferWebClient : WebViewClient() {
|
||||
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView,
|
||||
request: WebResourceRequest
|
||||
): Boolean {
|
||||
if (shouldOverrideUrlLoading(request.url.toString())) {
|
||||
return true
|
||||
}
|
||||
return super.shouldOverrideUrlLoading(view, request)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION", "KotlinRedundantDiagnosticSuppress")
|
||||
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
|
||||
if (shouldOverrideUrlLoading(url)) {
|
||||
return true
|
||||
}
|
||||
return super.shouldOverrideUrlLoading(view, url)
|
||||
}
|
||||
|
||||
private fun shouldOverrideUrlLoading(requestUrl: String): Boolean {
|
||||
overrideUrlRegex?.let {
|
||||
if (requestUrl.matches(it.toRegex())) {
|
||||
try {
|
||||
val response = StrResponse(url!!, requestUrl)
|
||||
callback?.onResult(response)
|
||||
} catch (e: Exception) {
|
||||
callback?.onError(e)
|
||||
}
|
||||
destroy()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onLoadResource(view: WebView, resUrl: String) {
|
||||
sourceRegex?.let {
|
||||
if (resUrl.matches(it.toRegex())) {
|
||||
@@ -213,8 +249,7 @@ class BackstageWebView(
|
||||
|
||||
override fun onPageFinished(webView: WebView, url: String) {
|
||||
setCookie(url)
|
||||
val js = javaScript
|
||||
if (!js.isNullOrEmpty()) {
|
||||
if (!javaScript.isNullOrEmpty()) {
|
||||
val runnable = LoadJsRunnable(webView, javaScript)
|
||||
mHandler.postDelayed(runnable, 1000L)
|
||||
}
|
||||
@@ -229,16 +264,16 @@ class BackstageWebView(
|
||||
handler?.proceed()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class LoadJsRunnable(
|
||||
webView: WebView,
|
||||
private val mJavaScript: String?
|
||||
) : Runnable {
|
||||
private val mWebView: WeakReference<WebView> = WeakReference(webView)
|
||||
override fun run() {
|
||||
mWebView.get()?.loadUrl("javascript:${mJavaScript}")
|
||||
private inner class LoadJsRunnable(
|
||||
webView: WebView,
|
||||
private val mJavaScript: String?
|
||||
) : Runnable {
|
||||
private val mWebView: WeakReference<WebView> = WeakReference(webView)
|
||||
override fun run() {
|
||||
mWebView.get()?.loadUrl("javascript:${mJavaScript}")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -24,7 +24,6 @@ import io.legado.app.ui.rss.favorites.RssFavoritesActivity
|
||||
import io.legado.app.ui.rss.read.ReadRssActivity
|
||||
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
|
||||
import io.legado.app.ui.rss.source.manage.RssSourceActivity
|
||||
import io.legado.app.ui.rss.source.manage.RssSourceViewModel
|
||||
import io.legado.app.ui.rss.subscription.RuleSubActivity
|
||||
import io.legado.app.utils.applyTint
|
||||
import io.legado.app.utils.cnCompare
|
||||
@@ -42,7 +41,7 @@ import kotlinx.coroutines.launch
|
||||
/**
|
||||
* 订阅界面
|
||||
*/
|
||||
class RssFragment() : VMBaseFragment<RssSourceViewModel>(R.layout.fragment_rss),
|
||||
class RssFragment() : VMBaseFragment<RssViewModel>(R.layout.fragment_rss),
|
||||
MainFragmentInterface,
|
||||
RssAdapter.CallBack {
|
||||
|
||||
@@ -55,7 +54,7 @@ class RssFragment() : VMBaseFragment<RssSourceViewModel>(R.layout.fragment_rss),
|
||||
override val position: Int? get() = arguments?.getInt("position")
|
||||
|
||||
private val binding by viewBinding(FragmentRssBinding::bind)
|
||||
override val viewModel by viewModels<RssSourceViewModel>()
|
||||
override val viewModel by viewModels<RssViewModel>()
|
||||
private val adapter by lazy { RssAdapter(requireContext(), this) }
|
||||
private val searchView: SearchView by lazy {
|
||||
binding.titleBar.findViewById(R.id.search_view)
|
||||
@@ -169,13 +168,15 @@ class RssFragment() : VMBaseFragment<RssSourceViewModel>(R.layout.fragment_rss),
|
||||
|
||||
override fun openRss(rssSource: RssSource) {
|
||||
if (rssSource.singleUrl) {
|
||||
if (rssSource.sourceUrl.startsWith("http", true)) {
|
||||
startActivity<ReadRssActivity> {
|
||||
putExtra("title", rssSource.sourceName)
|
||||
putExtra("origin", rssSource.sourceUrl)
|
||||
viewModel.getSingleUrl(rssSource) { url ->
|
||||
if (url.startsWith("http", true)) {
|
||||
startActivity<ReadRssActivity> {
|
||||
putExtra("title", rssSource.sourceName)
|
||||
putExtra("origin", url)
|
||||
}
|
||||
} else {
|
||||
context?.openUrl(url)
|
||||
}
|
||||
} else {
|
||||
context?.openUrl(rssSource.sourceUrl)
|
||||
}
|
||||
} else {
|
||||
startActivity<RssSortActivity> {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package io.legado.app.ui.main.rss
|
||||
|
||||
import android.app.Application
|
||||
import io.legado.app.base.BaseViewModel
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.data.entities.RssSource
|
||||
import io.legado.app.utils.toastOnUi
|
||||
|
||||
class RssViewModel(application: Application) : BaseViewModel(application) {
|
||||
|
||||
fun topSource(vararg sources: RssSource) {
|
||||
execute {
|
||||
sources.sortBy { it.customOrder }
|
||||
val minOrder = appDb.rssSourceDao.minOrder - 1
|
||||
val array = Array(sources.size) {
|
||||
sources[it].copy(customOrder = minOrder - it)
|
||||
}
|
||||
appDb.rssSourceDao.update(*array)
|
||||
}
|
||||
}
|
||||
|
||||
fun bottomSource(vararg sources: RssSource) {
|
||||
execute {
|
||||
sources.sortBy { it.customOrder }
|
||||
val maxOrder = appDb.rssSourceDao.maxOrder + 1
|
||||
val array = Array(sources.size) {
|
||||
sources[it].copy(customOrder = maxOrder + it)
|
||||
}
|
||||
appDb.rssSourceDao.update(*array)
|
||||
}
|
||||
}
|
||||
|
||||
fun del(vararg rssSource: RssSource) {
|
||||
execute { appDb.rssSourceDao.delete(*rssSource) }
|
||||
}
|
||||
|
||||
fun disable(rssSource: RssSource) {
|
||||
execute {
|
||||
rssSource.enabled = false
|
||||
appDb.rssSourceDao.update(rssSource)
|
||||
}
|
||||
}
|
||||
|
||||
fun getSingleUrl(rssSource: RssSource, onSuccess: (url: String) -> Unit) {
|
||||
execute {
|
||||
val url = rssSource.sortUrl
|
||||
if (!url.isNullOrBlank()) {
|
||||
if (url.startsWith("<js>", false)
|
||||
|| url.startsWith("@js:", false)
|
||||
) {
|
||||
val jsStr = if (url.startsWith("@")) {
|
||||
url.substring(4)
|
||||
} else {
|
||||
url.substring(4, url.lastIndexOf("<"))
|
||||
}
|
||||
val result = rssSource.evalJS(jsStr)?.toString()
|
||||
if (!result.isNullOrBlank()) {
|
||||
return@execute result
|
||||
}
|
||||
} else {
|
||||
return@execute url
|
||||
}
|
||||
}
|
||||
rssSource.sourceUrl
|
||||
}.timeout(10000)
|
||||
.onSuccess {
|
||||
onSuccess.invoke(it)
|
||||
}.onError {
|
||||
context.toastOnUi(it.localizedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user