parent
bc81041c0f
commit
136386cbfc
@ -1,274 +0,0 @@ |
|||||||
package io.legado.app.model.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 io.legado.app.data.entities.rule.TocRule |
|
||||||
import io.legado.app.help.coroutine.Coroutine |
|
||||||
import io.legado.app.model.Debug |
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeRule |
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeUrl |
|
||||||
import kotlinx.coroutines.CoroutineScope |
|
||||||
import kotlinx.coroutines.ensureActive |
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine |
|
||||||
import splitties.init.appCtx |
|
||||||
import kotlin.coroutines.resume |
|
||||||
import kotlin.coroutines.resumeWithException |
|
||||||
|
|
||||||
object BookChapterList { |
|
||||||
|
|
||||||
suspend fun analyzeChapterList( |
|
||||||
scope: CoroutineScope, |
|
||||||
book: Book, |
|
||||||
body: String?, |
|
||||||
bookSource: BookSource, |
|
||||||
baseUrl: String, |
|
||||||
redirectUrl: String |
|
||||||
): List<BookChapter> = suspendCancellableCoroutine { block -> |
|
||||||
kotlin.runCatching { |
|
||||||
val chapterList = ArrayList<BookChapter>() |
|
||||||
body ?: throw Exception( |
|
||||||
appCtx.getString(R.string.error_get_web_content, baseUrl) |
|
||||||
) |
|
||||||
Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") |
|
||||||
|
|
||||||
val tocRule = bookSource.getTocRule() |
|
||||||
val nextUrlList = arrayListOf(baseUrl) |
|
||||||
var reverse = AnalyzeRule.userReverse |
|
||||||
var listRule = tocRule.chapterList ?: "" |
|
||||||
if (listRule.startsWith("-")) { |
|
||||||
reverse = true |
|
||||||
listRule = listRule.substring(1) |
|
||||||
} |
|
||||||
if (listRule.startsWith("+")) { |
|
||||||
listRule = listRule.substring(1) |
|
||||||
} |
|
||||||
var chapterData = |
|
||||||
analyzeChapterList( |
|
||||||
scope, book, baseUrl, redirectUrl, body, |
|
||||||
tocRule, listRule, bookSource, log = true |
|
||||||
) |
|
||||||
chapterData.chapterList?.let { |
|
||||||
chapterList.addAll(it) |
|
||||||
} |
|
||||||
when (chapterData.nextUrl.size) { |
|
||||||
0 -> { |
|
||||||
block.resume(finish(book, chapterList, reverse)) |
|
||||||
} |
|
||||||
1 -> { |
|
||||||
Coroutine.async(scope = scope) { |
|
||||||
var nextUrl = chapterData.nextUrl[0] |
|
||||||
while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) { |
|
||||||
nextUrlList.add(nextUrl) |
|
||||||
AnalyzeUrl( |
|
||||||
ruleUrl = nextUrl, |
|
||||||
book = book, |
|
||||||
headerMapF = bookSource.getHeaderMap() |
|
||||||
).getStrResponse(bookSource.bookSourceUrl).body?.let { nextBody -> |
|
||||||
chapterData = analyzeChapterList( |
|
||||||
this, book, nextUrl, nextUrl, |
|
||||||
nextBody, tocRule, listRule, bookSource |
|
||||||
) |
|
||||||
nextUrl = chapterData.nextUrl.firstOrNull() ?: "" |
|
||||||
chapterData.chapterList?.let { |
|
||||||
chapterList.addAll(it) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
Debug.log(bookSource.bookSourceUrl, "◇目录总页数:${nextUrlList.size}") |
|
||||||
block.resume(finish(book, chapterList, reverse)) |
|
||||||
}.onError { |
|
||||||
block.resumeWithException(it) |
|
||||||
} |
|
||||||
} |
|
||||||
else -> { |
|
||||||
val chapterDataList = arrayListOf<ChapterData<String>>() |
|
||||||
for (item in chapterData.nextUrl) { |
|
||||||
if (!nextUrlList.contains(item)) { |
|
||||||
val data = ChapterData(nextUrl = item) |
|
||||||
chapterDataList.add(data) |
|
||||||
nextUrlList.add(item) |
|
||||||
} |
|
||||||
} |
|
||||||
Debug.log(bookSource.bookSourceUrl, "◇目录总页数:${nextUrlList.size}") |
|
||||||
for (item in chapterDataList) { |
|
||||||
downloadToc( |
|
||||||
scope, item, book, bookSource, |
|
||||||
tocRule, listRule, chapterList, chapterDataList, |
|
||||||
{ |
|
||||||
block.resume(finish(book, chapterList, reverse)) |
|
||||||
} |
|
||||||
) { |
|
||||||
block.cancel(it) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}.onFailure { |
|
||||||
block.cancel(it) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private fun downloadToc( |
|
||||||
scope: CoroutineScope, |
|
||||||
chapterData: ChapterData<String>, |
|
||||||
book: Book, |
|
||||||
bookSource: BookSource, |
|
||||||
tocRule: TocRule, |
|
||||||
listRule: String, |
|
||||||
chapterList: ArrayList<BookChapter>, |
|
||||||
chapterDataList: ArrayList<ChapterData<String>>, |
|
||||||
onFinish: () -> Unit, |
|
||||||
onError: (error: Throwable) -> Unit |
|
||||||
) { |
|
||||||
Coroutine.async(scope = scope) { |
|
||||||
val nextBody = AnalyzeUrl( |
|
||||||
ruleUrl = chapterData.nextUrl, |
|
||||||
book = book, |
|
||||||
headerMapF = bookSource.getHeaderMap() |
|
||||||
).getStrResponse(bookSource.bookSourceUrl).body |
|
||||||
?: throw Exception("${chapterData.nextUrl}, 下载失败") |
|
||||||
val nextChapterData = analyzeChapterList( |
|
||||||
this, book, chapterData.nextUrl, chapterData.nextUrl, |
|
||||||
nextBody, tocRule, listRule, bookSource, false |
|
||||||
) |
|
||||||
synchronized(chapterDataList) { |
|
||||||
val isFinished = addChapterListIsFinish( |
|
||||||
chapterDataList, |
|
||||||
chapterData, |
|
||||||
nextChapterData.chapterList |
|
||||||
) |
|
||||||
if (isFinished) { |
|
||||||
chapterDataList.forEach { item -> |
|
||||||
item.chapterList?.let { |
|
||||||
chapterList.addAll(it) |
|
||||||
} |
|
||||||
} |
|
||||||
onFinish() |
|
||||||
} |
|
||||||
} |
|
||||||
}.onError { |
|
||||||
onError.invoke(it) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private fun addChapterListIsFinish( |
|
||||||
chapterDataList: ArrayList<ChapterData<String>>, |
|
||||||
chapterData: ChapterData<String>, |
|
||||||
chapterList: List<BookChapter>? |
|
||||||
): Boolean { |
|
||||||
chapterData.chapterList = chapterList |
|
||||||
chapterDataList.forEach { |
|
||||||
if (it.chapterList == null) { |
|
||||||
return false |
|
||||||
} |
|
||||||
} |
|
||||||
return true |
|
||||||
} |
|
||||||
|
|
||||||
private fun finish( |
|
||||||
book: Book, |
|
||||||
chapterList: ArrayList<BookChapter>, |
|
||||||
reverse: Boolean |
|
||||||
): ArrayList<BookChapter> { |
|
||||||
//去重 |
|
||||||
if (!reverse) { |
|
||||||
chapterList.reverse() |
|
||||||
} |
|
||||||
val lh = LinkedHashSet(chapterList) |
|
||||||
val list = ArrayList(lh) |
|
||||||
list.reverse() |
|
||||||
Debug.log(book.origin, "◇目录总数:${list.size}") |
|
||||||
for ((index, item) in list.withIndex()) { |
|
||||||
item.index = 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.totalChapterNum = list.size |
|
||||||
return list |
|
||||||
} |
|
||||||
|
|
||||||
private fun analyzeChapterList( |
|
||||||
scope: CoroutineScope, |
|
||||||
book: Book, |
|
||||||
baseUrl: String, |
|
||||||
redirectUrl: String, |
|
||||||
body: String, |
|
||||||
tocRule: TocRule, |
|
||||||
listRule: String, |
|
||||||
bookSource: BookSource, |
|
||||||
getNextUrl: Boolean = true, |
|
||||||
log: Boolean = false |
|
||||||
): ChapterData<List<String>> { |
|
||||||
val analyzeRule = AnalyzeRule(book) |
|
||||||
analyzeRule.setContent(body).setBaseUrl(baseUrl) |
|
||||||
analyzeRule.setRedirectUrl(redirectUrl) |
|
||||||
//获取目录列表 |
|
||||||
val chapterList = arrayListOf<BookChapter>() |
|
||||||
Debug.log(bookSource.bookSourceUrl, "┌获取目录列表", log) |
|
||||||
val elements = analyzeRule.getElements(listRule) |
|
||||||
Debug.log(bookSource.bookSourceUrl, "└列表大小:${elements.size}", log) |
|
||||||
//获取下一页链接 |
|
||||||
val nextUrlList = arrayListOf<String>() |
|
||||||
val nextTocRule = tocRule.nextTocUrl |
|
||||||
if (getNextUrl && !nextTocRule.isNullOrEmpty()) { |
|
||||||
Debug.log(bookSource.bookSourceUrl, "┌获取目录下一页列表", log) |
|
||||||
analyzeRule.getStringList(nextTocRule, true)?.let { |
|
||||||
for (item in it) { |
|
||||||
if (item != baseUrl) { |
|
||||||
nextUrlList.add(item) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
Debug.log( |
|
||||||
bookSource.bookSourceUrl, |
|
||||||
"└" + TextUtils.join(",\n", nextUrlList), |
|
||||||
log |
|
||||||
) |
|
||||||
} |
|
||||||
scope.ensureActive() |
|
||||||
if (elements.isNotEmpty()) { |
|
||||||
Debug.log(bookSource.bookSourceUrl, "┌解析目录列表", log) |
|
||||||
val nameRule = analyzeRule.splitSourceRule(tocRule.chapterName) |
|
||||||
val urlRule = analyzeRule.splitSourceRule(tocRule.chapterUrl) |
|
||||||
val vipRule = analyzeRule.splitSourceRule(tocRule.isVip) |
|
||||||
val update = analyzeRule.splitSourceRule(tocRule.updateTime) |
|
||||||
var isVip: String? |
|
||||||
for (item in elements) { |
|
||||||
scope.ensureActive() |
|
||||||
analyzeRule.setContent(item) |
|
||||||
val bookChapter = BookChapter(bookUrl = book.bookUrl, baseUrl = baseUrl) |
|
||||||
analyzeRule.chapter = bookChapter |
|
||||||
bookChapter.title = analyzeRule.getString(nameRule) |
|
||||||
bookChapter.url = analyzeRule.getString(urlRule) |
|
||||||
bookChapter.tag = analyzeRule.getString(update) |
|
||||||
isVip = analyzeRule.getString(vipRule) |
|
||||||
if (bookChapter.url.isEmpty()) { |
|
||||||
bookChapter.url = baseUrl |
|
||||||
} |
|
||||||
if (bookChapter.title.isNotEmpty()) { |
|
||||||
if (isVip.isNotEmpty() && isVip != "null" && isVip != "false" && isVip != "0") { |
|
||||||
bookChapter.title = "\uD83D\uDD12" + bookChapter.title |
|
||||||
} |
|
||||||
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) |
|
||||||
} |
|
||||||
return ChapterData(chapterList, nextUrlList) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
Loading…
Reference in new issue