diff --git a/app/build.gradle b/app/build.gradle index 20b7047..c0103d7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -154,6 +154,7 @@ dependencies { implementation 'androidx.core:core-ktx:1.7.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' //anko def anko_version = '0.10.8' diff --git a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java index b74cf23..28988a2 100644 --- a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java +++ b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java @@ -52,6 +52,7 @@ public class APPCONST { public static final String XPATH = "Xpath"; public static final String JSON_PATH = "JsonPath"; public static final String THIRD_SOURCE = "ThirdSource"; + public static final String THIRD_3_SOURCE = "Third3Source"; public static final String DATA_KEY = "data_key"; public static final String FIND_CRAWLER = "findCrawler"; diff --git a/app/src/main/java/xyz/fycz/myreader/entity/sourceedit/EditEntityUtil.kt b/app/src/main/java/xyz/fycz/myreader/entity/sourceedit/EditEntityUtil.kt index 7366032..1f039cd 100644 --- a/app/src/main/java/xyz/fycz/myreader/entity/sourceedit/EditEntityUtil.kt +++ b/app/src/main/java/xyz/fycz/myreader/entity/sourceedit/EditEntityUtil.kt @@ -2,7 +2,6 @@ package xyz.fycz.myreader.entity.sourceedit import xyz.fycz.myreader.R import xyz.fycz.myreader.greendao.entity.rule.* -import java.util.* import kotlin.collections.ArrayList /** @@ -85,7 +84,7 @@ object EditEntityUtil { add(EditEntity("url", findRule?.url, R.string.r_find_url)) add( EditEntity( - "bookList", findRule?.bookList, R.string.r_book_list, + "bookList", findRule?.list, R.string.r_book_list, "对于Matcher解析器:此处填写书籍列表所在区间,仅支持普通函数;" + "\n对于Xpath/JsonPath解析器:此处填写书籍列表规则,仅支持列表函数" ) @@ -154,6 +153,18 @@ object EditEntityUtil { "对于Xpath/JsonPath解析器:此处填写章节URL规则" ) ) + add( + EditEntity( + "isVip", tocRule?.isVip, R.string.rule_is_vip, + "" + ) + ) + add( + EditEntity( + "updateTime", tocRule?.updateTime, R.string.rule_update_time, + "" + ) + ) add( EditEntity( "tocUrlNext", tocRule?.tocUrlNext, R.string.rule_next_toc_url, @@ -180,6 +191,12 @@ object EditEntityUtil { "填写后正文时将会不断地从下一页获取内容,直至下一页URL为空时停止,注意:千万不要获取恒存在的URL,否则将出现死循环甚至崩溃" ) ) + add( + EditEntity( + "replaceRegex", contentRule?.replaceRegex, R.string.rule_replace_regex, + "" + ) + ) } return contentEntities } @@ -231,7 +248,7 @@ object EditEntityUtil { findEntities.forEach { when (it.key) { "url" -> findRule.url = it.value - "bookList" -> findRule.bookList = it.value + "bookList" -> findRule.list = it.value "name" -> findRule.name = it.value "author" -> findRule.author = it.value "type" -> findRule.type = it.value @@ -277,6 +294,8 @@ object EditEntityUtil { "chapterBaseUrl" -> tocRule.chapterBaseUrl = it.value "chapterName" -> tocRule.chapterName = it.value "chapterUrl" -> tocRule.chapterUrl = it.value + "isVip" -> tocRule.isVip = it.value + "updateTime" -> tocRule.updateTime = it.value "tocUrlNext" -> tocRule.tocUrlNext = it.value } } @@ -290,6 +309,7 @@ object EditEntityUtil { "content" -> contentRule.content = it.value "contentBaseUrl" -> contentRule.contentBaseUrl = it.value "contentUrlNext" -> contentRule.contentUrlNext = it.value + "replaceRegex" -> contentRule.replaceRegex = it.value } } return contentRule diff --git a/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/ThirdSourceUtil.java b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/ThirdSourceUtil.java index 26ce800..7eebdba 100644 --- a/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/ThirdSourceUtil.java +++ b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/ThirdSourceUtil.java @@ -67,7 +67,7 @@ public class ThirdSourceUtil { FindRule findRule = new FindRule(); findRule.setUrl(bean.getRuleFindUrl()); - findRule.setBookList(bean.getRuleFindList()); + findRule.setList(bean.getRuleFindList()); findRule.setName(bean.getRuleFindName()); findRule.setAuthor(bean.getRuleFindAuthor()); findRule.setType(bean.getRuleFindKind()); diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/entity/Chapter.java b/app/src/main/java/xyz/fycz/myreader/greendao/entity/Chapter.java index d364723..ace97b7 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/entity/Chapter.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/entity/Chapter.java @@ -37,6 +37,9 @@ public class Chapter implements RuleDataInterface { private int number;//章节序号 private String title;//章节标题 private String url;//章节链接(本地书籍为:字符编码) + private boolean isVip;//是否VIP + private boolean isPay;//是否已购买 + private String updateTime;//更新时间 @Nullable private String content;//章节正文 @@ -49,14 +52,19 @@ public class Chapter implements RuleDataInterface { @Transient private Map variableMap; - @Generated(hash = 1398484308) + + @Generated(hash = 1109296579) public Chapter(String id, String bookId, int number, String title, String url, - String content, long start, long end, String variable) { + boolean isVip, boolean isPay, String updateTime, String content, + long start, long end, String variable) { this.id = id; this.bookId = bookId; this.number = number; this.title = title; this.url = url; + this.isVip = isVip; + this.isPay = isPay; + this.updateTime = updateTime; this.content = content; this.start = start; this.end = end; @@ -67,6 +75,7 @@ public class Chapter implements RuleDataInterface { public Chapter() { } + public String getId() { return this.id; } @@ -169,4 +178,28 @@ public class Chapter implements RuleDataInterface { public void setVariable(String variable) { this.variable = variable; } + + public String getUpdateTime() { + return this.updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public boolean getIsVip() { + return this.isVip; + } + + public void setIsVip(boolean isVip) { + this.isVip = isVip; + } + + public boolean getIsPay() { + return this.isPay; + } + + public void setIsPay(boolean isPay) { + this.isPay = isPay; + } } diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/BookListRule.kt b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/BookListRule.kt new file mode 100644 index 0000000..d4acb2e --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/BookListRule.kt @@ -0,0 +1,14 @@ +package xyz.fycz.myreader.greendao.entity.rule + +interface BookListRule { + var list: String? + var name: String? + var author: String? + var desc: String? + var type: String? + var lastChapter: String? + var updateTime: String? + var infoUrl: String? + var imgUrl: String? + var wordCount: String? +} \ No newline at end of file diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/ContentRule.java b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/ContentRule.java index 71ba5ab..f89bd21 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/ContentRule.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/ContentRule.java @@ -19,11 +19,13 @@ public class ContentRule implements Parcelable { private String content; private String contentBaseUrl; private String contentUrlNext; + private String replaceRegex; protected ContentRule(Parcel in) { content = in.readString(); contentBaseUrl = in.readString(); contentUrlNext = in.readString(); + replaceRegex = in.readString(); } @Override @@ -31,6 +33,7 @@ public class ContentRule implements Parcelable { dest.writeString(content); dest.writeString(contentBaseUrl); dest.writeString(contentUrlNext); + dest.writeString(replaceRegex); } @Override @@ -58,7 +61,8 @@ public class ContentRule implements Parcelable { ContentRule that = (ContentRule) o; return stringEquals(content, that.content) && stringEquals(contentBaseUrl, that.contentBaseUrl) && - stringEquals(contentUrlNext, that.contentUrlNext); + stringEquals(contentUrlNext, that.contentUrlNext) && + stringEquals(replaceRegex, that.replaceRegex); } @@ -89,5 +93,11 @@ public class ContentRule implements Parcelable { this.contentUrlNext = contentUrlNext; } + public String getReplaceRegex() { + return replaceRegex; + } + public void setReplaceRegex(String replaceRegex) { + this.replaceRegex = replaceRegex; + } } diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/FindRule.java b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/FindRule.java index 6e33326..5b5c836 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/FindRule.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/FindRule.java @@ -3,19 +3,15 @@ package xyz.fycz.myreader.greendao.entity.rule; import android.os.Parcel; import android.os.Parcelable; -import org.jetbrains.annotations.Nullable; - -import java.util.Objects; - import static xyz.fycz.myreader.util.utils.StringUtils.stringEquals; /** * @author fengyue * @date 2021/2/10 8:57 */ -public class FindRule implements Parcelable { +public class FindRule implements Parcelable, BookListRule { private String url; - private String bookList; + private String list; private String name; private String author; private String type; @@ -33,7 +29,7 @@ public class FindRule implements Parcelable { protected FindRule(Parcel in) { url = in.readString(); - bookList = in.readString(); + list = in.readString(); name = in.readString(); author = in.readString(); type = in.readString(); @@ -50,7 +46,7 @@ public class FindRule implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(url); - dest.writeString(bookList); + dest.writeString(list); dest.writeString(name); dest.writeString(author); dest.writeString(type); @@ -89,12 +85,12 @@ public class FindRule implements Parcelable { this.url = url; } - public String getBookList() { - return bookList; + public String getList() { + return list; } - public void setBookList(String bookList) { - this.bookList = bookList; + public void setList(String bookList) { + this.list = bookList; } public String getName() { @@ -192,7 +188,7 @@ public class FindRule implements Parcelable { if (getClass() != o.getClass()) return false; FindRule findRule = (FindRule) o; return stringEquals(url, findRule.url) && - stringEquals(bookList, findRule.bookList) && + stringEquals(list, findRule.list) && stringEquals(name, findRule.name) && stringEquals(author, findRule.author) && stringEquals(type, findRule.type) && diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/SearchRule.java b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/SearchRule.java index bef4856..570438f 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/SearchRule.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/SearchRule.java @@ -17,7 +17,7 @@ import static xyz.fycz.myreader.util.utils.StringUtils.stringEquals; * @author fengyue * @date 2021/2/8 17:48 */ -public class SearchRule implements Parcelable { +public class SearchRule implements Parcelable, BookListRule { private String searchUrl; private String charset; private String list; diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/TocRule.java b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/TocRule.java index fec6cb5..ffdd779 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/TocRule.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/entity/rule/TocRule.java @@ -22,6 +22,9 @@ public class TocRule implements Parcelable { private String chapterName; private String chapterUrl; private String tocUrlNext; + private String isVip; + private String isPay; + private String updateTime; protected TocRule(Parcel in) { chapterList = in.readString(); @@ -29,6 +32,9 @@ public class TocRule implements Parcelable { chapterName = in.readString(); chapterUrl = in.readString(); tocUrlNext = in.readString(); + isVip = in.readString(); + isPay = in.readString(); + updateTime = in.readString(); } @Override @@ -38,6 +44,9 @@ public class TocRule implements Parcelable { dest.writeString(chapterName); dest.writeString(chapterUrl); dest.writeString(tocUrlNext); + dest.writeString(isVip); + dest.writeString(isPay); + dest.writeString(updateTime); } @Override @@ -67,7 +76,10 @@ public class TocRule implements Parcelable { stringEquals(chapterBaseUrl, tocRule.chapterBaseUrl) && stringEquals(chapterName, tocRule.chapterName) && stringEquals(chapterUrl, tocRule.chapterUrl) && - stringEquals(tocUrlNext, tocRule.tocUrlNext); + stringEquals(tocUrlNext, tocRule.tocUrlNext) && + stringEquals(isVip, tocRule.isVip) && + stringEquals(isPay, tocRule.isPay) && + stringEquals(updateTime, tocRule.updateTime); } public String getChapterList() { @@ -110,6 +122,30 @@ public class TocRule implements Parcelable { this.tocUrlNext = tocUrlNext; } + public String getIsVip() { + return isVip; + } + + public void setIsVip(String isVip) { + this.isVip = isVip; + } + + public String getIsPay() { + return isPay; + } + + public void setIsPay(String isPay) { + this.isPay = isPay; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + public TocRule() { } diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java b/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java index c9e8c0c..46b52e9 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java @@ -39,10 +39,12 @@ public class ChapterService extends BaseService { chapter.setBookId(cursor.getString(1)); chapter.setNumber(cursor.getInt(2)); chapter.setTitle(cursor.getString(3)); - chapter.setUrl(cursor.getString(4)); - chapter.setContent(cursor.getString(5)); - chapter.setStart(cursor.getInt(6)); - chapter.setEnd(cursor.getInt(7)); + chapter.setIsVip(cursor.getInt(4) != 0); + chapter.setIsPay(cursor.getInt(5) != 0); + chapter.setUpdateTime(cursor.getString(6)); + chapter.setContent(cursor.getString(7)); + chapter.setStart(cursor.getInt(8)); + chapter.setEnd(cursor.getInt(9)); chapters.add(chapter); } } catch (Exception e) { diff --git a/app/src/main/java/xyz/fycz/myreader/model/third2/content/BookList.java b/app/src/main/java/xyz/fycz/myreader/model/third2/content/BookList.java index dff9157..2668931 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/third2/content/BookList.java +++ b/app/src/main/java/xyz/fycz/myreader/model/third2/content/BookList.java @@ -147,9 +147,9 @@ public class BookList { } private void initRule() { - if (isFind && !TextUtils.isEmpty(bookSource.getFindRule().getBookList())) { + if (isFind && !TextUtils.isEmpty(bookSource.getFindRule().getList())) { FindRule findRule = bookSource.getFindRule(); - ruleList = findRule.getBookList(); + ruleList = findRule.getList(); ruleName = findRule.getName(); ruleAuthor = findRule.getAuthor(); ruleKind = findRule.getType(); diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/Coroutine.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/Coroutine.kt new file mode 100644 index 0000000..e2aafa1 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/model/third3/Coroutine.kt @@ -0,0 +1,217 @@ +package xyz.fycz.myreader.model.third3 + +import android.util.Log +import kotlinx.coroutines.* +import kotlin.coroutines.CoroutineContext + + +@Suppress("unused") +class Coroutine( + val scope: CoroutineScope, + context: CoroutineContext = Dispatchers.IO, + block: suspend CoroutineScope.() -> T +) { + + companion object { + + private val DEFAULT = MainScope() + + fun async( + scope: CoroutineScope = DEFAULT, + context: CoroutineContext = Dispatchers.IO, + block: suspend CoroutineScope.() -> T + ): Coroutine { + return Coroutine(scope, context, block) + } + + } + + private val job: Job + + private var start: VoidCallback? = null + private var success: Callback? = null + private var error: Callback? = null + private var finally: VoidCallback? = null + private var cancel: VoidCallback? = null + + private var timeMillis: Long? = null + private var errorReturn: Result? = null + + val isCancelled: Boolean + get() = job.isCancelled + + val isActive: Boolean + get() = job.isActive + + val isCompleted: Boolean + get() = job.isCompleted + + init { + this.job = executeInternal(context, block) + } + + fun timeout(timeMillis: () -> Long): Coroutine { + this.timeMillis = timeMillis() + return this@Coroutine + } + + fun timeout(timeMillis: Long): Coroutine { + this.timeMillis = timeMillis + return this@Coroutine + } + + fun onErrorReturn(value: () -> T?): Coroutine { + this.errorReturn = Result(value()) + return this@Coroutine + } + + fun onErrorReturn(value: T?): Coroutine { + this.errorReturn = Result(value) + return this@Coroutine + } + + fun onStart( + context: CoroutineContext? = null, + block: (suspend CoroutineScope.() -> Unit) + ): Coroutine { + this.start = VoidCallback(context, block) + return this@Coroutine + } + + fun onSuccess( + context: CoroutineContext? = null, + block: suspend CoroutineScope.(T) -> Unit + ): Coroutine { + this.success = Callback(context, block) + return this@Coroutine + } + + fun onError( + context: CoroutineContext? = null, + block: suspend CoroutineScope.(Throwable) -> Unit + ): Coroutine { + this.error = Callback(context, block) + return this@Coroutine + } + + fun onFinally( + context: CoroutineContext? = null, + block: suspend CoroutineScope.() -> Unit + ): Coroutine { + this.finally = VoidCallback(context, block) + return this@Coroutine + } + + fun onCancel( + context: CoroutineContext? = null, + block: suspend CoroutineScope.() -> Unit + ): Coroutine { + this.cancel = VoidCallback(context, block) + return this@Coroutine + } + + //取消当前任务 + fun cancel(cause: CancellationException? = null) { + job.cancel(cause) + cancel?.let { + MainScope().launch { + if (null == it.context) { + it.block.invoke(scope) + } else { + withContext(scope.coroutineContext.plus(it.context)) { + it.block.invoke(this) + } + } + } + } + } + + fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle { + return job.invokeOnCompletion(handler) + } + + private fun executeInternal( + context: CoroutineContext, + block: suspend CoroutineScope.() -> T + ): Job { + return scope.plus(Dispatchers.Main).launch { + try { + start?.let { dispatchVoidCallback(this, it) } + ensureActive() + val value = executeBlock(scope, context, timeMillis ?: 0L, block) + ensureActive() + success?.let { dispatchCallback(this, value, it) } + } catch (e: CancellationException) { + Log.d("Coroutine", "任务取消") + } catch (e: Throwable) { + e.printStackTrace() + Log.d("Coroutine", "" + e.localizedMessage) + val consume: Boolean = errorReturn?.value?.let { value -> + if (isActive) { + success?.let { dispatchCallback(this, value, it) } + } + true + } ?: false + if (!consume && isActive) { + error?.let { dispatchCallback(this, e, it) } + } + } finally { + if (isActive) { + finally?.let { dispatchVoidCallback(this, it) } + } + } + } + } + + private suspend inline fun dispatchVoidCallback(scope: CoroutineScope, callback: VoidCallback) { + if (null == callback.context) { + callback.block.invoke(scope) + } else { + withContext(scope.coroutineContext.plus(callback.context)) { + callback.block.invoke(this) + } + } + } + + private suspend inline fun dispatchCallback( + scope: CoroutineScope, + value: R, + callback: Callback + ) { + if (!scope.isActive) return + if (null == callback.context) { + callback.block.invoke(scope, value) + } else { + withContext(scope.coroutineContext.plus(callback.context)) { + callback.block.invoke(this, value) + } + } + } + + private suspend inline fun executeBlock( + scope: CoroutineScope, + context: CoroutineContext, + timeMillis: Long, + noinline block: suspend CoroutineScope.() -> T + ): T { + return withContext(scope.coroutineContext.plus(context)) { + if (timeMillis > 0L) withTimeout(timeMillis) { + block() + } else { + block() + } + } + } + + private data class Result(val value: T?) + + private inner class VoidCallback( + val context: CoroutineContext?, + val block: suspend CoroutineScope.() -> Unit + ) + + private inner class Callback( + val context: CoroutineContext?, + val block: suspend CoroutineScope.(VALUE) -> Unit + ) +} diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookChapterList.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookChapterList.kt index a413e38..77d379d 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookChapterList.kt +++ b/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookChapterList.kt @@ -1,14 +1,7 @@ package xyz.fycz.myreader.model.third3.webBook import android.text.TextUtils -import io.legado.app.R -import io.legado.app.data.entities.Book -import io.legado.app.data.entities.BookChapter -import io.legado.app.data.entities.BookSource -import xyz.fycz.myreader.model.third3.rule.TocRule -import io.legado.app.model.Debug -import io.legado.app.model.NoStackTraceException -import io.legado.app.model.TocEmptyException +import android.util.Log import xyz.fycz.myreader.model.third3.analyzeRule.AnalyzeRule import xyz.fycz.myreader.model.third3.analyzeRule.AnalyzeUrl import kotlinx.coroutines.CoroutineScope @@ -16,7 +9,15 @@ import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.async import kotlinx.coroutines.ensureActive import kotlinx.coroutines.withContext -import splitties.init.appCtx +import xyz.fycz.myreader.R +import xyz.fycz.myreader.application.App +import xyz.fycz.myreader.greendao.entity.Book +import xyz.fycz.myreader.greendao.entity.Chapter +import xyz.fycz.myreader.greendao.entity.rule.BookSource +import xyz.fycz.myreader.greendao.entity.rule.TocRule +import xyz.fycz.myreader.model.third3.NoStackTraceException +import xyz.fycz.myreader.model.third3.TocEmptyException + /** * 获取目录 @@ -31,15 +32,15 @@ object BookChapterList { book: Book, redirectUrl: String, baseUrl: String, - body: String? - ): List { + body: String?, + ): List { body ?: throw NoStackTraceException( - appCtx.getString(R.string.error_get_web_content, baseUrl) + App.getmContext().getString(R.string.error_get_web_content, baseUrl) ) - val chapterList = ArrayList() - Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") - Debug.log(bookSource.bookSourceUrl, body, state = 30) - val tocRule = bookSource.getTocRule() + val chapterList = ArrayList() + Log.d(bookSource.sourceUrl, "≡获取成功:${baseUrl}") + Log.d(bookSource.sourceUrl, body) + val tocRule = bookSource.tocRule val nextUrlList = arrayListOf(baseUrl) var reverse = false var listRule = tocRule.chapterList ?: "" @@ -66,7 +67,7 @@ object BookChapterList { mUrl = nextUrl, source = bookSource, ruleData = book, - headerMapF = bookSource.getHeaderMap() + //headerMapF = bookSource.getHeaderMap() ).getStrResponseAwait().body?.let { nextBody -> chapterData = analyzeChapterList( scope, book, nextUrl, nextUrl, @@ -76,10 +77,10 @@ object BookChapterList { chapterList.addAll(chapterData.first) } } - Debug.log(bookSource.bookSourceUrl, "◇目录总页数:${nextUrlList.size}") + Log.d(bookSource.sourceUrl, "◇目录总页数:${nextUrlList.size}") } else -> { - Debug.log(bookSource.bookSourceUrl, "◇并发解析目录,总页数:${chapterData.second.size}") + Log.d(bookSource.sourceUrl, "◇并发解析目录,总页数:${chapterData.second.size}") withContext(IO) { val asyncArray = Array(chapterData.second.size) { async(IO) { @@ -88,7 +89,7 @@ object BookChapterList { mUrl = urlStr, source = bookSource, ruleData = book, - headerMapF = bookSource.getHeaderMap() + //headerMapF = bookSource.getHeaderMap() ) val res = analyzeUrl.getStrResponseAwait() analyzeChapterList( @@ -104,7 +105,7 @@ object BookChapterList { } } if (chapterList.isEmpty()) { - throw TocEmptyException(appCtx.getString(R.string.chapter_list_empty)) + throw TocEmptyException(App.getmContext().getString(R.string.chapter_list_empty)) } //去重 if (!reverse) { @@ -112,22 +113,22 @@ object BookChapterList { } val lh = LinkedHashSet(chapterList) val list = ArrayList(lh) - if (!book.getReverseToc()) { + /*if (!book.getReverseToc()) { list.reverse() - } - Debug.log(book.origin, "◇目录总数:${list.size}") + }*/ + Log.d(book.source, "◇目录总数:${list.size}") list.forEachIndexed { index, bookChapter -> - bookChapter.index = index + bookChapter.number = index } - book.latestChapterTitle = list.last().title - book.durChapterTitle = - list.getOrNull(book.durChapterIndex)?.title ?: book.latestChapterTitle - if (book.totalChapterNum < list.size) { - book.lastCheckCount = list.size - book.totalChapterNum - book.latestChapterTime = System.currentTimeMillis() + book.newestChapterTitle = list.last().title + book.historyChapterId = + list.getOrNull(book.histtoryChapterNum)?.title ?: book.newestChapterTitle + if (book.chapterTotalNum < list.size) { + book.noReadNum = list.size - book.chapterTotalNum + book.lastReadTime = System.currentTimeMillis() } - book.lastCheckTime = System.currentTimeMillis() - book.totalChapterNum = list.size + book.lastReadTime = System.currentTimeMillis() + book.chapterTotalNum = list.size return list } @@ -142,20 +143,20 @@ object BookChapterList { bookSource: BookSource, getNextUrl: Boolean = true, log: Boolean = false - ): Pair, List> { + ): Pair, List> { val analyzeRule = AnalyzeRule(book, bookSource) analyzeRule.setContent(body).setBaseUrl(baseUrl) analyzeRule.setRedirectUrl(redirectUrl) //获取目录列表 - val chapterList = arrayListOf() - Debug.log(bookSource.bookSourceUrl, "┌获取目录列表", log) + val chapterList = arrayListOf() + if (log) Log.d(bookSource.sourceUrl, "┌获取目录列表") val elements = analyzeRule.getElements(listRule) - Debug.log(bookSource.bookSourceUrl, "└列表大小:${elements.size}", log) + if (log) Log.d(bookSource.sourceUrl, "└列表大小:${elements.size}",) //获取下一页链接 val nextUrlList = arrayListOf() - val nextTocRule = tocRule.nextTocUrl + val nextTocRule = tocRule.tocUrlNext if (getNextUrl && !nextTocRule.isNullOrEmpty()) { - Debug.log(bookSource.bookSourceUrl, "┌获取目录下一页列表", log) + if (log) Log.d(bookSource.sourceUrl, "┌获取目录下一页列表") analyzeRule.getStringList(nextTocRule, isUrl = true)?.let { for (item in it) { if (item != baseUrl) { @@ -163,15 +164,11 @@ object BookChapterList { } } } - Debug.log( - bookSource.bookSourceUrl, - "└" + TextUtils.join(",\n", nextUrlList), - log - ) + if (log) Log.d(bookSource.sourceUrl, "└" + TextUtils.join(",\n", nextUrlList),) } scope.ensureActive() if (elements.isNotEmpty()) { - Debug.log(bookSource.bookSourceUrl, "┌解析目录列表", log) + if (log) Log.d(bookSource.sourceUrl, "┌解析目录列表") val nameRule = analyzeRule.splitSourceRule(tocRule.chapterName) val urlRule = analyzeRule.splitSourceRule(tocRule.chapterUrl) val vipRule = analyzeRule.splitSourceRule(tocRule.isVip) @@ -180,14 +177,15 @@ object BookChapterList { elements.forEachIndexed { index, item -> scope.ensureActive() analyzeRule.setContent(item) - val bookChapter = BookChapter(bookUrl = book.bookUrl, baseUrl = baseUrl) + //val bookChapter = Chapter(bookUrl = book.bookUrl, baseUrl = baseUrl) + val bookChapter = Chapter() analyzeRule.chapter = bookChapter bookChapter.title = analyzeRule.getString(nameRule) bookChapter.url = analyzeRule.getString(urlRule) - bookChapter.tag = analyzeRule.getString(upTimeRule) + bookChapter.updateTime = analyzeRule.getString(upTimeRule) if (bookChapter.url.isEmpty()) { bookChapter.url = baseUrl - Debug.log(bookSource.bookSourceUrl, "目录${index}未获取到url,使用baseUrl替代") + if (log) Log.d(bookSource.sourceUrl, "目录${index}未获取到url,使用baseUrl替代") } if (bookChapter.title.isNotEmpty()) { val isVip = analyzeRule.getString(vipRule) @@ -201,13 +199,13 @@ object BookChapterList { chapterList.add(bookChapter) } } - Debug.log(bookSource.bookSourceUrl, "└目录列表解析完成", log) - Debug.log(bookSource.bookSourceUrl, "┌获取首章名称", log) - Debug.log(bookSource.bookSourceUrl, "└${chapterList[0].title}", log) - Debug.log(bookSource.bookSourceUrl, "┌获取首章链接", log) - Debug.log(bookSource.bookSourceUrl, "└${chapterList[0].url}", log) - Debug.log(bookSource.bookSourceUrl, "┌获取首章信息", log) - Debug.log(bookSource.bookSourceUrl, "└${chapterList[0].tag}", log) + if (log) Log.d(bookSource.sourceUrl, "└目录列表解析完成") + if (log) Log.d(bookSource.sourceUrl, "┌获取首章名称") + if (log) Log.d(bookSource.sourceUrl, "└${chapterList[0].title}") + if (log) Log.d(bookSource.sourceUrl, "┌获取首章链接") + if (log) Log.d(bookSource.sourceUrl, "└${chapterList[0].url}") + if (log) Log.d(bookSource.sourceUrl, "┌获取首章信息") + if (log) Log.d(bookSource.sourceUrl, "└${chapterList[0].updateTime}") } return Pair(chapterList, nextUrlList) } diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookContent.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookContent.kt index dfd7165..5a98cd0 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookContent.kt +++ b/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookContent.kt @@ -1,24 +1,25 @@ package xyz.fycz.myreader.model.third3.webBook -import io.legado.app.R -import io.legado.app.data.appDb -import io.legado.app.data.entities.Book -import io.legado.app.data.entities.BookChapter -import io.legado.app.data.entities.BookSource -import io.legado.app.data.entities.rule.ContentRule -import io.legado.app.help.BookHelp -import io.legado.app.model.ContentEmptyException -import io.legado.app.model.Debug -import io.legado.app.model.NoStackTraceException +import android.util.Log import xyz.fycz.myreader.model.third3.analyzeRule.AnalyzeRule import xyz.fycz.myreader.model.third3.analyzeRule.AnalyzeUrl -import io.legado.app.utils.NetworkUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.async import kotlinx.coroutines.ensureActive import kotlinx.coroutines.withContext -import splitties.init.appCtx +import xyz.fycz.myreader.R +import xyz.fycz.myreader.application.App +import xyz.fycz.myreader.greendao.DbManager +import xyz.fycz.myreader.greendao.entity.Book +import xyz.fycz.myreader.greendao.entity.Chapter +import xyz.fycz.myreader.greendao.entity.rule.BookSource +import xyz.fycz.myreader.greendao.entity.rule.ContentRule +import xyz.fycz.myreader.greendao.service.ChapterService +import xyz.fycz.myreader.model.third3.ContentEmptyException +import xyz.fycz.myreader.model.third3.NoStackTraceException +import xyz.fycz.myreader.util.utils.HtmlFormatter +import xyz.fycz.myreader.util.utils.NetworkUtils /** * 获取正文 @@ -30,25 +31,25 @@ object BookContent { scope: CoroutineScope, bookSource: BookSource, book: Book, - bookChapter: BookChapter, + bookChapter: Chapter, redirectUrl: String, baseUrl: String, body: String?, nextChapterUrl: String? = null ): String { body ?: throw NoStackTraceException( - appCtx.getString(R.string.error_get_web_content, baseUrl) + App.getmContext().getString(R.string.error_get_web_content, baseUrl) ) - Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") - Debug.log(bookSource.bookSourceUrl, body, state = 40) + Log.d(bookSource.sourceUrl, "≡获取成功:${baseUrl}") + Log.d(bookSource.sourceUrl, body) val mNextChapterUrl = if (!nextChapterUrl.isNullOrEmpty()) { nextChapterUrl } else { - appDb.bookChapterDao.getChapter(book.bookUrl, bookChapter.index + 1)?.url + ChapterService.getInstance().findBookAllChapterByBookId(book.id)[bookChapter.number + 1].url } val content = StringBuilder() val nextUrlList = arrayListOf(baseUrl) - val contentRule = bookSource.getContentRule() + val contentRule = bookSource.contentRule val analyzeRule = AnalyzeRule(book, bookSource).setContent(body, baseUrl) analyzeRule.setRedirectUrl(baseUrl) analyzeRule.nextChapterUrl = mNextChapterUrl @@ -70,7 +71,7 @@ object BookContent { mUrl = nextUrl, source = bookSource, ruleData = book, - headerMapF = bookSource.getHeaderMap() + //headerMapF = bookSource.getHeaderMap() ).getStrResponseAwait() res.body?.let { nextBody -> contentData = analyzeContent( @@ -82,9 +83,9 @@ object BookContent { content.append("\n").append(contentData.first) } } - Debug.log(bookSource.bookSourceUrl, "◇本章总页数:${nextUrlList.size}") + Log.d(bookSource.sourceUrl, "◇本章总页数:${nextUrlList.size}") } else if (contentData.second.size > 1) { - Debug.log(bookSource.bookSourceUrl, "◇并发解析目录,总页数:${contentData.second.size}") + Log.d(bookSource.sourceUrl, "◇并发解析目录,总页数:${contentData.second.size}") withContext(IO) { val asyncArray = Array(contentData.second.size) { async(IO) { @@ -93,7 +94,7 @@ object BookContent { mUrl = urlStr, source = bookSource, ruleData = book, - headerMapF = bookSource.getHeaderMap() + //headerMapF = bookSource.getHeaderMap() ) val res = analyzeUrl.getStrResponseAwait() analyzeContent( @@ -113,14 +114,14 @@ object BookContent { if (!replaceRegex.isNullOrEmpty()) { contentStr = analyzeRule.getString(replaceRegex, contentStr) } - Debug.log(bookSource.bookSourceUrl, "┌获取章节名称") - Debug.log(bookSource.bookSourceUrl, "└${bookChapter.title}") - Debug.log(bookSource.bookSourceUrl, "┌获取正文内容") - Debug.log(bookSource.bookSourceUrl, "└\n$contentStr") + Log.d(bookSource.sourceUrl, "┌获取章节名称") + Log.d(bookSource.sourceUrl, "└${bookChapter.title}") + Log.d(bookSource.sourceUrl, "┌获取正文内容") + Log.d(bookSource.sourceUrl, "└\n$contentStr") if (contentStr.isBlank()) { throw ContentEmptyException("内容为空") } - BookHelp.saveContent(bookSource, book, bookChapter, contentStr) + //BookHelp.saveContent(bookSource, book, bookChapter, contentStr) return contentStr } @@ -131,7 +132,7 @@ object BookContent { redirectUrl: String, body: String, contentRule: ContentRule, - chapter: BookChapter, + chapter: Chapter, bookSource: BookSource, nextChapterUrl: String?, printLog: Boolean = true @@ -144,15 +145,16 @@ object BookContent { analyzeRule.chapter = chapter //获取正文 var content = analyzeRule.getString(contentRule.content) - content = HtmlFormatter.formatKeepImg(content, rUrl) + //content = HtmlFormatter.formatKeepImg(content, rUrl) + content = HtmlFormatter.format(content) //获取下一页链接 - val nextUrlRule = contentRule.nextContentUrl + val nextUrlRule = contentRule.contentUrlNext if (!nextUrlRule.isNullOrEmpty()) { - Debug.log(bookSource.bookSourceUrl, "┌获取正文下一页链接", printLog) + if (printLog) Log.d(bookSource.sourceUrl, "┌获取正文下一页链接") analyzeRule.getStringList(nextUrlRule, isUrl = true)?.let { nextUrlList.addAll(it) } - Debug.log(bookSource.bookSourceUrl, "└" + nextUrlList.joinToString(","), printLog) + if (printLog) Log.d(bookSource.sourceUrl, "└" + nextUrlList.joinToString(",")) } return Pair(content, nextUrlList) } diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookInfo.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookInfo.kt index 7de87d1..0df0d36 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookInfo.kt +++ b/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookInfo.kt @@ -1,19 +1,17 @@ package xyz.fycz.myreader.model.third3.webBook -import io.legado.app.R -import io.legado.app.data.entities.Book -import io.legado.app.data.entities.BookSource -import io.legado.app.help.BookHelp -import io.legado.app.model.Debug -import io.legado.app.model.NoStackTraceException + +import android.util.Log import xyz.fycz.myreader.model.third3.analyzeRule.AnalyzeRule -import io.legado.app.utils.NetworkUtils -import io.legado.app.utils.StringUtils.wordCountFormat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ensureActive -import splitties.init.appCtx +import xyz.fycz.myreader.R +import xyz.fycz.myreader.application.App import xyz.fycz.myreader.greendao.entity.Book import xyz.fycz.myreader.greendao.entity.rule.BookSource +import xyz.fycz.myreader.model.third3.NoStackTraceException +import xyz.fycz.myreader.util.utils.HtmlFormatter +import xyz.fycz.myreader.util.utils.NetworkUtils /** * 获取详情 @@ -31,10 +29,10 @@ object BookInfo { canReName: Boolean, ) { body ?: throw NoStackTraceException( - appCtx.getString(R.string.error_get_web_content, baseUrl) + App.getmContext().getString(R.string.error_get_web_content, baseUrl) ) - Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") - Debug.log(bookSource.bookSourceUrl, body, state = 20) + Log.d(bookSource.sourceUrl, "≡获取成功:${baseUrl}") + Log.d(bookSource.sourceUrl, body) val analyzeRule = AnalyzeRule(book, bookSource) analyzeRule.setContent(body).setBaseUrl(baseUrl) analyzeRule.setRedirectUrl(redirectUrl) @@ -55,87 +53,92 @@ object BookInfo { infoRule.init?.let { if (it.isNotBlank()) { scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "≡执行详情页初始化规则") + Log.d(bookSource.sourceUrl, "≡执行详情页初始化规则") analyzeRule.setContent(analyzeRule.getElement(it)) } } - val mCanReName = canReName && !infoRule.canReName.isNullOrBlank() + //val mCanReName = canReName && !infoRule.canReName.isNullOrBlank() + val mCanReName = false scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取书名") - BookHelp.formatBookName(analyzeRule.getString(infoRule.name)).let { + Log.d(bookSource.sourceUrl, "┌获取书名") + BookList.formatBookName(analyzeRule.getString(infoRule.name)).let { if (it.isNotEmpty() && (mCanReName || book.name.isEmpty())) { book.name = it } - Debug.log(bookSource.bookSourceUrl, "└${it}") + Log.d(bookSource.sourceUrl, "└${it}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取作者") - BookHelp.formatBookAuthor(analyzeRule.getString(infoRule.author)).let { + Log.d(bookSource.sourceUrl, "┌获取作者") + BookList.formatBookAuthor(analyzeRule.getString(infoRule.author)).let { if (it.isNotEmpty() && (mCanReName || book.author.isEmpty())) { book.author = it } - Debug.log(bookSource.bookSourceUrl, "└${it}") + Log.d(bookSource.sourceUrl, "└${it}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取分类") + Log.d(bookSource.sourceUrl, "┌获取分类") try { - analyzeRule.getStringList(infoRule.kind) + analyzeRule.getStringList(infoRule.type) ?.joinToString(",") ?.let { - if (it.isNotEmpty()) book.kind = it + if (it.isNotEmpty()) book.type = it } - Debug.log(bookSource.bookSourceUrl, "└${book.kind}") + Log.d(bookSource.sourceUrl, "└${book.type}") } catch (e: Exception) { - Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}") + Log.d(bookSource.sourceUrl, "└${e.localizedMessage}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取字数") + Log.d(bookSource.sourceUrl, "┌获取字数") try { - wordCountFormat(analyzeRule.getString(infoRule.wordCount)).let { + /*wordCountFormat(analyzeRule.getString(infoRule.wordCount)).let { + if (it.isNotEmpty()) book.wordCount = it + }*/ + analyzeRule.getString(infoRule.wordCount).let { if (it.isNotEmpty()) book.wordCount = it } - Debug.log(bookSource.bookSourceUrl, "└${book.wordCount}") + Log.d(bookSource.sourceUrl, "└${book.wordCount}") } catch (e: Exception) { - Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}") + Log.d(bookSource.sourceUrl, "└${e.localizedMessage}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取最新章节") + Log.d(bookSource.sourceUrl, "┌获取最新章节") try { analyzeRule.getString(infoRule.lastChapter).let { - if (it.isNotEmpty()) book.latestChapterTitle = it + if (it.isNotEmpty()) book.newestChapterTitle = it } - Debug.log(bookSource.bookSourceUrl, "└${book.latestChapterTitle}") + Log.d(bookSource.sourceUrl, "└${book.newestChapterTitle}") } catch (e: Exception) { - Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}") + Log.d(bookSource.sourceUrl, "└${e.localizedMessage}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取简介") + Log.d(bookSource.sourceUrl, "┌获取简介") try { - analyzeRule.getString(infoRule.intro).let { - if (it.isNotEmpty()) book.intro = HtmlFormatter.format(it) + analyzeRule.getString(infoRule.desc).let { + if (it.isNotEmpty()) book.desc = HtmlFormatter.format(it) } - Debug.log(bookSource.bookSourceUrl, "└${book.intro}") + Log.d(bookSource.sourceUrl, "└${book.desc}") } catch (e: Exception) { - Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}") + Log.d(bookSource.sourceUrl, "└${e.localizedMessage}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取封面链接") + Log.d(bookSource.sourceUrl, "┌获取封面链接") try { - analyzeRule.getString(infoRule.coverUrl).let { - if (it.isNotEmpty()) book.coverUrl = NetworkUtils.getAbsoluteURL(baseUrl, it) + analyzeRule.getString(infoRule.imgUrl).let { + if (it.isNotEmpty()) book.imgUrl = NetworkUtils.getAbsoluteURL(baseUrl, it) } - Debug.log(bookSource.bookSourceUrl, "└${book.coverUrl}") + Log.d(bookSource.sourceUrl, "└${book.imgUrl}") } catch (e: Exception) { - Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}") + Log.d(bookSource.sourceUrl, "└${e.localizedMessage}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取目录链接") - book.tocUrl = analyzeRule.getString(infoRule.tocUrl, isUrl = true) - if (book.tocUrl.isEmpty()) book.tocUrl = redirectUrl - if (book.tocUrl == redirectUrl) { - book.tocHtml = body + Log.d(bookSource.sourceUrl, "┌获取目录链接") + book.chapterUrl = analyzeRule.getString(infoRule.tocUrl, isUrl = true) + if (book.chapterUrl.isEmpty()) book.chapterUrl = redirectUrl + if (book.chapterUrl == redirectUrl) { + book.putCathe("tocHtml", body) + //book.tocHtml = body } - Debug.log(bookSource.bookSourceUrl, "└${book.tocUrl}") + Log.d(bookSource.sourceUrl, "└${book.chapterUrl}") } } \ No newline at end of file diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookList.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookList.kt index 82949ea..5affcd9 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookList.kt +++ b/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/BookList.kt @@ -1,20 +1,18 @@ package xyz.fycz.myreader.model.third3.webBook -import io.legado.app.R -import io.legado.app.data.entities.Book -import io.legado.app.data.entities.BookSource -import io.legado.app.data.entities.SearchBook -import io.legado.app.data.entities.rule.BookListRule -import io.legado.app.help.BookHelp -import io.legado.app.model.Debug -import io.legado.app.model.NoStackTraceException +import android.util.Log import xyz.fycz.myreader.model.third3.analyzeRule.AnalyzeRule import xyz.fycz.myreader.model.third3.analyzeRule.AnalyzeUrl -import io.legado.app.utils.NetworkUtils -import io.legado.app.utils.StringUtils.wordCountFormat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ensureActive -import splitties.init.appCtx +import xyz.fycz.myreader.R +import xyz.fycz.myreader.application.App +import xyz.fycz.myreader.greendao.entity.Book +import xyz.fycz.myreader.greendao.entity.rule.BookListRule +import xyz.fycz.myreader.greendao.entity.rule.BookSource +import xyz.fycz.myreader.model.third3.NoStackTraceException +import xyz.fycz.myreader.util.utils.HtmlFormatter +import xyz.fycz.myreader.util.utils.NetworkUtils /** * 获取书籍列表 @@ -25,32 +23,32 @@ object BookList { fun analyzeBookList( scope: CoroutineScope, bookSource: BookSource, - variableBook: SearchBook, + variableBook: Book, analyzeUrl: AnalyzeUrl, baseUrl: String, body: String?, isSearch: Boolean = true, - ): ArrayList { + ): ArrayList { body ?: throw NoStackTraceException( - appCtx.getString( + App.getmContext().getString( R.string.error_get_web_content, analyzeUrl.ruleUrl ) ) - val bookList = ArrayList() - Debug.log(bookSource.bookSourceUrl, "≡获取成功:${analyzeUrl.ruleUrl}") - Debug.log(bookSource.bookSourceUrl, body, state = 10) + val bookList = ArrayList() + Log.d(bookSource.sourceUrl, "≡获取成功:${analyzeUrl.ruleUrl}") + Log.d(bookSource.sourceUrl, body) val analyzeRule = AnalyzeRule(variableBook, bookSource) analyzeRule.setContent(body).setBaseUrl(baseUrl) analyzeRule.setRedirectUrl(baseUrl) - bookSource.bookUrlPattern?.let { + bookSource.infoRule.urlPattern?.let { scope.ensureActive() if (baseUrl.matches(it.toRegex())) { - Debug.log(bookSource.bookSourceUrl, "≡链接为详情页") + Log.d(bookSource.sourceUrl, "≡链接为详情页") getInfoItem( scope, bookSource, analyzeRule, analyzeUrl, body, baseUrl, variableBook.variable )?.let { searchBook -> - searchBook.infoHtml = body + searchBook.putCathe("infoHtml", body) bookList.add(searchBook) } return bookList @@ -59,11 +57,11 @@ object BookList { val collections: List var reverse = false val bookListRule: BookListRule = when { - isSearch -> bookSource.getSearchRule() - bookSource.getExploreRule().bookList.isNullOrBlank() -> bookSource.getSearchRule() - else -> bookSource.getExploreRule() + isSearch -> bookSource.searchRule + bookSource.findRule.url.isNullOrBlank() -> bookSource.searchRule + else -> bookSource.findRule } - var ruleList: String = bookListRule.bookList ?: "" + var ruleList: String = bookListRule.list ?: "" if (ruleList.startsWith("-")) { reverse = true ruleList = ruleList.substring(1) @@ -71,27 +69,27 @@ object BookList { if (ruleList.startsWith("+")) { ruleList = ruleList.substring(1) } - Debug.log(bookSource.bookSourceUrl, "┌获取书籍列表") + Log.d(bookSource.sourceUrl, "┌获取书籍列表") collections = analyzeRule.getElements(ruleList) scope.ensureActive() - if (collections.isEmpty() && bookSource.bookUrlPattern.isNullOrEmpty()) { - Debug.log(bookSource.bookSourceUrl, "└列表为空,按详情页解析") + if (collections.isEmpty() && bookSource.infoRule.urlPattern.isNullOrEmpty()) { + Log.d(bookSource.sourceUrl, "└列表为空,按详情页解析") getInfoItem( scope, bookSource, analyzeRule, analyzeUrl, body, baseUrl, variableBook.variable )?.let { searchBook -> - searchBook.infoHtml = body + searchBook.putCathe("infoHtml", body) bookList.add(searchBook) } } else { val ruleName = analyzeRule.splitSourceRule(bookListRule.name) - val ruleBookUrl = analyzeRule.splitSourceRule(bookListRule.bookUrl) + val ruleBookUrl = analyzeRule.splitSourceRule(bookListRule.infoUrl) val ruleAuthor = analyzeRule.splitSourceRule(bookListRule.author) - val ruleCoverUrl = analyzeRule.splitSourceRule(bookListRule.coverUrl) - val ruleIntro = analyzeRule.splitSourceRule(bookListRule.intro) - val ruleKind = analyzeRule.splitSourceRule(bookListRule.kind) + val ruleCoverUrl = analyzeRule.splitSourceRule(bookListRule.imgUrl) + val ruleIntro = analyzeRule.splitSourceRule(bookListRule.desc) + val ruleKind = analyzeRule.splitSourceRule(bookListRule.type) val ruleLastChapter = analyzeRule.splitSourceRule(bookListRule.lastChapter) val ruleWordCount = analyzeRule.splitSourceRule(bookListRule.wordCount) - Debug.log(bookSource.bookSourceUrl, "└列表大小:${collections.size}") + Log.d(bookSource.sourceUrl, "└列表大小:${collections.size}") for ((index, item) in collections.withIndex()) { getSearchItem( scope, bookSource, analyzeRule, item, baseUrl, variableBook.variable, @@ -105,8 +103,8 @@ object BookList { ruleLastChapter = ruleLastChapter, ruleWordCount = ruleWordCount )?.let { searchBook -> - if (baseUrl == searchBook.bookUrl) { - searchBook.infoHtml = body + if (baseUrl == searchBook.infoUrl) { + searchBook.putCathe("infoHtml", body) } bookList.add(searchBook) } @@ -127,13 +125,14 @@ object BookList { body: String, baseUrl: String, variable: String? - ): SearchBook? { - val book = Book(variable = variable) - book.bookUrl = analyzeUrl.ruleUrl - book.origin = bookSource.bookSourceUrl - book.originName = bookSource.bookSourceName - book.originOrder = bookSource.customOrder - book.type = bookSource.bookSourceType + ): Book? { + val book = Book() + book.variable = variable + book.infoUrl = analyzeUrl.ruleUrl + book.source = bookSource.sourceUrl + //book.originName = bookSource.bookSourceName + //book.originOrder = bookSource.customOrder + //book.type = bookSource.bookSourceType analyzeRule.book = book BookInfo.analyzeBookInfo( scope, @@ -146,7 +145,8 @@ object BookList { false ) if (book.name.isNotBlank()) { - return book.toSearchBook() + //return book.toSearchBook() + return book } return null } @@ -168,76 +168,98 @@ object BookList { ruleWordCount: List, ruleIntro: List, ruleLastChapter: List - ): SearchBook? { - val searchBook = SearchBook(variable = variable) - searchBook.origin = bookSource.bookSourceUrl - searchBook.originName = bookSource.bookSourceName + ): Book? { + val searchBook = Book() + searchBook.variable = variable + searchBook.source = bookSource.sourceUrl + /* searchBook.originName = bookSource.bookSourceName searchBook.type = bookSource.bookSourceType - searchBook.originOrder = bookSource.customOrder + searchBook.originOrder = bookSource.customOrder*/ analyzeRule.book = searchBook analyzeRule.setContent(item) scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取书名", log) - searchBook.name = BookHelp.formatBookName(analyzeRule.getString(ruleName)) - Debug.log(bookSource.bookSourceUrl, "└${searchBook.name}", log) + if (log) if (log) Log.d(bookSource.sourceUrl, "┌获取书名") + searchBook.name = formatBookName(analyzeRule.getString(ruleName)) + if (log) Log.d(bookSource.sourceUrl, "└${searchBook.name}") if (searchBook.name.isNotEmpty()) { scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取作者", log) - searchBook.author = BookHelp.formatBookAuthor(analyzeRule.getString(ruleAuthor)) - Debug.log(bookSource.bookSourceUrl, "└${searchBook.author}", log) + if (log) Log.d(bookSource.sourceUrl, "┌获取作者") + searchBook.author = formatBookAuthor(analyzeRule.getString(ruleAuthor)) + if (log) Log.d(bookSource.sourceUrl, "└${searchBook.author}") scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取分类", log) + if (log) Log.d(bookSource.sourceUrl, "┌获取分类") try { - searchBook.kind = analyzeRule.getStringList(ruleKind)?.joinToString(",") - Debug.log(bookSource.bookSourceUrl, "└${searchBook.kind}", log) + searchBook.type = analyzeRule.getStringList(ruleKind)?.joinToString(",") + if (log) Log.d(bookSource.sourceUrl, "└${searchBook.type}") } catch (e: Exception) { - Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}", log) + if (log) Log.d(bookSource.sourceUrl, "└${e.localizedMessage}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取字数", log) + if (log) Log.d(bookSource.sourceUrl, "┌获取字数") try { - searchBook.wordCount = wordCountFormat(analyzeRule.getString(ruleWordCount)) - Debug.log(bookSource.bookSourceUrl, "└${searchBook.wordCount}", log) + //searchBook.wordCount = wordCountFormat(analyzeRule.getString(ruleWordCount)) + searchBook.wordCount = analyzeRule.getString(ruleWordCount) + if (log) Log.d(bookSource.sourceUrl, "└${searchBook.wordCount}") } catch (e: java.lang.Exception) { - Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}", log) + if (log) Log.d(bookSource.sourceUrl, "└${e.localizedMessage}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取最新章节", log) + if (log) Log.d(bookSource.sourceUrl, "┌获取最新章节") try { - searchBook.latestChapterTitle = analyzeRule.getString(ruleLastChapter) - Debug.log(bookSource.bookSourceUrl, "└${searchBook.latestChapterTitle}", log) + searchBook.newestChapterTitle = analyzeRule.getString(ruleLastChapter) + if (log) Log.d(bookSource.sourceUrl, "└${searchBook.newestChapterTitle}") } catch (e: java.lang.Exception) { - Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}", log) + if (log) Log.d(bookSource.sourceUrl, "└${e.localizedMessage}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取简介", log) + if (log) Log.d(bookSource.sourceUrl, "┌获取简介") try { - searchBook.intro = HtmlFormatter.format(analyzeRule.getString(ruleIntro)) - Debug.log(bookSource.bookSourceUrl, "└${searchBook.intro}", log) + searchBook.desc = HtmlFormatter.format(analyzeRule.getString(ruleIntro)) + if (log) Log.d(bookSource.sourceUrl, "└${searchBook.desc}") } catch (e: java.lang.Exception) { - Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}", log) + if (log) Log.d(bookSource.sourceUrl, "└${e.localizedMessage}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取封面链接", log) + if (log) Log.d(bookSource.sourceUrl, "┌获取封面链接") try { analyzeRule.getString(ruleCoverUrl).let { - if (it.isNotEmpty()) searchBook.coverUrl = + if (it.isNotEmpty()) searchBook.imgUrl = NetworkUtils.getAbsoluteURL(baseUrl, it) } - Debug.log(bookSource.bookSourceUrl, "└${searchBook.coverUrl}", log) + if (log) Log.d(bookSource.sourceUrl, "└${searchBook.imgUrl}") } catch (e: java.lang.Exception) { - Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}", log) + if (log) Log.d(bookSource.sourceUrl, "└${e.localizedMessage}") } scope.ensureActive() - Debug.log(bookSource.bookSourceUrl, "┌获取详情页链接", log) - searchBook.bookUrl = analyzeRule.getString(ruleBookUrl, isUrl = true) - if (searchBook.bookUrl.isEmpty()) { - searchBook.bookUrl = baseUrl + if (log) Log.d(bookSource.sourceUrl, "┌获取详情页链接") + searchBook.infoUrl = analyzeRule.getString(ruleBookUrl, isUrl = true) + if (searchBook.infoUrl.isEmpty()) { + searchBook.infoUrl = baseUrl } - Debug.log(bookSource.bookSourceUrl, "└${searchBook.bookUrl}", log) + if (log) Log.d(bookSource.sourceUrl, "└${searchBook.infoUrl}") return searchBook } return null } + val nameRegex = Regex("\\s+作\\s*者.*|\\s+\\S+\\s+著") + val authorRegex = Regex("^\\s*作\\s*者[::\\s]+|\\s+著") + /** + * 格式化书名 + */ + fun formatBookName(name: String): String { + return name + .replace(nameRegex, "") + .trim { it <= ' ' } + } + + /** + * 格式化作者 + */ + fun formatBookAuthor(author: String): String { + return author + .replace(authorRegex, "") + .trim { it <= ' ' } + } + } \ No newline at end of file diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/SearchModel.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/SearchModel.kt deleted file mode 100644 index 961a383..0000000 --- a/app/src/main/java/xyz/fycz/myreader/model/third3/webBook/SearchModel.kt +++ /dev/null @@ -1,209 +0,0 @@ -package xyz.fycz.myreader.model.third3.webBook - -import io.legado.app.constant.AppConst -import io.legado.app.constant.PreferKey -import io.legado.app.data.appDb -import io.legado.app.data.entities.BookSource -import io.legado.app.data.entities.SearchBook -import io.legado.app.help.AppConfig -import io.legado.app.help.coroutine.CompositeCoroutine -import io.legado.app.utils.getPrefBoolean -import io.legado.app.utils.getPrefString -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExecutorCoroutineDispatcher -import kotlinx.coroutines.asCoroutineDispatcher -import kotlinx.coroutines.isActive -import splitties.init.appCtx -import java.util.concurrent.Executors -import kotlin.math.min - -class SearchModel(private val scope: CoroutineScope, private val callBack: CallBack) { - val threadCount = AppConfig.threadCount - private var searchPool: ExecutorCoroutineDispatcher? = null - private var mSearchId = 0L - private var searchPage = 1 - private var searchKey: String = "" - private var tasks = CompositeCoroutine() - private var bookSourceList = arrayListOf() - private var searchBooks = arrayListOf() - - @Volatile - private var searchIndex = -1 - - private fun initSearchPool() { - searchPool?.close() - searchPool = Executors - .newFixedThreadPool(min(threadCount, AppConst.MAX_THREAD)).asCoroutineDispatcher() - } - - fun search(searchId: Long, key: String) { - callBack.onSearchStart() - if (searchId != mSearchId) { - if (key.isEmpty()) { - callBack.onSearchCancel() - return - } else { - this.searchKey = key - } - if (mSearchId != 0L) { - close() - } - initSearchPool() - mSearchId = searchId - searchPage = 1 - val searchGroup = appCtx.getPrefString("searchGroup") ?: "" - bookSourceList.clear() - if (searchGroup.isBlank()) { - bookSourceList.addAll(appDb.bookSourceDao.allEnabled) - } else { - val sources = appDb.bookSourceDao.getEnabledByGroup(searchGroup) - if (sources.isEmpty()) { - bookSourceList.addAll(appDb.bookSourceDao.allEnabled) - } else { - bookSourceList.addAll(sources) - } - } - } else { - searchPage++ - } - searchIndex = -1 - for (i in 0 until threadCount) { - search(searchId) - } - } - - @Synchronized - private fun search(searchId: Long) { - if (searchIndex >= bookSourceList.lastIndex) { - return - } - searchIndex++ - val source = bookSourceList[searchIndex] - searchPool?.let { searchPool -> - val task = WebBook.searchBook( - scope, - source, - searchKey, - searchPage, - context = searchPool - ).timeout(30000L) - .onSuccess(searchPool) { - onSuccess(searchId, it) - } - .onFinally(searchPool) { - onFinally(searchId) - } - tasks.add(task) - } - } - - @Synchronized - private fun onSuccess(searchId: Long, items: ArrayList) { - if (searchId == mSearchId) { - appDb.searchBookDao.insert(*items.toTypedArray()) - val precision = appCtx.getPrefBoolean(PreferKey.precisionSearch) - mergeItems(scope, items, precision) - callBack.onSearchSuccess(searchBooks) - } - } - - @Synchronized - private fun onFinally(searchId: Long) { - if (searchIndex < bookSourceList.lastIndex) { - search(searchId) - } else { - searchIndex++ - } - if (searchIndex >= bookSourceList.lastIndex - + min(bookSourceList.size, threadCount) - ) { - callBack.onSearchFinish() - } - } - - private fun mergeItems(scope: CoroutineScope, newDataS: List, precision: Boolean) { - if (newDataS.isNotEmpty()) { - val copyData = ArrayList(searchBooks) - val equalData = arrayListOf() - val containsData = arrayListOf() - val otherData = arrayListOf() - copyData.forEach { - if (!scope.isActive) return - if (it.name == searchKey || it.author == searchKey) { - equalData.add(it) - } else if (it.name.contains(searchKey) || it.author.contains(searchKey)) { - containsData.add(it) - } else { - otherData.add(it) - } - } - newDataS.forEach { nBook -> - if (!scope.isActive) return - if (nBook.name == searchKey || nBook.author == searchKey) { - var hasSame = false - equalData.forEach { pBook -> - if (!scope.isActive) return - if (pBook.name == nBook.name && pBook.author == nBook.author) { - pBook.addOrigin(nBook.origin) - hasSame = true - } - } - if (!hasSame) { - equalData.add(nBook) - } - } else if (nBook.name.contains(searchKey) || nBook.author.contains(searchKey)) { - var hasSame = false - containsData.forEach { pBook -> - if (!scope.isActive) return - if (pBook.name == nBook.name && pBook.author == nBook.author) { - pBook.addOrigin(nBook.origin) - hasSame = true - } - } - if (!hasSame) { - containsData.add(nBook) - } - } else if (!precision) { - var hasSame = false - otherData.forEach { pBook -> - if (!scope.isActive) return - if (pBook.name == nBook.name && pBook.author == nBook.author) { - pBook.addOrigin(nBook.origin) - hasSame = true - } - } - if (!hasSame) { - otherData.add(nBook) - } - } - } - if (!scope.isActive) return - equalData.sortByDescending { it.origins.size } - equalData.addAll(containsData.sortedByDescending { it.origins.size }) - if (!precision) { - equalData.addAll(otherData) - } - searchBooks = equalData - } - } - - fun cancelSearch() { - close() - callBack.onSearchCancel() - } - - fun close() { - tasks.clear() - searchPool?.close() - searchPool = null - mSearchId = 0L - } - - interface CallBack { - fun onSearchStart() - fun onSearchSuccess(searchBooks: ArrayList) - fun onSearchFinish() - fun onSearchCancel() - } - -} \ No newline at end of file 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 ee46354..f864c81 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 @@ -1,12 +1,17 @@ package xyz.fycz.myreader.model.third3.webBook +import android.util.Log import xyz.fycz.myreader.model.third3.http.StrResponse import xyz.fycz.myreader.model.third3.analyzeRule.AnalyzeUrl import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.isActive import xyz.fycz.myreader.greendao.entity.Book +import xyz.fycz.myreader.greendao.entity.Chapter import xyz.fycz.myreader.greendao.entity.rule.BookSource +import xyz.fycz.myreader.model.third3.Coroutine +import xyz.fycz.myreader.model.third3.NoStackTraceException +import xyz.fycz.myreader.util.utils.NetworkUtils import kotlin.coroutines.CoroutineContext @Suppress("MemberVisibilityCanBePrivate") @@ -21,7 +26,7 @@ object WebBook { key: String, page: Int? = 1, context: CoroutineContext = Dispatchers.IO, - ): Coroutine> { + ): Coroutine> { return Coroutine.async(scope, context) { searchBookAwait(scope, bookSource, key, page) } @@ -32,25 +37,25 @@ object WebBook { bookSource: BookSource, key: String, page: Int? = 1, - ): ArrayList { - val variableBook = SearchBook() - bookSource.searchUrl?.let { searchUrl -> + ): ArrayList { + val variableBook = Book() + bookSource.searchRule.searchUrl?.let { searchUrl -> val analyzeUrl = AnalyzeUrl( mUrl = searchUrl, key = key, page = page, - baseUrl = bookSource.bookSourceUrl, - headerMapF = bookSource.getHeaderMap(true), + baseUrl = bookSource.sourceUrl, + //headerMapF = bookSource.getHeaderMap(true), source = bookSource, ruleData = variableBook, ) 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, @@ -73,7 +78,7 @@ object WebBook { url: String, page: Int? = 1, context: CoroutineContext = Dispatchers.IO, - ): Coroutine> { + ): Coroutine> { return Coroutine.async(scope, context) { exploreBookAwait(scope, bookSource, url, page) } @@ -84,23 +89,23 @@ object WebBook { bookSource: BookSource, url: String, page: Int? = 1, - ): ArrayList { - val variableBook = SearchBook() + ): ArrayList { + val variableBook = Book() val analyzeUrl = AnalyzeUrl( mUrl = url, page = page, - baseUrl = bookSource.bookSourceUrl, + baseUrl = bookSource.sourceUrl, source = bookSource, ruleData = variableBook, - headerMapF = bookSource.getHeaderMap(true) + //headerMapF = bookSource.getHeaderMap(true) ) 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, @@ -133,37 +138,37 @@ object WebBook { book: Book, canReName: Boolean = true, ): Book { - book.type = bookSource.bookSourceType - if (!book.infoHtml.isNullOrEmpty()) { + //book.type = bookSource.bookSourceType + if (!book.getCathe("infoHtml").isNullOrEmpty()) { BookInfo.analyzeBookInfo( scope, bookSource, book, - book.bookUrl, - book.bookUrl, - book.infoHtml, + book.infoUrl, + book.infoUrl, + book.getCathe("infoHtml"), canReName ) } else { val analyzeUrl = AnalyzeUrl( - mUrl = book.bookUrl, - baseUrl = bookSource.bookSourceUrl, + mUrl = book.infoUrl, + baseUrl = bookSource.sourceUrl, source = bookSource, ruleData = book, - headerMapF = bookSource.getHeaderMap(true) + //headerMapF = bookSource.getHeaderMap(true) ) 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, book, - book.bookUrl, + book.infoUrl, res.url, res.body, canReName @@ -180,7 +185,7 @@ object WebBook { bookSource: BookSource, book: Book, context: CoroutineContext = Dispatchers.IO - ): Coroutine> { + ): Coroutine> { return Coroutine.async(scope, context) { getChapterListAwait(scope, bookSource, book) } @@ -190,39 +195,38 @@ object WebBook { scope: CoroutineScope, bookSource: BookSource, book: Book, - ): List { - book.type = bookSource.bookSourceType - return if (book.bookUrl == book.tocUrl && !book.tocHtml.isNullOrEmpty()) { + ): List { + //book.type = bookSource.bookSourceType + return if (book.infoUrl == book.chapterUrl && !book.getCathe("tocHtml").isNullOrEmpty()) { BookChapterList.analyzeChapterList( scope, bookSource, book, - book.tocUrl, - book.tocUrl, - book.tocHtml + book.chapterUrl, + book.chapterUrl, + book.getCathe("tocHtml"), ) } else { val analyzeUrl = AnalyzeUrl( - mUrl = book.tocUrl, - baseUrl = book.bookUrl, + mUrl = book.chapterUrl, + baseUrl = book.infoUrl, source = bookSource, ruleData = book, - headerMapF = bookSource.getHeaderMap(true) ) 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, book, - book.tocUrl, + book.chapterUrl, res.url, - res.body + res.body, ) } } @@ -234,7 +238,7 @@ object WebBook { scope: CoroutineScope, bookSource: BookSource, book: Book, - bookChapter: BookChapter, + bookChapter: Chapter, nextChapterUrl: String? = null, context: CoroutineContext = Dispatchers.IO ): Coroutine { @@ -247,49 +251,50 @@ object WebBook { scope: CoroutineScope, bookSource: BookSource, book: Book, - bookChapter: BookChapter, - nextChapterUrl: String? = null + bookChapter: Chapter, + nextChapterUrl: String? = null, ): String { - if (bookSource.getContentRule().content.isNullOrEmpty()) { - Debug.log(bookSource.bookSourceUrl, "⇒正文规则为空,使用章节链接:${bookChapter.url}") + if (bookSource.contentRule.content.isNullOrEmpty()) { + Log.d(bookSource.sourceUrl, "⇒正文规则为空,使用章节链接:${bookChapter.url}") return bookChapter.url } - return if (bookChapter.url == book.bookUrl && !book.tocHtml.isNullOrEmpty()) { + val absoluteUrl = NetworkUtils.getAbsoluteURL(book.chapterUrl, bookChapter.url) + return if (bookChapter.url == book.infoUrl && !book.getCathe("tocHtml").isNullOrEmpty()) { BookContent.analyzeContent( scope, bookSource, book, bookChapter, - bookChapter.getAbsoluteURL(), - bookChapter.getAbsoluteURL(), - book.tocHtml, + absoluteUrl, + absoluteUrl, + book.getCathe("tocHtml"), nextChapterUrl ) } else { val analyzeUrl = AnalyzeUrl( - mUrl = bookChapter.getAbsoluteURL(), - baseUrl = book.tocUrl, + mUrl = absoluteUrl, + baseUrl = book.chapterUrl, source = bookSource, ruleData = book, chapter = bookChapter, - headerMapF = bookSource.getHeaderMap(true) - ) - var res = analyzeUrl.getStrResponseAwait( - jsStr = bookSource.getContentRule().webJs, - sourceRegex = bookSource.getContentRule().sourceRegex ) + /*var res = analyzeUrl.getStrResponseAwait( + jsStr = bookSource.contentRule.webJs, + sourceRegex = bookSource.contentRule.sourceRegex + )*/ + 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, book, bookChapter, - bookChapter.getAbsoluteURL(), + absoluteUrl, res.url, res.body, nextChapterUrl @@ -317,7 +322,7 @@ object WebBook { scope: CoroutineScope, bookSources: List, name: String, - author: String + author: String, ): Pair? { bookSources.forEach { source -> kotlin.runCatching { @@ -326,8 +331,8 @@ object WebBook { it.name == name && it.author == author }?.let { searchBook -> if (!scope.isActive) return null - var book = searchBook.toBook() - if (book.tocUrl.isBlank()) { + var book = searchBook + if (book.chapterUrl.isBlank()) { book = getBookInfoAwait(scope, source, book) } return Pair(source, book) diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java index 7dcd369..b8681ef 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java @@ -120,6 +120,9 @@ public class SourceEditActivity extends BaseActivity { case APPCONST.THIRD_SOURCE: sourceType = 3; break; + case APPCONST.THIRD_3_SOURCE: + sourceType = 4; + break; } binding.sSourceType.setSelection(sourceType); } @@ -286,6 +289,9 @@ public class SourceEditActivity extends BaseActivity { case 3: sourceType = APPCONST.THIRD_SOURCE; break; + case 4: + sourceType = APPCONST.THIRD_3_SOURCE; + break; } source.setSourceType(sourceType); source.setSearchRule(entityUtil.getSearchRule(searchEntities)); diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/Third3SourceApi.kt b/app/src/main/java/xyz/fycz/myreader/webapi/Third3SourceApi.kt new file mode 100644 index 0000000..0b33348 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/webapi/Third3SourceApi.kt @@ -0,0 +1,90 @@ +package xyz.fycz.myreader.webapi + +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope +import io.reactivex.Observable +import xyz.fycz.myreader.application.App +import xyz.fycz.myreader.entity.SearchBookBean +import xyz.fycz.myreader.greendao.entity.Book +import xyz.fycz.myreader.greendao.entity.Chapter +import xyz.fycz.myreader.model.mulvalmap.ConMVMap +import xyz.fycz.myreader.model.third3.webBook.WebBook +import xyz.fycz.myreader.webapi.crawler.source.Third3Crawler + +/** + * @author fengyue + * @date 2022/1/20 11:46 + */ +object Third3SourceApi : AndroidViewModel(App.getApplication()) { + val scope = viewModelScope + + fun searchByT3C( + key: String, + rc: Third3Crawler + ): Observable> { + return Observable.create { emitter -> + WebBook.searchBook( + scope, + rc.source, + key, + 1, + ).timeout(30000L) + .onSuccess { + emitter.onNext(rc.getBooks(it)) + }.onError { + emitter.onError(it) + }.onFinally { + emitter.onComplete() + } + } + } + + fun getBookInfoByT3C(book: Book, rc: Third3Crawler): Observable { + return Observable.create { emitter -> + WebBook.getBookInfo( + scope, + rc.source, + book, + ).onSuccess { + emitter.onNext(it) + }.onError { + emitter.onError(it) + }.onFinally { + emitter.onComplete() + } + } + } + + fun getBookChaptersByT3C(book: Book, rc: Third3Crawler): Observable>{ + return Observable.create { emitter -> + WebBook.getChapterList( + scope, + rc.source, + book, + ).onSuccess { + emitter.onNext(it) + }.onError { + emitter.onError(it) + }.onFinally { + emitter.onComplete() + } + } + } + + fun getChapterContentByT3C(chapter: Chapter, book: Book, rc: Third3Crawler): Observable{ + return Observable.create { emitter -> + WebBook.getContent( + scope, + rc.source, + book, + chapter + ).onSuccess { + emitter.onNext(it) + }.onError { + emitter.onError(it) + }.onFinally { + emitter.onComplete() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/ThirdSourceApi.java b/app/src/main/java/xyz/fycz/myreader/webapi/ThirdSourceApi.java index c47d671..c240157 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/ThirdSourceApi.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/ThirdSourceApi.java @@ -23,6 +23,7 @@ import xyz.fycz.myreader.model.third2.content.BookInfo; import xyz.fycz.myreader.model.third2.content.BookList; import xyz.fycz.myreader.util.utils.OkHttpUtils; import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; +import xyz.fycz.myreader.webapi.crawler.source.Third3Crawler; import xyz.fycz.myreader.webapi.crawler.source.ThirdCrawler; import xyz.fycz.myreader.webapi.crawler.source.find.ThirdFindCrawler; @@ -43,6 +44,9 @@ public class ThirdSourceApi { * @return */ protected static Observable> searchByTC(String key, final ThirdCrawler rc) { + if (rc instanceof Third3Crawler) { + return Third3SourceApi.INSTANCE.searchByT3C(key, (Third3Crawler) rc); + } try { Map headers = rc.getHeaders(); headers.putAll(getCookies(rc.getNameSpace())); @@ -61,6 +65,9 @@ public class ThirdSourceApi { } protected static Observable getBookInfoByTC(Book book, ThirdCrawler rc) { + if (rc instanceof Third3Crawler) { + return Third3SourceApi.INSTANCE.getBookInfoByT3C(book, (Third3Crawler) rc); + } BookSource source = rc.getSource(); BookInfo bookInfo = new BookInfo(source.getSourceUrl(), source.getSourceName(), source); if (!TextUtils.isEmpty(book.getCathe("BookInfoHtml"))) { @@ -79,6 +86,9 @@ public class ThirdSourceApi { } protected static Observable> getBookChaptersByTC(Book book, ThirdCrawler rc) { + if (rc instanceof Third3Crawler) { + return Third3SourceApi.INSTANCE.getBookChaptersByT3C(book, (Third3Crawler) rc); + } BookSource source = rc.getSource(); Map headers = rc.getHeaders(); headers.putAll(getCookies(rc.getNameSpace())); @@ -113,6 +123,9 @@ public class ThirdSourceApi { } protected static Observable getChapterContentByTC(Chapter chapter, Book book, ThirdCrawler rc) { + if (rc instanceof Third3Crawler) { + return Third3SourceApi.INSTANCE.getChapterContentByT3C(chapter, book, (Third3Crawler) rc); + } BookSource source = rc.getSource(); Map headers = rc.getHeaders(); headers.putAll(getCookies(rc.getNameSpace())); diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/ReadCrawlerUtil.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/ReadCrawlerUtil.java index 1a3b94a..0a8a988 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/ReadCrawlerUtil.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/ReadCrawlerUtil.java @@ -16,6 +16,7 @@ import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; import xyz.fycz.myreader.webapi.crawler.read.FYReadCrawler; import xyz.fycz.myreader.webapi.crawler.source.JsonPathCrawler; import xyz.fycz.myreader.webapi.crawler.source.MatcherCrawler; +import xyz.fycz.myreader.webapi.crawler.source.Third3Crawler; import xyz.fycz.myreader.webapi.crawler.source.ThirdCrawler; import xyz.fycz.myreader.webapi.crawler.source.XpathCrawler; @@ -27,6 +28,7 @@ import java.util.ResourceBundle; import static xyz.fycz.myreader.common.APPCONST.JSON_PATH; import static xyz.fycz.myreader.common.APPCONST.MATCHER; +import static xyz.fycz.myreader.common.APPCONST.THIRD_3_SOURCE; import static xyz.fycz.myreader.common.APPCONST.THIRD_SOURCE; import static xyz.fycz.myreader.common.APPCONST.XPATH; @@ -178,6 +180,7 @@ public class ReadCrawlerUtil { public static ReadCrawler getReadCrawler(BookSource source) { return getReadCrawler(source, false); } + public static ReadCrawler getReadCrawler(BookSource source, boolean isInfo) { try { if (StringHelper.isEmpty(source.getSourceEName())) { @@ -196,6 +199,8 @@ public class ReadCrawlerUtil { break; case THIRD_SOURCE: return new ThirdCrawler(source); + case THIRD_3_SOURCE: + return new Third3Crawler(source); } if (source.getSearchRule().isRelatedWithInfo() || isInfo) { return crawler; diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/Third3Crawler.kt b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/Third3Crawler.kt new file mode 100644 index 0000000..5229d00 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/Third3Crawler.kt @@ -0,0 +1,10 @@ +package xyz.fycz.myreader.webapi.crawler.source + +import xyz.fycz.myreader.greendao.entity.rule.BookSource + +/** + * @author fengyue + * @date 2022/1/20 12:23 + */ +class Third3Crawler(source: BookSource) : ThirdCrawler(source) { +} \ No newline at end of file diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/ThirdCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/ThirdCrawler.java index dcd752d..e76d994 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/ThirdCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/ThirdCrawler.java @@ -15,7 +15,7 @@ import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler; * @date 2021/5/14 10:55 */ public class ThirdCrawler extends BaseReadCrawler implements BookInfoCrawler { - private BookSource source; + private final BookSource source; public ThirdCrawler(BookSource source) { this.source = source; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 89f06d1..b5a40f3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -521,6 +521,21 @@ 重新发送%s + + 没有书源 + 书籍信息获取失败 + 内容获取失败 + 目录获取失败 + 访问网站失败:%s + 文件读取失败 + 加载目录失败 + 获取数据失败! + 加载失败\n%s + 没有网络 + 网络连接超时 + 数据解析失败 + + 跟随系统 常亮 @@ -602,7 +617,8 @@ Matcher Xpath JsonPath - 第三方 + 第三方-2.0 + 第三方-3.0