pull/32/head
kunfei 5 years ago
parent 44ab6dc900
commit d215884d04
  1. 229
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt

@ -5,8 +5,10 @@ import android.text.TextUtils
import androidx.annotation.Keep import androidx.annotation.Keep
import io.legado.app.constant.AppConst.SCRIPT_ENGINE import io.legado.app.constant.AppConst.SCRIPT_ENGINE
import io.legado.app.constant.Pattern.EXP_PATTERN import io.legado.app.constant.Pattern.EXP_PATTERN
import io.legado.app.constant.Pattern.JS_PATTERN import io.legado.app.utils.GSON
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.fromJsonObject
import okhttp3.RequestBody
import java.net.URLEncoder import java.net.URLEncoder
import java.util.* import java.util.*
import java.util.regex.Pattern import java.util.regex.Pattern
@ -18,11 +20,19 @@ import javax.script.SimpleBindings
* 搜索URL规则解析 * 搜索URL规则解析
*/ */
@Keep @Keep
class AnalyzeUrl @SuppressLint("DefaultLocale") @SuppressLint("DefaultLocale")
@Throws(Exception::class) class AnalyzeUrl(
constructor(ruleUrl: String, key: String?, page: Int?, headerMapF: Map<String, String>?, baseUrl: String?) { private var ruleUrl: String,
private var baseUrl: String? = null key: String? = null,
var url: String? = null page: Int? = null,
headerMapF: Map<String, String>? = null,
var baseUrl: String? = null
) {
companion object {
private val pagePattern = Pattern.compile("<(.*?)>")
}
lateinit var url: String
private set private set
var host: String? = null var host: String? = null
private set private set
@ -32,8 +42,10 @@ constructor(ruleUrl: String, key: String?, page: Int?, headerMapF: Map<String, S
private set private set
private val queryMap = LinkedHashMap<String, String>() private val queryMap = LinkedHashMap<String, String>()
private val headerMap = HashMap<String, String>() private val headerMap = HashMap<String, String>()
private var charCode: String? = null private var charset: String? = null
var urlMode = UrlMode.DEFAULT var body: RequestBody? = null
private set
var method = Method.GET
private set private set
val postData: ByteArray val postData: ByteArray
@ -47,142 +59,42 @@ constructor(ruleUrl: String, key: String?, page: Int?, headerMapF: Map<String, S
return builder.toString().toByteArray() return builder.toString().toByteArray()
} }
@Throws(Exception::class)
constructor(urlRule: String) : this(urlRule, null, null, null, null)
@Throws(Exception::class)
constructor(urlRule: String, headerMapF: Map<String, String>, baseUrl: String) : this(
urlRule,
null,
null,
headerMapF,
baseUrl
)
init { init {
var ruleUrl = ruleUrl baseUrl = baseUrl?.split(",\n*".toRegex(), 1)?.get(0)
if (!TextUtils.isEmpty(baseUrl)) { headerMapF?.let { headerMap.putAll(it) }
// this.baseUrl = headerPattern.matcher(baseUrl).replaceAll("") //替换参数
} replaceKeyPageJs(key, page)
//解析Header //处理URL
ruleUrl = analyzeHeader(ruleUrl, headerMapF) initUrl()
//替换关键字
key?.let {
if (it.isNotBlank()) {
ruleUrl = ruleUrl.replace("searchKey", it)
}
}
//分离编码规则
ruleUrl = splitCharCode(ruleUrl)
//判断是否有下一页
if (page != null && page > 1 && !ruleUrl.contains("searchPage"))
throw Exception("没有下一页")
//替换js
ruleUrl = replaceJs(ruleUrl, baseUrl, page, key)
//设置页数
ruleUrl = analyzePage(ruleUrl, page)
//执行规则列表
val ruleList = splitRule(ruleUrl)
for (rule in ruleList) {
ruleUrl = if (rule.startsWith("<js>")) {
evalJS(rule.substring(4, rule.lastIndexOf("<")), ruleUrl) as String
} else {
rule.replace("@result", ruleUrl)
}
}
//分离post参数
var ruleUrlS = ruleUrl.split("@".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (ruleUrlS.size > 1) {
urlMode = UrlMode.POST
} else {
//分离get参数
ruleUrlS = ruleUrlS[0].split("\\?".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (ruleUrlS.size > 1) {
urlMode = UrlMode.GET
}
}
generateUrlPath(ruleUrlS[0])
if (urlMode != UrlMode.DEFAULT) {
analyzeQuery(ruleUrlS[1])
}
} }
/** /**
* 解析Header * 替换关键字,页数,JS
*/ */
private fun analyzeHeader(ruleUrl: String, headerMapF: Map<String, String>?): String { private fun replaceKeyPageJs(key: String?, page: Int?) {
// var ruleUrl = ruleUrl //page
// if (headerMapF != null) { page?.let {
// headerMap.putAll(headerMapF)
// }
// val matcher = headerPattern.matcher(ruleUrl)
// if (matcher.find()) {
// var find = matcher.group(0)
// ruleUrl = ruleUrl.replace(find, "")
// find = find.substring(8)
// try {
// val map = Gson().fromJsonObject<Map<String, String>>(find)
// headerMap.putAll(map)
// } catch (ignored: Exception) {
// }
// }
return ruleUrl
}
/**
* 分离编码规则
*/
private fun splitCharCode(rule: String): String {
val ruleUrlS = rule.split("\\|".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (ruleUrlS.size > 1) {
if (!TextUtils.isEmpty(ruleUrlS[1])) {
val qtS = ruleUrlS[1].split("&".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (qt in qtS) {
val gz = qt.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (gz[0] == "char") {
charCode = gz[1]
}
}
}
}
return ruleUrlS[0]
}
/**
* 解析页数
*/
private fun analyzePage(ruleUrl: String, searchPage: Int?): String {
var ruleUrl = ruleUrl
if (searchPage == null) return ruleUrl
val matcher = pagePattern.matcher(ruleUrl) val matcher = pagePattern.matcher(ruleUrl)
while (matcher.find()) { while (matcher.find()) {
val pages = matcher.group(1).split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val pages =
if (searchPage <= pages.size) { matcher.group(1).split(",".toRegex()).dropLastWhile { it.isEmpty() }
ruleUrl = ruleUrl.replace(matcher.group(), pages[searchPage - 1].trim { it <= ' ' }) .toTypedArray()
ruleUrl = if (page <= pages.size) {
ruleUrl.replace(matcher.group(), pages[page - 1].trim { it <= ' ' })
} else { } else {
ruleUrl = ruleUrl.replace(matcher.group(), pages[pages.size - 1].trim { it <= ' ' }) ruleUrl.replace(matcher.group(), pages[pages.size - 1].trim { it <= ' ' })
} }
} }
return ruleUrl.replace("searchPage-1", (searchPage - 1).toString())
.replace("searchPage+1", (searchPage + 1).toString())
.replace("searchPage", searchPage.toString())
} }
//js
/**
* 替换js
*/
@SuppressLint("DefaultLocale")
@Throws(Exception::class)
private fun replaceJs(ruleUrl: String, baseUrl: String?, searchPage: Int?, searchKey: String?): String {
var ruleUrl = ruleUrl
if (ruleUrl.contains("{{") && ruleUrl.contains("}}")) { if (ruleUrl.contains("{{") && ruleUrl.contains("}}")) {
var jsEval: Any var jsEval: Any
val sb = StringBuffer(ruleUrl.length) val sb = StringBuffer(ruleUrl.length)
val simpleBindings = object : SimpleBindings() { val simpleBindings = object : SimpleBindings() {
init { init {
this["baseUrl"] = baseUrl this["baseUrl"] = baseUrl
this["searchPage"] = searchPage this["page"] = page
this["searchKey"] = searchKey this["key"] = key
} }
} }
val expMatcher = EXP_PATTERN.matcher(ruleUrl) val expMatcher = EXP_PATTERN.matcher(ruleUrl)
@ -199,7 +111,27 @@ constructor(ruleUrl: String, key: String?, page: Int?, headerMapF: Map<String, S
expMatcher.appendTail(sb) expMatcher.appendTail(sb)
ruleUrl = sb.toString() ruleUrl = sb.toString()
} }
return ruleUrl }
/**
* 处理URL
*/
private fun initUrl() {
val urlArray = ruleUrl.split(",\n*".toRegex(), 2)
url = urlArray[0]
if (urlArray.size > 1) {
val options = GSON.fromJsonObject<Map<String, String>>(urlArray[1])
options?.let {
options["method"]?.let { if (it.equals("POST", true)) method = Method.POST }
options["headers"]?.let { headers ->
GSON.fromJsonObject<Map<String, String>>(
headers
)?.let { headerMap.putAll(it) }
}
options["body"]?.let { }
options["charset"]?.let { charset = it }
}
}
} }
/** /**
@ -212,47 +144,20 @@ constructor(ruleUrl: String, key: String?, page: Int?, headerMapF: Map<String, S
for (query in queryS) { for (query in queryS) {
val queryM = query.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val queryM = query.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val value = if (queryM.size > 1) queryM[1] else "" val value = if (queryM.size > 1) queryM[1] else ""
if (TextUtils.isEmpty(charCode)) { if (TextUtils.isEmpty(charset)) {
if (NetworkUtils.hasUrlEncoded(value)) { if (NetworkUtils.hasUrlEncoded(value)) {
queryMap[queryM[0]] = value queryMap[queryM[0]] = value
} else { } else {
queryMap[queryM[0]] = URLEncoder.encode(value, "UTF-8") queryMap[queryM[0]] = URLEncoder.encode(value, "UTF-8")
} }
} else if (charCode == "escape") { } else if (charset == "escape") {
// queryMap[queryM[0]] = StringUtils.escape(value) // queryMap[queryM[0]] = StringUtils.escape(value)
} else { } else {
queryMap[queryM[0]] = URLEncoder.encode(value, charCode) queryMap[queryM[0]] = URLEncoder.encode(value, charset)
} }
} }
} }
/**
* 拆分规则
*/
private fun splitRule(ruleStr: String): List<String> {
val ruleList = ArrayList<String>()
val jsMatcher = JS_PATTERN.matcher(ruleStr)
var start = 0
var tmp: String
while (jsMatcher.find()) {
if (jsMatcher.start() > start) {
tmp = ruleStr.substring(start, jsMatcher.start()).replace("\n".toRegex(), "").trim { it <= ' ' }
if (!TextUtils.isEmpty(tmp)) {
ruleList.add(tmp)
}
}
ruleList.add(jsMatcher.group())
start = jsMatcher.end()
}
if (ruleStr.length > start) {
tmp = ruleStr.substring(start).replace("\n".toRegex(), "").trim { it <= ' ' }
if (!TextUtils.isEmpty(tmp)) {
ruleList.add(tmp)
}
}
return ruleList
}
/** /**
* 分解URL * 分解URL
*/ */
@ -280,11 +185,7 @@ constructor(ruleUrl: String, key: String?, page: Int?, headerMapF: Map<String, S
return headerMap return headerMap
} }
enum class UrlMode { enum class Method {
GET, POST, DEFAULT GET, POST
}
companion object {
private val pagePattern = Pattern.compile("\\{(.*?)\\}")
} }
} }

Loading…
Cancel
Save