|
|
@ -7,11 +7,14 @@ import io.legado.app.data.entities.Book |
|
|
|
import io.legado.app.data.entities.BookChapter |
|
|
|
import io.legado.app.data.entities.BookChapter |
|
|
|
import io.legado.app.data.entities.BookSource |
|
|
|
import io.legado.app.data.entities.BookSource |
|
|
|
import io.legado.app.data.entities.rule.TocRule |
|
|
|
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.Debug |
|
|
|
import io.legado.app.model.analyzeRule.AnalyzeRule |
|
|
|
import io.legado.app.model.analyzeRule.AnalyzeRule |
|
|
|
import io.legado.app.model.analyzeRule.AnalyzeUrl |
|
|
|
import io.legado.app.model.analyzeRule.AnalyzeUrl |
|
|
|
import kotlinx.coroutines.CoroutineScope |
|
|
|
import kotlinx.coroutines.CoroutineScope |
|
|
|
import kotlinx.coroutines.withContext |
|
|
|
import kotlin.coroutines.resume |
|
|
|
|
|
|
|
import kotlin.coroutines.resumeWithException |
|
|
|
|
|
|
|
import kotlin.coroutines.suspendCoroutine |
|
|
|
|
|
|
|
|
|
|
|
object BookChapterList { |
|
|
|
object BookChapterList { |
|
|
|
|
|
|
|
|
|
|
@ -21,95 +24,143 @@ object BookChapterList { |
|
|
|
body: String?, |
|
|
|
body: String?, |
|
|
|
bookSource: BookSource, |
|
|
|
bookSource: BookSource, |
|
|
|
baseUrl: String |
|
|
|
baseUrl: String |
|
|
|
): List<BookChapter> { |
|
|
|
): List<BookChapter> = suspendCoroutine { block -> |
|
|
|
var chapterList = ArrayList<BookChapter>() |
|
|
|
try { |
|
|
|
body ?: throw Exception( |
|
|
|
val chapterList = ArrayList<BookChapter>() |
|
|
|
App.INSTANCE.getString(R.string.error_get_web_content, baseUrl) |
|
|
|
body ?: throw Exception( |
|
|
|
) |
|
|
|
App.INSTANCE.getString(R.string.error_get_web_content, baseUrl) |
|
|
|
Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") |
|
|
|
) |
|
|
|
val tocRule = bookSource.getTocRule() |
|
|
|
Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") |
|
|
|
val nextUrlList = arrayListOf(baseUrl) |
|
|
|
val tocRule = bookSource.getTocRule() |
|
|
|
var reverse = false |
|
|
|
val nextUrlList = arrayListOf(baseUrl) |
|
|
|
var listRule = tocRule.chapterList ?: "" |
|
|
|
var reverse = false |
|
|
|
if (listRule.startsWith("-")) { |
|
|
|
var listRule = tocRule.chapterList ?: "" |
|
|
|
reverse = true |
|
|
|
if (listRule.startsWith("-")) { |
|
|
|
listRule = listRule.substring(1) |
|
|
|
reverse = true |
|
|
|
} |
|
|
|
listRule = listRule.substring(1) |
|
|
|
if (listRule.startsWith("+")) { |
|
|
|
|
|
|
|
listRule = listRule.substring(1) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
var chapterData = |
|
|
|
|
|
|
|
analyzeChapterList(body, baseUrl, tocRule, listRule, book, bookSource, log = true) |
|
|
|
|
|
|
|
chapterData.chapterList?.let { |
|
|
|
|
|
|
|
chapterList.addAll(it) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (chapterData.nextUrl.size == 1) { |
|
|
|
|
|
|
|
var nextUrl = chapterData.nextUrl[0] |
|
|
|
|
|
|
|
while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) { |
|
|
|
|
|
|
|
nextUrlList.add(nextUrl) |
|
|
|
|
|
|
|
AnalyzeUrl( |
|
|
|
|
|
|
|
ruleUrl = nextUrl, book = book, headerMapF = bookSource.getHeaderMap() |
|
|
|
|
|
|
|
).getResponseAwait() |
|
|
|
|
|
|
|
.body?.let { nextBody -> |
|
|
|
|
|
|
|
chapterData = analyzeChapterList( |
|
|
|
|
|
|
|
nextBody, nextUrl, tocRule, listRule, |
|
|
|
|
|
|
|
book, bookSource, log = false |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
nextUrl = if (chapterData.nextUrl.isNotEmpty()) |
|
|
|
|
|
|
|
chapterData.nextUrl[0] |
|
|
|
|
|
|
|
else "" |
|
|
|
|
|
|
|
chapterData.chapterList?.let { |
|
|
|
|
|
|
|
chapterList.addAll(it) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
Debug.log(bookSource.bookSourceUrl, "◇目录总页数:${nextUrlList.size}") |
|
|
|
if (listRule.startsWith("+")) { |
|
|
|
} else if (chapterData.nextUrl.size > 1) { |
|
|
|
listRule = listRule.substring(1) |
|
|
|
val chapterDataList = arrayListOf<ChapterData<String>>() |
|
|
|
|
|
|
|
for (item in chapterData.nextUrl) { |
|
|
|
|
|
|
|
val data = ChapterData(nextUrl = item) |
|
|
|
|
|
|
|
chapterDataList.add(data) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
for (item in chapterDataList) { |
|
|
|
var chapterData = |
|
|
|
withContext(coroutineScope.coroutineContext) { |
|
|
|
analyzeChapterList(body, baseUrl, tocRule, listRule, book, bookSource, log = true) |
|
|
|
val nextBody = AnalyzeUrl( |
|
|
|
chapterData.chapterList?.let { |
|
|
|
ruleUrl = item.nextUrl, |
|
|
|
chapterList.addAll(it) |
|
|
|
book = book, |
|
|
|
|
|
|
|
headerMapF = bookSource.getHeaderMap() |
|
|
|
|
|
|
|
).getResponseAwait().body |
|
|
|
|
|
|
|
val nextChapterData = analyzeChapterList( |
|
|
|
|
|
|
|
nextBody, item.nextUrl, tocRule, listRule, book, bookSource |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
item.chapterList = nextChapterData.chapterList |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
for (item in chapterDataList) { |
|
|
|
when (chapterData.nextUrl.size) { |
|
|
|
item.chapterList?.let { |
|
|
|
0 -> { |
|
|
|
chapterList.addAll(it) |
|
|
|
block.resume(finish(book, chapterList, reverse)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
1 -> { |
|
|
|
|
|
|
|
Coroutine.async(scope = coroutineScope) { |
|
|
|
|
|
|
|
var nextUrl = chapterData.nextUrl[0] |
|
|
|
|
|
|
|
while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) { |
|
|
|
|
|
|
|
nextUrlList.add(nextUrl) |
|
|
|
|
|
|
|
AnalyzeUrl( |
|
|
|
|
|
|
|
ruleUrl = nextUrl, |
|
|
|
|
|
|
|
book = book, |
|
|
|
|
|
|
|
headerMapF = bookSource.getHeaderMap() |
|
|
|
|
|
|
|
).getResponseAwait() |
|
|
|
|
|
|
|
.body?.let { nextBody -> |
|
|
|
|
|
|
|
chapterData = analyzeChapterList( |
|
|
|
|
|
|
|
nextBody, nextUrl, tocRule, listRule, |
|
|
|
|
|
|
|
book, bookSource, log = false |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
nextUrl = if (chapterData.nextUrl.isNotEmpty()) { |
|
|
|
|
|
|
|
chapterData.nextUrl[0] |
|
|
|
|
|
|
|
} else "" |
|
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
|
val data = ChapterData(nextUrl = item) |
|
|
|
|
|
|
|
chapterDataList.add(data) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for (item in chapterDataList) { |
|
|
|
|
|
|
|
Coroutine.async(scope = coroutineScope) { |
|
|
|
|
|
|
|
val nextBody = AnalyzeUrl( |
|
|
|
|
|
|
|
ruleUrl = item.nextUrl, |
|
|
|
|
|
|
|
book = book, |
|
|
|
|
|
|
|
headerMapF = bookSource.getHeaderMap() |
|
|
|
|
|
|
|
).getResponseAwait().body |
|
|
|
|
|
|
|
val nextChapterData = analyzeChapterList( |
|
|
|
|
|
|
|
nextBody, item.nextUrl, tocRule, listRule, book, bookSource |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
synchronized(chapterDataList) { |
|
|
|
|
|
|
|
val isFinished = addChapterListIsFinish( |
|
|
|
|
|
|
|
chapterDataList, |
|
|
|
|
|
|
|
item, |
|
|
|
|
|
|
|
nextChapterData.chapterList |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
if (isFinished) { |
|
|
|
|
|
|
|
chapterDataList.forEach { item -> |
|
|
|
|
|
|
|
item.chapterList?.let { |
|
|
|
|
|
|
|
chapterList.addAll(it) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
block.resume(finish(book, chapterList, reverse)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}.onError { |
|
|
|
|
|
|
|
block.resumeWithException(it) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
|
|
block.resumeWithException(e) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) { |
|
|
|
if (!reverse) { |
|
|
|
chapterList.reverse() |
|
|
|
chapterList.reverse() |
|
|
|
} |
|
|
|
} |
|
|
|
val lh = LinkedHashSet(chapterList) |
|
|
|
val lh = LinkedHashSet(chapterList) |
|
|
|
chapterList = ArrayList(lh) |
|
|
|
val list = ArrayList(lh) |
|
|
|
chapterList.reverse() |
|
|
|
list.reverse() |
|
|
|
for ((index, item) in chapterList.withIndex()) { |
|
|
|
for ((index, item) in list.withIndex()) { |
|
|
|
item.index = index |
|
|
|
item.index = index |
|
|
|
} |
|
|
|
} |
|
|
|
book.latestChapterTitle = chapterList.last().title |
|
|
|
book.latestChapterTitle = list.last().title |
|
|
|
book.durChapterTitle = |
|
|
|
book.durChapterTitle = |
|
|
|
chapterList.getOrNull(book.durChapterIndex)?.title ?: book.latestChapterTitle |
|
|
|
list.getOrNull(book.durChapterIndex)?.title ?: book.latestChapterTitle |
|
|
|
if (book.totalChapterNum < chapterList.size) { |
|
|
|
if (book.totalChapterNum < list.size) { |
|
|
|
book.lastCheckCount = chapterList.size - book.totalChapterNum |
|
|
|
book.lastCheckCount = list.size - book.totalChapterNum |
|
|
|
} |
|
|
|
} |
|
|
|
book.totalChapterNum = chapterList.size |
|
|
|
book.totalChapterNum = list.size |
|
|
|
return chapterList |
|
|
|
return list |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun analyzeChapterList( |
|
|
|
private fun analyzeChapterList( |
|
|
|
body: String?, |
|
|
|
body: String?, |
|
|
|
baseUrl: String, |
|
|
|
baseUrl: String, |
|
|
|