diff --git a/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/BookInfoRule3.kt b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/BookInfoRule3.kt new file mode 100644 index 0000000..46d20f6 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/BookInfoRule3.kt @@ -0,0 +1,19 @@ +package xyz.fycz.myreader.entity.thirdsource.source3 + +/** + * @author fengyue + * @date 2022/1/20 13:49 + */ +data class BookInfoRule3( + var init: String? = null, + var name: String? = null, + var author: String? = null, + var intro: String? = null, + var kind: String? = null, + var lastChapter: String? = null, + var updateTime: String? = null, + var coverUrl: String? = null, + var tocUrl: String? = null, + var wordCount: String? = null, + var canReName: String? = null +) diff --git a/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/ContentRule3.kt b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/ContentRule3.kt new file mode 100644 index 0000000..6dfcce1 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/ContentRule3.kt @@ -0,0 +1,15 @@ +package xyz.fycz.myreader.entity.thirdsource.source3 + +/** + * @author fengyue + * @date 2022/1/20 13:50 + */ +data class ContentRule3( + var content: String? = null, + var nextContentUrl: String? = null, + var webJs: String? = null, + var sourceRegex: String? = null, + var replaceRegex: String? = null, //替换规则 + var imageStyle: String? = null, //默认大小居中,FULL最大宽度 + var payAction: String? = null, //购买操作,url/js +) diff --git a/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/ExploreRule3.kt b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/ExploreRule3.kt new file mode 100644 index 0000000..f2a680b --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/ExploreRule3.kt @@ -0,0 +1,18 @@ +package xyz.fycz.myreader.entity.thirdsource.source3 + +/** + * @author fengyue + * @date 2022/1/20 13:50 + */ +data class ExploreRule3( + var bookList: String? = null, + var name: String? = null, + var author: String? = null, + var intro: String? = null, + var kind: String? = null, + var lastChapter: String? = null, + var updateTime: String? = null, + var bookUrl: String? = null, + var coverUrl: String? = null, + var wordCount: String? = null +) diff --git a/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/SearchRule3.kt b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/SearchRule3.kt new file mode 100644 index 0000000..7b3d79f --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/SearchRule3.kt @@ -0,0 +1,19 @@ +package xyz.fycz.myreader.entity.thirdsource.source3 + +/** + * @author fengyue + * @date 2022/1/20 13:48 + */ +data class SearchRule3( + var checkKeyWord: String? = null, // 校验关键字 + var bookList: String? = null, + var name: String? = null, + var author: String? = null, + var intro: String? = null, + var kind: String? = null, + var lastChapter: String? = null, + var updateTime: String? = null, + var bookUrl: String? = null, + var coverUrl: String? = null, + var wordCount: String? = null +) diff --git a/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/Source3.kt b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/Source3.kt new file mode 100644 index 0000000..32d7681 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/Source3.kt @@ -0,0 +1,36 @@ +package xyz.fycz.myreader.entity.thirdsource.source3 + +/** + * @author fengyue + * @date 2022/1/20 13:47 + */ +data class Source3( + var bookSourceUrl: String = "", // 地址,包括 http/https + var bookSourceName: String = "", // 名称 + var bookSourceGroup: String? = null, // 分组 + //未加 + var bookSourceType: Int = 0, // 类型,0 文本,1 音频, 3 图片 + var bookUrlPattern: String? = null, // 详情页url正则 + var customOrder: Int = 0, // 手动排序编号 + var enabled: Boolean = true, // 是否启用 + //未加 + var enabledExplore: Boolean = true, // 启用发现 + var concurrentRate: String? = null, // 并发率 + var header: String? = null, // 请求头 + var loginUrl: String? = null, // 登录地址 + //未加 + var loginUi: String? = null, // 登录UI + var loginCheckJs: String? = null, // 登录检测js + var bookSourceComment: String? = null, // 注释 + var lastUpdateTime: Long = 0, // 最后更新时间,用于排序 + //未加 + var respondTime: Long = 180000L, // 响应时间,用于排序 + var weight: Int = 0, // 智能排序的权重 + var exploreUrl: String? = null, // 发现url + var ruleExplore: ExploreRule3? = null, // 发现规则 + var searchUrl: String? = null, // 搜索url + var ruleSearch: SearchRule3? = null, // 搜索规则 + var ruleBookInfo: BookInfoRule3? = null, // 书籍信息页规则 + var ruleToc: TocRule3? = null, // 目录页规则 + var ruleContent: ContentRule3? = null // 正文页规则 +) diff --git a/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/Third3SourceUtil.kt b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/Third3SourceUtil.kt new file mode 100644 index 0000000..b1febcc --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/Third3SourceUtil.kt @@ -0,0 +1,101 @@ +package xyz.fycz.myreader.entity.thirdsource.source3 + +import xyz.fycz.myreader.common.APPCONST +import xyz.fycz.myreader.greendao.entity.rule.* + +/** + * @author fengyue + * @date 2022/1/20 13:51 + */ +object Third3SourceUtil { + fun source3ToSource(bean: Source3): BookSource { + val bookSource = BookSource() + bookSource.sourceUrl = bean.bookSourceUrl + bookSource.sourceName = bean.bookSourceName + bookSource.sourceGroup = bean.bookSourceGroup + bookSource.sourceType = APPCONST.THIRD_3_SOURCE + bookSource.orderNum = bean.customOrder + bookSource.enable = bean.enabled + bookSource.concurrentRate = bean.concurrentRate + bookSource.sourceHeaders = bean.header + bookSource.loginUrl = bean.loginUrl + bookSource.loginCheckJs = bean.loginCheckJs + bookSource.sourceComment = bean.bookSourceComment + bookSource.lastUpdateTime = bean.lastUpdateTime + bookSource.weight = bean.weight + + + val searchRule = SearchRule() + val ruleSearch = bean.ruleSearch + searchRule.searchUrl = bean.searchUrl + searchRule.list = ruleSearch?.bookList + searchRule.name = ruleSearch?.name + searchRule.author = ruleSearch?.author + searchRule.desc = ruleSearch?.intro + searchRule.type = ruleSearch?.kind + searchRule.lastChapter = ruleSearch?.lastChapter + searchRule.updateTime = ruleSearch?.updateTime + searchRule.infoUrl = ruleSearch?.bookUrl + searchRule.imgUrl = ruleSearch?.coverUrl + searchRule.wordCount = ruleSearch?.wordCount + searchRule.isRelatedWithInfo = true + bookSource.searchRule = searchRule + + val infoRule = InfoRule() + val ruleInfo = bean.ruleBookInfo + infoRule.urlPattern = bean.bookUrlPattern + infoRule.init = ruleInfo?.init + infoRule.name = ruleInfo?.name + infoRule.author = ruleInfo?.author + infoRule.desc = ruleInfo?.intro + infoRule.type = ruleInfo?.kind + infoRule.lastChapter = ruleInfo?.lastChapter + infoRule.updateTime = ruleInfo?.updateTime + infoRule.imgUrl = ruleInfo?.coverUrl + infoRule.tocUrl = ruleInfo?.tocUrl + infoRule.wordCount = ruleInfo?.wordCount + bookSource.infoRule = infoRule + + val tocRule = TocRule() + val ruleToc = bean.ruleToc + tocRule.chapterList = ruleToc?.chapterList + tocRule.chapterName = ruleToc?.chapterName + tocRule.chapterUrl = ruleToc?.chapterUrl + tocRule.isVip = ruleToc?.isVip + tocRule.isPay = ruleToc?.isPay + tocRule.updateTime = ruleToc?.updateTime + tocRule.tocUrlNext = ruleToc?.nextTocUrl + bookSource.tocRule = tocRule + + val contentRule = ContentRule() + val ruleContent = bean.ruleContent + contentRule.content = ruleContent?.content + contentRule.contentUrlNext = ruleContent?.nextContentUrl + contentRule.replaceRegex = ruleContent?.replaceRegex + bookSource.contentRule = contentRule + + val findRule = FindRule() + val ruleFind = bean.ruleExplore + findRule.url = bean.exploreUrl + findRule.list = ruleFind?.bookList + findRule.name = ruleFind?.name + findRule.author = ruleFind?.author + findRule.desc = ruleFind?.intro + findRule.type = ruleFind?.kind + findRule.lastChapter = ruleFind?.lastChapter + findRule.updateTime = ruleFind?.updateTime + findRule.imgUrl = ruleFind?.coverUrl + findRule.infoUrl = ruleFind?.bookUrl + findRule.wordCount = ruleFind?.wordCount + bookSource.findRule = findRule + return bookSource + } + + fun source3sToSources(source3s: List): List { + val sources = mutableListOf() + source3s.forEach { + sources.add(source3ToSource(it)) + } + return sources + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/TocRule3.kt b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/TocRule3.kt new file mode 100644 index 0000000..b30300c --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/source3/TocRule3.kt @@ -0,0 +1,15 @@ +package xyz.fycz.myreader.entity.thirdsource.source3 + +/** + * @author fengyue + * @date 2022/1/20 13:50 + */ +data class TocRule3( + var chapterList: String? = null, + var chapterName: String? = null, + var chapterUrl: String? = null, + var isVip: String? = null, + var isPay: String? = null, + var updateTime: String? = null, + var nextTocUrl: String? = null +) diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/BookSource.java b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/BookSource.java index 54f7df7..b6ca3ce 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/BookSource.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/BookSource.java @@ -43,6 +43,7 @@ public class BookSource implements Parcelable, Cloneable { private String sourceType; private String sourceHeaders; private String loginUrl; + private String loginCheckJs; private String sourceComment; private String concurrentRate; private Long lastUpdateTime; @@ -114,6 +115,7 @@ public class BookSource implements Parcelable, Cloneable { sourceType = in.readString(); sourceHeaders = in.readString(); loginUrl = in.readString(); + loginCheckJs = in.readString(); sourceComment = in.readString(); concurrentRate = in.readString(); if (in.readByte() == 0) { @@ -141,6 +143,7 @@ public class BookSource implements Parcelable, Cloneable { dest.writeString(sourceType); dest.writeString(sourceHeaders); dest.writeString(loginUrl); + dest.writeString(loginCheckJs); dest.writeString(sourceComment); dest.writeString(concurrentRate); if (lastUpdateTime == null) { @@ -190,6 +193,7 @@ public class BookSource implements Parcelable, Cloneable { stringEquals(sourceType, source.sourceType) && stringEquals(sourceHeaders, source.sourceHeaders) && stringEquals(loginUrl, source.loginUrl) && + stringEquals(loginCheckJs, source.loginCheckJs) && stringEquals(sourceComment, source.sourceComment) && stringEquals(concurrentRate, source.concurrentRate) && Objects.equals(searchRule, source.searchRule) && @@ -402,6 +406,14 @@ public class BookSource implements Parcelable, Cloneable { this.loginUrl = loginUrl; } + public String getLoginCheckJs() { + return loginCheckJs; + } + + public void setLoginCheckJs(String loginCheckJs) { + this.loginCheckJs = loginCheckJs; + } + public String getConcurrentRate() { return this.concurrentRate; } diff --git a/app/src/main/java/xyz/fycz/myreader/model/sourceAnalyzer/BookSourceManager.java b/app/src/main/java/xyz/fycz/myreader/model/sourceAnalyzer/BookSourceManager.java index 2985c0c..6222116 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/sourceAnalyzer/BookSourceManager.java +++ b/app/src/main/java/xyz/fycz/myreader/model/sourceAnalyzer/BookSourceManager.java @@ -18,10 +18,13 @@ import xyz.fycz.myreader.application.App; import xyz.fycz.myreader.entity.thirdsource.BookSource3Bean; import xyz.fycz.myreader.entity.thirdsource.BookSourceBean; import xyz.fycz.myreader.entity.thirdsource.ThirdSourceUtil; +import xyz.fycz.myreader.entity.thirdsource.source3.Source3; +import xyz.fycz.myreader.entity.thirdsource.source3.Third3SourceUtil; import xyz.fycz.myreader.enums.LocalBookSource; import xyz.fycz.myreader.greendao.DbManager; import xyz.fycz.myreader.greendao.entity.rule.BookSource; import xyz.fycz.myreader.greendao.gen.BookSourceDao; +import xyz.fycz.myreader.model.third3.SourceAnalyzer; import xyz.fycz.myreader.util.SharedPreUtils; import xyz.fycz.myreader.util.help.StringHelper; import xyz.fycz.myreader.util.utils.FileUtils; @@ -355,16 +358,17 @@ public class BookSourceManager { return sources; } + List source3s = SourceAnalyzer.INSTANCE.jsonToBookSources(json); + String source3sJson = GsonExtensionsKt.getGSON().toJson(source3s); + if (source3s.size() > 0 && source3sJson.length() > source3s.size() * SOURCE_LENGTH) { + return Third3SourceUtil.INSTANCE.source3sToSources(source3s); + } + List source2s = GsonUtils.parseJArray(json, BookSourceBean.class); String source2sJson = GsonExtensionsKt.getGSON().toJson(source2s); if (source2s.size() > 0 && source2sJson.length() > source2s.size() * SOURCE_LENGTH) { return ThirdSourceUtil.source2sToSources(source2s); } - List source3s = GsonUtils.parseJArray(json, BookSource3Bean.class); - String source3sJson = GsonExtensionsKt.getGSON().toJson(source3s); - if (source3s.size() > 0 && source3sJson.length() > source3s.size() * SOURCE_LENGTH) { - return ThirdSourceUtil.source3sToSources(source3s); - } } catch (Exception e) { e.printStackTrace(); } @@ -379,6 +383,13 @@ public class BookSourceManager { return sources; } + Source3 source3 = SourceAnalyzer.INSTANCE.jsonToBookSource(json); + String source3Json = GsonExtensionsKt.getGSON().toJson(source3); + if (!StringHelper.isEmpty(source3Json) && source3Json.length() > SOURCE_LENGTH) { + sources.add(Third3SourceUtil.INSTANCE.source3ToSource(source3)); + return sources; + } + BookSourceBean source2 = GsonUtils.parseJObject(json, BookSourceBean.class); String source2Json = GsonExtensionsKt.getGSON().toJson(source2); if (!StringHelper.isEmpty(source2Json) && source2Json.length() > SOURCE_LENGTH) { @@ -386,12 +397,6 @@ public class BookSourceManager { return sources; } - BookSource3Bean source3 = GsonUtils.parseJObject(json, BookSource3Bean.class); - String source3Json = GsonExtensionsKt.getGSON().toJson(source3); - if (!StringHelper.isEmpty(source3Json) && source3Json.length() > SOURCE_LENGTH) { - sources.add(ThirdSourceUtil.source3ToSource(source3)); - return sources; - } } catch (Exception e) { e.printStackTrace(); } diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/BookType.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/BookType.kt new file mode 100644 index 0000000..4f76956 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/model/third3/BookType.kt @@ -0,0 +1,8 @@ +package xyz.fycz.myreader.model.third3 + +object BookType { + const val default = 0 // 0 文本 + const val audio = 1 // 1 音频 + const val image = 3 //图片 + const val local = "loc_book" +} \ No newline at end of file diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/SourceAnalyzer.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/SourceAnalyzer.kt new file mode 100644 index 0000000..66d29de --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/model/third3/SourceAnalyzer.kt @@ -0,0 +1,318 @@ +package xyz.fycz.myreader.model.third3 + +import android.util.Log +import androidx.annotation.Keep +import com.jayway.jsonpath.JsonPath +import xyz.fycz.myreader.common.APPCONST +import xyz.fycz.myreader.entity.thirdsource.source3.* +import xyz.fycz.myreader.util.utils.* +import java.util.regex.Pattern + +@Suppress("RegExpRedundantEscape") +object SourceAnalyzer { + private val headerPattern = Pattern.compile("@Header:\\{.+?\\}", Pattern.CASE_INSENSITIVE) + private val jsPattern = Pattern.compile("\\{\\{.+?\\}\\}", Pattern.CASE_INSENSITIVE) + + fun jsonToBookSources(json: String): List { + val bookSources = mutableListOf() + val items: List> = jsonPath.parse(json).read("$") + for (item in items) { + val jsonItem = jsonPath.parse(item) + jsonToBookSource(jsonItem.jsonString())?.let { + bookSources.add(it) + } + } + return bookSources + } + + fun jsonToBookSource(json: String): Source3? { + val source = Source3() + val sourceAny = try { + GSON.fromJsonObject(json.trim()) + } catch (e: Exception) { + null + } + try { + if (sourceAny?.ruleToc == null) { + source.apply { + val jsonItem = jsonPath.parse(json.trim()) + bookSourceUrl = jsonItem.readString("bookSourceUrl") ?: return null + bookSourceName = jsonItem.readString("bookSourceName") ?: "" + bookSourceGroup = jsonItem.readString("bookSourceGroup") + loginUrl = jsonItem.readString("loginUrl") + loginUi = jsonItem.readString("loginUi") + loginCheckJs = jsonItem.readString("loginCheckJs") + bookSourceComment = jsonItem.readString("bookSourceComment") ?: "" + bookUrlPattern = jsonItem.readString("ruleBookUrlPattern") + customOrder = jsonItem.readInt("serialNumber") ?: 0 + header = uaToHeader(jsonItem.readString("httpUserAgent")) + searchUrl = toNewUrl(jsonItem.readString("ruleSearchUrl")) + exploreUrl = toNewUrls(jsonItem.readString("ruleFindUrl")) + bookSourceType = + if (jsonItem.readString("bookSourceType") == "AUDIO") BookType.audio else BookType.default + enabled = jsonItem.readBool("enable") ?: true + if (exploreUrl.isNullOrBlank()) { + enabledExplore = false + } + ruleSearch = SearchRule3( + bookList = toNewRule(jsonItem.readString("ruleSearchList")), + name = toNewRule(jsonItem.readString("ruleSearchName")), + author = toNewRule(jsonItem.readString("ruleSearchAuthor")), + intro = toNewRule(jsonItem.readString("ruleSearchIntroduce")), + kind = toNewRule(jsonItem.readString("ruleSearchKind")), + bookUrl = toNewRule(jsonItem.readString("ruleSearchNoteUrl")), + coverUrl = toNewRule(jsonItem.readString("ruleSearchCoverUrl")), + lastChapter = toNewRule(jsonItem.readString("ruleSearchLastChapter")) + ) + ruleExplore = ExploreRule3( + bookList = toNewRule(jsonItem.readString("ruleFindList")), + name = toNewRule(jsonItem.readString("ruleFindName")), + author = toNewRule(jsonItem.readString("ruleFindAuthor")), + intro = toNewRule(jsonItem.readString("ruleFindIntroduce")), + kind = toNewRule(jsonItem.readString("ruleFindKind")), + bookUrl = toNewRule(jsonItem.readString("ruleFindNoteUrl")), + coverUrl = toNewRule(jsonItem.readString("ruleFindCoverUrl")), + lastChapter = toNewRule(jsonItem.readString("ruleFindLastChapter")) + ) + ruleBookInfo = BookInfoRule3( + init = toNewRule(jsonItem.readString("ruleBookInfoInit")), + name = toNewRule(jsonItem.readString("ruleBookName")), + author = toNewRule(jsonItem.readString("ruleBookAuthor")), + intro = toNewRule(jsonItem.readString("ruleIntroduce")), + kind = toNewRule(jsonItem.readString("ruleBookKind")), + coverUrl = toNewRule(jsonItem.readString("ruleCoverUrl")), + lastChapter = toNewRule(jsonItem.readString("ruleBookLastChapter")), + tocUrl = toNewRule(jsonItem.readString("ruleChapterUrl")) + ) + ruleToc = TocRule3( + chapterList = toNewRule(jsonItem.readString("ruleChapterList")), + chapterName = toNewRule(jsonItem.readString("ruleChapterName")), + chapterUrl = toNewRule(jsonItem.readString("ruleContentUrl")), + nextTocUrl = toNewRule(jsonItem.readString("ruleChapterUrlNext")) + ) + var content = toNewRule(jsonItem.readString("ruleBookContent")) ?: "" + if (content.startsWith("$") && !content.startsWith("$.")) { + content = content.substring(1) + } + ruleContent = ContentRule3( + content = content, + replaceRegex = toNewRule(jsonItem.readString("ruleBookContentReplace")), + nextContentUrl = toNewRule(jsonItem.readString("ruleContentUrlNext")) + ) + } + } else { + source.bookSourceUrl = sourceAny.bookSourceUrl + source.bookSourceName = sourceAny.bookSourceName + source.bookSourceGroup = sourceAny.bookSourceGroup + source.bookSourceType = sourceAny.bookSourceType + source.bookUrlPattern = sourceAny.bookUrlPattern + source.customOrder = sourceAny.customOrder + source.enabled = sourceAny.enabled + source.enabledExplore = sourceAny.enabledExplore + source.concurrentRate = sourceAny.concurrentRate + source.header = sourceAny.header + source.loginUrl = when (sourceAny.loginUrl) { + null -> null + is String -> sourceAny.loginUrl.toString() + else -> JsonPath.parse(sourceAny.loginUrl).readString("url") + } + source.loginUi = if (sourceAny.loginUi is List<*>) { + GSON.toJson(sourceAny.loginUi) + } else { + sourceAny.loginUi?.toString() + } + source.loginCheckJs = sourceAny.loginCheckJs + source.bookSourceComment = sourceAny.bookSourceComment + source.lastUpdateTime = sourceAny.lastUpdateTime + source.respondTime = sourceAny.respondTime + source.weight = sourceAny.weight + source.exploreUrl = sourceAny.exploreUrl + source.ruleExplore = if (sourceAny.ruleExplore is String) { + GSON.fromJsonObject(sourceAny.ruleExplore.toString()) + } else { + GSON.fromJsonObject(GSON.toJson(sourceAny.ruleExplore)) + } + source.searchUrl = sourceAny.searchUrl + source.ruleSearch = if (sourceAny.ruleSearch is String) { + GSON.fromJsonObject(sourceAny.ruleSearch.toString()) + } else { + GSON.fromJsonObject(GSON.toJson(sourceAny.ruleSearch)) + } + source.ruleBookInfo = if (sourceAny.ruleBookInfo is String) { + GSON.fromJsonObject(sourceAny.ruleBookInfo.toString()) + } else { + GSON.fromJsonObject(GSON.toJson(sourceAny.ruleBookInfo)) + } + source.ruleToc = if (sourceAny.ruleToc is String) { + GSON.fromJsonObject(sourceAny.ruleToc.toString()) + } else { + GSON.fromJsonObject(GSON.toJson(sourceAny.ruleToc)) + } + source.ruleContent = if (sourceAny.ruleContent is String) { + GSON.fromJsonObject(sourceAny.ruleContent.toString()) + } else { + GSON.fromJsonObject(GSON.toJson(sourceAny.ruleContent)) + } + } + } catch (e: Exception) { + Log.e("SourceAnalyzer", "" + e.localizedMessage) + } + return source + } + + @Keep + data class BookSourceAny( + var bookSourceName: String = "", // 名称 + var bookSourceGroup: String? = null, // 分组 + var bookSourceUrl: String = "", // 地址,包括 http/https + var bookSourceType: Int = BookType.default, // 类型,0 文本,1 音频 + var bookUrlPattern: String? = null, // 详情页url正则 + var customOrder: Int = 0, // 手动排序编号 + var enabled: Boolean = true, // 是否启用 + var enabledExplore: Boolean = true, // 启用发现 + var concurrentRate: String? = null, // 并发率 + var header: String? = null, // 请求头 + var loginUrl: Any? = null, // 登录规则 + var loginUi: Any? = null, // 登录UI + var loginCheckJs: String? = null, //登录检测js + var bookSourceComment: String? = "", //书源注释 + var lastUpdateTime: Long = 0, // 最后更新时间,用于排序 + var respondTime: Long = 180000L, // 响应时间,用于排序 + var weight: Int = 0, // 智能排序的权重 + var exploreUrl: String? = null, // 发现url + var ruleExplore: Any? = null, // 发现规则 + var searchUrl: String? = null, // 搜索url + var ruleSearch: Any? = null, // 搜索规则 + var ruleBookInfo: Any? = null, // 书籍信息页规则 + var ruleToc: Any? = null, // 目录页规则 + var ruleContent: Any? = null // 正文页规则 + ) + + // default规则适配 + // #正则#替换内容 替换成 ##正则##替换内容 + // | 替换成 || + // & 替换成 && + private fun toNewRule(oldRule: String?): String? { + if (oldRule.isNullOrBlank()) return null + var newRule = oldRule + var reverse = false + var allinone = false + if (oldRule.startsWith("-")) { + reverse = true + newRule = oldRule.substring(1) + } + if (newRule.startsWith("+")) { + allinone = true + newRule = newRule.substring(1) + } + if (!newRule.startsWith("@CSS:", true) && + !newRule.startsWith("@XPath:", true) && + !newRule.startsWith("//") && + !newRule.startsWith("##") && + !newRule.startsWith(":") && + !newRule.contains("@js:", true) && + !newRule.contains("", true) + ) { + if (newRule.contains("#") && !newRule.contains("##")) { + newRule = oldRule.replace("#", "##") + } + if (newRule.contains("|") && !newRule.contains("||")) { + if (newRule.contains("##")) { + val list = newRule.split("##") + if (list[0].contains("|")) { + newRule = list[0].replace("|", "||") + for (i in 1 until list.size) { + newRule += "##" + list[i] + } + } + } else { + newRule = newRule.replace("|", "||") + } + } + if (newRule.contains("&") + && !newRule.contains("&&") + && !newRule.contains("http") + && !newRule.startsWith("/") + ) { + newRule = newRule.replace("&", "&&") + } + } + if (allinone) { + newRule = "+$newRule" + } + if (reverse) { + newRule = "-$newRule" + } + return newRule + } + + private fun toNewUrls(oldUrls: String?): String? { + if (oldUrls.isNullOrBlank()) return null + if (oldUrls.startsWith("@js:") || oldUrls.startsWith("")) { + return oldUrls + } + if (!oldUrls.contains("\n") && !oldUrls.contains("&&")) { + return toNewUrl(oldUrls) + } + val urls = oldUrls.split("(&&|\r?\n)+".toRegex()) + return urls.map { + toNewUrl(it)?.replace("\n\\s*".toRegex(), "") + }.joinToString("\n") + } + + private fun toNewUrl(oldUrl: String?): String? { + if (oldUrl.isNullOrBlank()) return null + var url: String = oldUrl + if (oldUrl.startsWith("", true)) { + url = url.replace("=searchKey", "={{key}}") + .replace("=searchPage", "={{page}}") + return url + } + val map = HashMap() + var mather = headerPattern.matcher(url) + if (mather.find()) { + val header = mather.group() + url = url.replace(header, "") + map["headers"] = header.substring(8) + } + var urlList = url.split("|") + url = urlList[0] + if (urlList.size > 1) { + map["charset"] = urlList[1].split("=")[1] + } + mather = jsPattern.matcher(url) + val jsList = arrayListOf() + while (mather.find()) { + jsList.add(mather.group()) + url = url.replace(jsList.last(), "$${jsList.size - 1}") + } + url = url.replace("{", "<").replace("}", ">") + url = url.replace("searchKey", "{{key}}") + url = url.replace("".toRegex(), "{{page$1}}") + .replace("searchPage([-+]1)".toRegex(), "{{page$1}}") + .replace("searchPage", "{{page}}") + for ((index, item) in jsList.withIndex()) { + url = url.replace( + "$$index", + item.replace("searchKey", "key").replace("searchPage", "page") + ) + } + urlList = url.split("@") + url = urlList[0] + if (urlList.size > 1) { + map["method"] = "POST" + map["body"] = urlList[1] + } + if (map.size > 0) { + url += "," + GSON.toJson(map) + } + return url + } + + private fun uaToHeader(ua: String?): String? { + if (ua.isNullOrEmpty()) return null + val map = mapOf(Pair(APPCONST.UA_NAME, ua)) + return GSON.toJson(map) + } + +} diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/WebBook.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/WebBook.kt index f864c81..dabd0a1 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/WebBook.kt +++ b/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/WebBook.kt @@ -51,11 +51,11 @@ object WebBook { ) var res = analyzeUrl.getStrResponseAwait() //检测书源是否已登录 - /*bookSource.loginCheckJs?.let { checkJs -> + bookSource.loginCheckJs?.let { checkJs -> if (checkJs.isNotBlank()) { res = analyzeUrl.evalJS(checkJs, res) as StrResponse } - }*/ + } return BookList.analyzeBookList( scope, bookSource, @@ -101,11 +101,11 @@ object WebBook { ) var res = analyzeUrl.getStrResponseAwait() //检测书源是否已登录 - /*bookSource.loginCheckJs?.let { checkJs -> + bookSource.loginCheckJs?.let { checkJs -> if (checkJs.isNotBlank()) { res = analyzeUrl.evalJS(checkJs, result = res) as StrResponse } - }*/ + } return BookList.analyzeBookList( scope, bookSource, @@ -159,11 +159,11 @@ object WebBook { ) var res = analyzeUrl.getStrResponseAwait() //检测书源是否已登录 - /*bookSource.loginCheckJs?.let { checkJs -> + bookSource.loginCheckJs?.let { checkJs -> if (checkJs.isNotBlank()) { res = analyzeUrl.evalJS(checkJs, result = res) as StrResponse } - }*/ + } BookInfo.analyzeBookInfo( scope, bookSource, @@ -215,11 +215,11 @@ object WebBook { ) var res = analyzeUrl.getStrResponseAwait() //检测书源是否已登录 - /*bookSource.loginCheckJs?.let { checkJs -> + bookSource.loginCheckJs?.let { checkJs -> if (checkJs.isNotBlank()) { res = analyzeUrl.evalJS(checkJs, result = res) as StrResponse } - }*/ + } BookChapterList.analyzeChapterList( scope, bookSource, @@ -284,11 +284,11 @@ object WebBook { )*/ var res = analyzeUrl.getStrResponseAwait() //检测书源是否已登录 - /*bookSource.loginCheckJs?.let { checkJs -> + bookSource.loginCheckJs?.let { checkJs -> if (checkJs.isNotBlank()) { res = analyzeUrl.evalJS(checkJs, result = res) as StrResponse } - }*/ + } BookContent.analyzeContent( scope, bookSource, diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/JsonExtensions.kt b/app/src/main/java/xyz/fycz/myreader/util/utils/JsonExtensions.kt new file mode 100644 index 0000000..da091ae --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/JsonExtensions.kt @@ -0,0 +1,20 @@ +package xyz.fycz.myreader.util.utils + +import com.jayway.jsonpath.* + +val jsonPath: ParseContext by lazy { + JsonPath.using( + Configuration.builder() + .options(Option.SUPPRESS_EXCEPTIONS) + .build() + ) +} + +fun ReadContext.readString(path: String): String? = this.read(path, String::class.java) + +fun ReadContext.readBool(path: String): Boolean? = this.read(path, Boolean::class.java) + +fun ReadContext.readInt(path: String): Int? = this.read(path, Int::class.java) + +fun ReadContext.readLong(path: String): Long? = this.read(path, Long::class.java) +