pull/1145/head
gedoor 3 years ago
parent 95d7b3c441
commit b7e89f522f
  1. 219
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
  2. 8
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt

@ -25,9 +25,7 @@ import kotlin.collections.HashMap
@Keep @Keep
@Suppress("unused", "RegExpRedundantEscape") @Suppress("unused", "RegExpRedundantEscape")
class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
var book: BaseBook? = null
var book = if (ruleData is BaseBook) ruleData else null
var chapter: BookChapter? = null var chapter: BookChapter? = null
var nextChapterUrl: String? = null var nextChapterUrl: String? = null
var content: Any? = null var content: Any? = null
@ -44,12 +42,18 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
private var objectChangedJS = false private var objectChangedJS = false
private var objectChangedJP = false private var objectChangedJP = false
init {
if (ruleData is BaseBook) {
book = ruleData
}
}
@JvmOverloads @JvmOverloads
fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule { fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule {
if (content == null) throw AssertionError("内容不可空(Content cannot be null)") if (content == null) throw AssertionError("Content cannot be null")
this.content = content this.content = content
isJSON = content.toString().isJson()
setBaseUrl(baseUrl) setBaseUrl(baseUrl)
isJSON = content.toString().isJson()
objectChangedXP = true objectChangedXP = true
objectChangedJS = true objectChangedJS = true
objectChangedJP = true objectChangedJP = true
@ -59,14 +63,16 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
fun setBaseUrl(baseUrl: String?): AnalyzeRule { fun setBaseUrl(baseUrl: String?): AnalyzeRule {
baseUrl?.let { baseUrl?.let {
this.baseUrl = baseUrl this.baseUrl = baseUrl
val urlMatcher = AnalyzeUrl.paramPattern.matcher(baseUrl)
if (urlMatcher.find()) this.baseUrl = baseUrl.substring(0, urlMatcher.start())
} }
return this return this
} }
fun setRedirectUrl(url: String): URL? { fun setRedirectUrl(url: String): URL? {
kotlin.runCatching { kotlin.runCatching {
val urlMatcher = AnalyzeUrl.paramPattern.matcher(url) val urlMatcher = AnalyzeUrl.paramPattern.matcher(baseUrl)
redirectUrl = URL( if(urlMatcher.find())url.substring(0,urlMatcher.start()) else url) redirectUrl = URL(if (urlMatcher.find()) url.substring(0, urlMatcher.start()) else url)
} }
return redirectUrl return redirectUrl
} }
@ -122,7 +128,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
@JvmOverloads @JvmOverloads
fun getStringList(rule: String?, isUrl: Boolean = false): List<String>? { fun getStringList(rule: String?, isUrl: Boolean = false): List<String>? {
if (rule.isNullOrEmpty()) return null if (rule.isNullOrEmpty()) return null
val ruleList = splitSourceRule(rule, true) val ruleList = splitSourceRule(rule)
return getStringList(ruleList, isUrl) return getStringList(ruleList, isUrl)
} }
@ -282,7 +288,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun getElements(ruleStr: String): List<Any> { fun getElements(ruleStr: String): List<Any> {
var result: Any? = null var result: Any? = null
val ruleList = splitSourceRule(ruleStr, true) val ruleList = splitSourceRule(ruleStr)
content?.let { o -> content?.let { o ->
if (ruleList.isNotEmpty()) result = o if (ruleList.isNotEmpty()) result = o
for (sourceRule in ruleList) { for (sourceRule in ruleList) {
@ -357,44 +363,58 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
/** /**
* 分解规则生成规则列表 * 分解规则生成规则列表
*/ */
fun splitSourceRule(ruleStr: String?, isList: Boolean = false): List<SourceRule> { fun splitSourceRule(ruleStr: String?, mode: Mode = Mode.Default): List<SourceRule> {
if (ruleStr.isNullOrEmpty()) return ArrayList<SourceRule>() var vRuleStr = ruleStr
val ruleList = ArrayList<SourceRule>() val ruleList = ArrayList<SourceRule>()
var mMode: Mode = Mode.Default if (vRuleStr.isNullOrEmpty()) return ruleList
var start = 0 //检测Mode
//仅首字符为:时为AllInOne,其实:与伪类选择器冲突,建议改成?更合理 var mMode: Mode = mode
if (isList && ruleStr.startsWith(":")) { when {
mMode = Mode.Regex vRuleStr.startsWith("@@") -> {
isRegex = true vRuleStr = vRuleStr.substring(2)
start = 1 }
vRuleStr.startsWith("@XPath:", true) -> {
mMode = Mode.XPath
vRuleStr = vRuleStr.substring(7)
}
vRuleStr.startsWith("@Json:", true) -> {
mMode = Mode.Json
vRuleStr = vRuleStr.substring(6)
}
vRuleStr.startsWith(":") -> {
mMode = Mode.Regex
isRegex = true
vRuleStr = vRuleStr.substring(1)
}
isRegex -> mMode = Mode.Regex
isJSON -> mMode = Mode.Json
} }
//拆分为规则列表
var start = 0
var tmp: String var tmp: String
val jsMatcher = JS_PATTERN.matcher(ruleStr) val jsMatcher = JS_PATTERN.matcher(vRuleStr)
while (jsMatcher.find()) { while (jsMatcher.find()) {
if (jsMatcher.start() > start) { if (jsMatcher.start() > start) {
tmp = ruleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } tmp = vRuleStr.substring(start, jsMatcher.start()).trim { it <= ' ' }
if (tmp.isNotEmpty()) { if (!TextUtils.isEmpty(tmp)) {
ruleList.add(SourceRule(tmp, mMode)) ruleList.add(SourceRule(tmp, mMode))
} }
} }
ruleList.add(SourceRule(jsMatcher.group(2) ?: jsMatcher.group(1), Mode.Js)) ruleList.add(SourceRule(jsMatcher.group(), Mode.Js))
start = jsMatcher.end() start = jsMatcher.end()
} }
if (vRuleStr.length > start) {
if (ruleStr.length > start) { tmp = vRuleStr.substring(start).trim { it <= ' ' }
tmp = ruleStr.substring(start).trim { it <= ' ' } if (!TextUtils.isEmpty(tmp)) {
if (tmp.isNotEmpty()) {
ruleList.add(SourceRule(tmp, mMode)) ruleList.add(SourceRule(tmp, mMode))
} }
} }
return ruleList return ruleList
} }
/** /**
* 规则类 * 规则类
*/ */
inner class SourceRule internal constructor(ruleStr: String, mainMode: Mode = Mode.Default) { inner class SourceRule internal constructor(ruleStr: String, mainMode: Mode = Mode.Default) {
internal var mode: Mode internal var mode: Mode
internal var rule: String internal var rule: String
@ -410,33 +430,39 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
init { init {
this.mode = mainMode this.mode = mainMode
rule = when { if (mode == Mode.Js) {
ruleStr.startsWith("@CSS:", true) -> { rule = if (ruleStr.startsWith("<js>")) {
mode = Mode.Default ruleStr.substring(4, ruleStr.lastIndexOf("<"))
ruleStr } else {
} ruleStr.substring(4)
ruleStr.startsWith("@@") -> {
mode = Mode.Default
ruleStr.substring(2)
}
ruleStr.startsWith("@XPath:", true) -> {
mode = Mode.XPath
ruleStr.substring(7)
}
ruleStr.startsWith("/") -> {//XPath特征很明显,无需配置单独的识别标头
mode = Mode.XPath
ruleStr
}
ruleStr.startsWith("@Json:", true) -> {
mode = Mode.Json
ruleStr.substring(6)
}
ruleStr.startsWith("$.") || isJSON -> {
mode = Mode.Json
ruleStr
} }
else -> { } else {
ruleStr when {
ruleStr.startsWith("@CSS:", true) -> {
mode = Mode.Default
rule = ruleStr
}
ruleStr.startsWith("@@") -> {
mode = Mode.Default
rule = ruleStr.substring(2)
}
ruleStr.startsWith("@XPath:", true) -> {
mode = Mode.XPath
rule = ruleStr.substring(7)
}
ruleStr.startsWith("//") -> {//XPath特征很明显,无需配置单独的识别标头
mode = Mode.XPath
rule = ruleStr
}
ruleStr.startsWith("@Json:", true) -> {
mode = Mode.Json
rule = ruleStr.substring(6)
}
ruleStr.startsWith("$.") -> {
mode = Mode.Json
rule = ruleStr
}
else -> rule = ruleStr
} }
} }
//分离put //分离put
@ -445,39 +471,35 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
var start = 0 var start = 0
var tmp: String var tmp: String
val evalMatcher = evalPattern.matcher(rule) val evalMatcher = evalPattern.matcher(rule)
while (evalMatcher.find()) {
if (evalMatcher.find()) { if (evalMatcher.start() > start) {
if (mode != Mode.Js && mode != Mode.Regex tmp = rule.substring(start, evalMatcher.start())
if (mode != Mode.Js && mode != Mode.Regex
&& start == 0 && !tmp.contains("##")
) {
mode = Mode.Regex
}
splitRegex(tmp)
} else if (mode != Mode.Js && mode != Mode.Regex
&& evalMatcher.start() == 0 && evalMatcher.start() == 0
) { ) {
mode = Mode.Regex mode = Mode.Regex
} }
do { tmp = evalMatcher.group()
if (evalMatcher.start() > start) { when {
tmp = rule.substring(start, evalMatcher.start()) tmp.startsWith("@get:", true) -> {
if (mode != Mode.Js && mode != Mode.Regex ruleType.add(getRuleType)
&& start == 0 && !tmp.contains("##") ruleParam.add(tmp.substring(6, tmp.lastIndex))
) {
mode = Mode.Regex
}
splitRegex(tmp)
} }
tmp = evalMatcher.group() tmp.startsWith("{{") -> {
when { ruleType.add(jsRuleType)
tmp.startsWith("@get:", true) -> { ruleParam.add(tmp.substring(2, tmp.length - 2))
ruleType.add(getRuleType)
ruleParam.add(tmp.substring(6, tmp.lastIndex))
}
tmp.startsWith("{{") -> {
ruleType.add(jsRuleType)
ruleParam.add(tmp.substring(2, tmp.length - 2))
}
else -> {
splitRegex(tmp)
}
} }
start = evalMatcher.end() else -> {
} while (evalMatcher.find()) splitRegex(tmp)
}
}
start = evalMatcher.end()
} }
if (rule.length > start) { if (rule.length > start) {
tmp = rule.substring(start) tmp = rule.substring(start)
@ -493,22 +515,19 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
var tmp: String var tmp: String
val ruleStrArray = ruleStr.split("##") val ruleStrArray = ruleStr.split("##")
val regexMatcher = regexPattern.matcher(ruleStrArray[0]) val regexMatcher = regexPattern.matcher(ruleStrArray[0])
while (regexMatcher.find()) {
if (regexMatcher.find()) {
if (mode != Mode.Js && mode != Mode.Regex) { if (mode != Mode.Js && mode != Mode.Regex) {
mode = Mode.Regex mode = Mode.Regex
} }
do { if (regexMatcher.start() > start) {
if (regexMatcher.start() > start) { tmp = ruleStr.substring(start, regexMatcher.start())
tmp = ruleStr.substring(start, regexMatcher.start()) ruleType.add(defaultRuleType)
ruleType.add(defaultRuleType)
ruleParam.add(tmp)
}
tmp = regexMatcher.group()
ruleType.add(tmp.substring(1).toInt())
ruleParam.add(tmp) ruleParam.add(tmp)
start = regexMatcher.end() }
} while (regexMatcher.find()) tmp = regexMatcher.group()
ruleType.add(tmp.substring(1).toInt())
ruleParam.add(tmp)
start = regexMatcher.end()
} }
if (ruleStr.length > start) { if (ruleStr.length > start) {
tmp = ruleStr.substring(start) tmp = ruleStr.substring(start)
@ -581,10 +600,15 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
} }
private fun isRule(ruleStr: String): Boolean { private fun isRule(ruleStr: String): Boolean {
return ruleStr.startsWith('@') //js首个字符不可能是@,除非是装饰器,所以@开头规定为规则 return when {
|| ruleStr.startsWith("$.") ruleStr.startsWith("$.") -> true
|| ruleStr.startsWith("$[") ruleStr.startsWith("@Json:", true) -> true
|| ruleStr.startsWith("//") ruleStr.startsWith("//") -> true
ruleStr.startsWith("@XPath:", true) -> true
ruleStr.startsWith("@CSS:", true) -> true
ruleStr.startsWith("@@") -> true
else -> false
}
} }
} }
@ -662,6 +686,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
companion object { companion object {
private val putPattern = Pattern.compile("@put:(\\{[^}]+?\\})", Pattern.CASE_INSENSITIVE) private val putPattern = Pattern.compile("@put:(\\{[^}]+?\\})", Pattern.CASE_INSENSITIVE)
private val getPattern = Pattern.compile("@get:\\{([^}]+?)\\}", Pattern.CASE_INSENSITIVE)
private val evalPattern = private val evalPattern =
Pattern.compile("@get:\\{[^}]+?\\}|\\{\\{[\\w\\W]*?\\}\\}", Pattern.CASE_INSENSITIVE) Pattern.compile("@get:\\{[^}]+?\\}|\\{\\{[\\w\\W]*?\\}\\}", Pattern.CASE_INSENSITIVE)
private val regexPattern = Pattern.compile("\\$\\d{1,2}") private val regexPattern = Pattern.compile("\\$\\d{1,2}")

@ -57,7 +57,7 @@ class AnalyzeUrl(
init { init {
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?.let { headerMapF?.let {
headerMap.putAll(it) headerMap.putAll(it)
if (it.containsKey("proxy")) { if (it.containsKey("proxy")) {
@ -144,12 +144,12 @@ class AnalyzeUrl(
*/ */
private fun initUrl() { //replaceKeyPageJs已经替换掉额外内容,此处url是基础形式,可以直接切首个‘,’之前字符串。 private fun initUrl() { //replaceKeyPageJs已经替换掉额外内容,此处url是基础形式,可以直接切首个‘,’之前字符串。
val urlMatcher = paramPattern.matcher(ruleUrl) val urlMatcher = paramPattern.matcher(ruleUrl)
urlHasQuery = if(urlMatcher.find())ruleUrl.substring(0,urlMatcher.start()) else ruleUrl urlHasQuery = if (urlMatcher.find()) ruleUrl.substring(0, urlMatcher.start()) else ruleUrl
url = NetworkUtils.getAbsoluteURL(baseUrl,urlHasQuery ) url = NetworkUtils.getAbsoluteURL(baseUrl, urlHasQuery)
NetworkUtils.getBaseUrl(url)?.let { NetworkUtils.getBaseUrl(url)?.let {
baseUrl = it baseUrl = it
} }
if(urlHasQuery.length != ruleUrl.length ) { if (urlHasQuery.length != ruleUrl.length) {
GSON.fromJsonObject<UrlOption>(ruleUrl.substring(urlMatcher.end()))?.let { option -> GSON.fromJsonObject<UrlOption>(ruleUrl.substring(urlMatcher.end()))?.let { option ->
option.method?.let { option.method?.let {
if (it.equals("POST", true)) method = RequestMethod.POST if (it.equals("POST", true)) method = RequestMethod.POST

Loading…
Cancel
Save