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 47f60b720..9765605c5 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 @@ -25,9 +25,7 @@ import kotlin.collections.HashMap @Keep @Suppress("unused", "RegExpRedundantEscape") class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { - - var book = if (ruleData is BaseBook) ruleData else null - + var book: BaseBook? = null var chapter: BookChapter? = null var nextChapterUrl: String? = null var content: Any? = null @@ -44,12 +42,18 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { private var objectChangedJS = false private var objectChangedJP = false + init { + if (ruleData is BaseBook) { + book = ruleData + } + } + @JvmOverloads 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 - isJSON = content.toString().isJson() setBaseUrl(baseUrl) + isJSON = content.toString().isJson() objectChangedXP = true objectChangedJS = true objectChangedJP = true @@ -59,14 +63,16 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { fun setBaseUrl(baseUrl: String?): AnalyzeRule { baseUrl?.let { this.baseUrl = baseUrl + val urlMatcher = AnalyzeUrl.paramPattern.matcher(baseUrl) + if (urlMatcher.find()) this.baseUrl = baseUrl.substring(0, urlMatcher.start()) } return this } fun setRedirectUrl(url: String): URL? { kotlin.runCatching { - val urlMatcher = AnalyzeUrl.paramPattern.matcher(url) - redirectUrl = URL( if(urlMatcher.find())url.substring(0,urlMatcher.start()) else url) + val urlMatcher = AnalyzeUrl.paramPattern.matcher(baseUrl) + redirectUrl = URL(if (urlMatcher.find()) url.substring(0, urlMatcher.start()) else url) } return redirectUrl } @@ -122,7 +128,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { @JvmOverloads fun getStringList(rule: String?, isUrl: Boolean = false): List? { if (rule.isNullOrEmpty()) return null - val ruleList = splitSourceRule(rule, true) + val ruleList = splitSourceRule(rule) return getStringList(ruleList, isUrl) } @@ -282,7 +288,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { @Suppress("UNCHECKED_CAST") fun getElements(ruleStr: String): List { var result: Any? = null - val ruleList = splitSourceRule(ruleStr, true) + val ruleList = splitSourceRule(ruleStr) content?.let { o -> if (ruleList.isNotEmpty()) result = o for (sourceRule in ruleList) { @@ -357,44 +363,58 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { /** * 分解规则生成规则列表 */ - fun splitSourceRule(ruleStr: String?, isList: Boolean = false): List { - if (ruleStr.isNullOrEmpty()) return ArrayList() + fun splitSourceRule(ruleStr: String?, mode: Mode = Mode.Default): List { + var vRuleStr = ruleStr val ruleList = ArrayList() - var mMode: Mode = Mode.Default - var start = 0 - //仅首字符为:时为AllInOne,其实:与伪类选择器冲突,建议改成?更合理 - if (isList && ruleStr.startsWith(":")) { - mMode = Mode.Regex - isRegex = true - start = 1 + if (vRuleStr.isNullOrEmpty()) return ruleList + //检测Mode + var mMode: Mode = mode + when { + vRuleStr.startsWith("@@") -> { + vRuleStr = vRuleStr.substring(2) + } + 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 - val jsMatcher = JS_PATTERN.matcher(ruleStr) + val jsMatcher = JS_PATTERN.matcher(vRuleStr) while (jsMatcher.find()) { if (jsMatcher.start() > start) { - tmp = ruleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } - if (tmp.isNotEmpty()) { + tmp = vRuleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } + if (!TextUtils.isEmpty(tmp)) { 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() } - - if (ruleStr.length > start) { - tmp = ruleStr.substring(start).trim { it <= ' ' } - if (tmp.isNotEmpty()) { + if (vRuleStr.length > start) { + tmp = vRuleStr.substring(start).trim { it <= ' ' } + if (!TextUtils.isEmpty(tmp)) { ruleList.add(SourceRule(tmp, mMode)) } } - return ruleList } /** * 规则类 */ - inner class SourceRule internal constructor(ruleStr: String, mainMode: Mode = Mode.Default) { internal var mode: Mode internal var rule: String @@ -410,33 +430,39 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { init { this.mode = mainMode - rule = when { - ruleStr.startsWith("@CSS:", true) -> { - mode = Mode.Default - ruleStr - } - 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 + if (mode == Mode.Js) { + rule = if (ruleStr.startsWith("")) { + ruleStr.substring(4, ruleStr.lastIndexOf("<")) + } else { + ruleStr.substring(4) } - else -> { - ruleStr + } else { + 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 @@ -445,39 +471,35 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { var start = 0 var tmp: String val evalMatcher = evalPattern.matcher(rule) - - if (evalMatcher.find()) { - if (mode != Mode.Js && mode != Mode.Regex + while (evalMatcher.find()) { + if (evalMatcher.start() > start) { + 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 ) { mode = Mode.Regex } - do { - if (evalMatcher.start() > start) { - tmp = rule.substring(start, evalMatcher.start()) - if (mode != Mode.Js && mode != Mode.Regex - && start == 0 && !tmp.contains("##") - ) { - mode = Mode.Regex - } - splitRegex(tmp) + tmp = evalMatcher.group() + when { + tmp.startsWith("@get:", true) -> { + ruleType.add(getRuleType) + ruleParam.add(tmp.substring(6, tmp.lastIndex)) } - tmp = evalMatcher.group() - when { - tmp.startsWith("@get:", true) -> { - 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) - } + tmp.startsWith("{{") -> { + ruleType.add(jsRuleType) + ruleParam.add(tmp.substring(2, tmp.length - 2)) } - start = evalMatcher.end() - } while (evalMatcher.find()) + else -> { + splitRegex(tmp) + } + } + start = evalMatcher.end() } if (rule.length > start) { tmp = rule.substring(start) @@ -493,22 +515,19 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { var tmp: String val ruleStrArray = ruleStr.split("##") val regexMatcher = regexPattern.matcher(ruleStrArray[0]) - - if (regexMatcher.find()) { + while (regexMatcher.find()) { if (mode != Mode.Js && mode != Mode.Regex) { mode = Mode.Regex } - do { - if (regexMatcher.start() > start) { - tmp = ruleStr.substring(start, regexMatcher.start()) - ruleType.add(defaultRuleType) - ruleParam.add(tmp) - } - tmp = regexMatcher.group() - ruleType.add(tmp.substring(1).toInt()) + if (regexMatcher.start() > start) { + tmp = ruleStr.substring(start, regexMatcher.start()) + ruleType.add(defaultRuleType) 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) { tmp = ruleStr.substring(start) @@ -581,10 +600,15 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { } private fun isRule(ruleStr: String): Boolean { - return ruleStr.startsWith('@') //js首个字符不可能是@,除非是装饰器,所以@开头规定为规则 - || ruleStr.startsWith("$.") - || ruleStr.startsWith("$[") - || ruleStr.startsWith("//") + return when { + ruleStr.startsWith("$.") -> true + ruleStr.startsWith("@Json:", true) -> true + 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 { private val putPattern = Pattern.compile("@put:(\\{[^}]+?\\})", Pattern.CASE_INSENSITIVE) + private val getPattern = Pattern.compile("@get:\\{([^}]+?)\\}", Pattern.CASE_INSENSITIVE) private val evalPattern = Pattern.compile("@get:\\{[^}]+?\\}|\\{\\{[\\w\\W]*?\\}\\}", Pattern.CASE_INSENSITIVE) private val regexPattern = Pattern.compile("\\$\\d{1,2}") 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 bc0c18113..a89d78ec8 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 @@ -57,7 +57,7 @@ class AnalyzeUrl( init { 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 { headerMap.putAll(it) if (it.containsKey("proxy")) { @@ -144,12 +144,12 @@ class AnalyzeUrl( */ private fun initUrl() { //replaceKeyPageJs已经替换掉额外内容,此处url是基础形式,可以直接切首个‘,’之前字符串。 val urlMatcher = paramPattern.matcher(ruleUrl) - urlHasQuery = if(urlMatcher.find())ruleUrl.substring(0,urlMatcher.start()) else ruleUrl - url = NetworkUtils.getAbsoluteURL(baseUrl,urlHasQuery ) + urlHasQuery = if (urlMatcher.find()) ruleUrl.substring(0, urlMatcher.start()) else ruleUrl + url = NetworkUtils.getAbsoluteURL(baseUrl, urlHasQuery) NetworkUtils.getBaseUrl(url)?.let { baseUrl = it } - if(urlHasQuery.length != ruleUrl.length ) { + if (urlHasQuery.length != ruleUrl.length) { GSON.fromJsonObject(ruleUrl.substring(urlMatcher.end()))?.let { option -> option.method?.let { if (it.equals("POST", true)) method = RequestMethod.POST