diff --git a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt index 94ba0baf7..1bba73ec1 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt @@ -12,6 +12,7 @@ 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 kotlin.coroutines.resume import kotlin.coroutines.resumeWithException @@ -19,13 +20,13 @@ import kotlin.coroutines.resumeWithException object BookChapterList { suspend fun analyzeChapterList( - coroutineScope: CoroutineScope, + scope: CoroutineScope, book: Book, body: String?, bookSource: BookSource, baseUrl: String ): List = suspendCancellableCoroutine { block -> - try { + kotlin.runCatching { val chapterList = ArrayList() body ?: throw Exception( App.INSTANCE.getString(R.string.error_get_web_content, baseUrl) @@ -45,7 +46,7 @@ object BookChapterList { } var chapterData = analyzeChapterList( - book, baseUrl, body, tocRule, listRule, bookSource, log = true + scope, book, baseUrl, body, tocRule, listRule, bookSource, log = true ) chapterData.chapterList?.let { chapterList.addAll(it) @@ -55,7 +56,7 @@ object BookChapterList { block.resume(finish(book, chapterList, reverse)) } 1 -> { - Coroutine.async(scope = coroutineScope) { + Coroutine.async(scope = scope) { var nextUrl = chapterData.nextUrl[0] while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) { nextUrlList.add(nextUrl) @@ -63,18 +64,15 @@ object BookChapterList { ruleUrl = nextUrl, book = book, headerMapF = bookSource.getHeaderMap() - ).getStrResponse(bookSource.bookSourceUrl) - .body?.let { nextBody -> - chapterData = analyzeChapterList( - book, nextUrl, nextBody, tocRule, listRule, bookSource - ) - nextUrl = if (chapterData.nextUrl.isNotEmpty()) { - chapterData.nextUrl[0] - } else "" - chapterData.chapterList?.let { - chapterList.addAll(it) - } + ).getStrResponse(bookSource.bookSourceUrl).body?.let { nextBody -> + chapterData = analyzeChapterList( + this, book, 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)) @@ -94,29 +92,21 @@ object BookChapterList { Debug.log(bookSource.bookSourceUrl, "◇目录总页数:${nextUrlList.size}") for (item in chapterDataList) { downloadToc( - coroutineScope, - item, - book, - bookSource, - tocRule, - listRule, - chapterList, - chapterDataList, - { - block.resume(finish(book, chapterList, reverse)) - }, { - block.cancel(it) - }) + scope, item, book, bookSource, + tocRule, listRule, chapterList, chapterDataList + ) { + block.resume(finish(book, chapterList, reverse)) + } } } } - } catch (e: Exception) { - block.resumeWithException(e) + }.onFailure { + block.resumeWithException(it) } } private fun downloadToc( - coroutineScope: CoroutineScope, + scope: CoroutineScope, chapterData: ChapterData, book: Book, bookSource: BookSource, @@ -124,10 +114,9 @@ object BookChapterList { listRule: String, chapterList: ArrayList, chapterDataList: ArrayList>, - onFinish: () -> Unit, - onError: (e: Throwable) -> Unit + onFinish: () -> Unit ) { - Coroutine.async(scope = coroutineScope) { + Coroutine.async(scope = scope) { val nextBody = AnalyzeUrl( ruleUrl = chapterData.nextUrl, book = book, @@ -135,8 +124,7 @@ object BookChapterList { ).getStrResponse(bookSource.bookSourceUrl).body ?: throw Exception("${chapterData.nextUrl}, 下载失败") val nextChapterData = analyzeChapterList( - book, chapterData.nextUrl, nextBody, tocRule, listRule, bookSource, - false + this, book, chapterData.nextUrl, nextBody, tocRule, listRule, bookSource, false ) synchronized(chapterDataList) { val isFinished = addChapterListIsFinish( @@ -154,7 +142,7 @@ object BookChapterList { } } }.onError { - onError(it) + throw it } } @@ -200,6 +188,7 @@ object BookChapterList { } private fun analyzeChapterList( + scope: CoroutineScope, book: Book, baseUrl: String, body: String, @@ -232,6 +221,7 @@ object BookChapterList { Debug.log(bookSource.bookSourceUrl, "┌获取目录列表", log) val elements = analyzeRule.getElements(listRule) Debug.log(bookSource.bookSourceUrl, "└列表大小:${elements.size}", log) + scope.ensureActive() if (elements.isNotEmpty()) { val nameRule = analyzeRule.splitSourceRule(tocRule.chapterName) val urlRule = analyzeRule.splitSourceRule(tocRule.chapterUrl) @@ -239,6 +229,7 @@ object BookChapterList { 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 diff --git a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt index f1bb8d405..25fcfa512 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt @@ -19,7 +19,7 @@ object BookContent { @Throws(Exception::class) suspend fun analyzeContent( - coroutineScope: CoroutineScope, + scope: CoroutineScope, body: String?, book: Book, bookChapter: BookChapter, @@ -73,7 +73,7 @@ object BookContent { contentDataList.add(ContentData(nextUrl = item)) } for (item in contentDataList) { - withContext(coroutineScope.coroutineContext) { + withContext(scope.coroutineContext) { AnalyzeUrl( ruleUrl = item.nextUrl, book = book, diff --git a/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt b/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt index 851995f37..d4eb8b9d9 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt @@ -11,6 +11,7 @@ import io.legado.app.utils.NetworkUtils import io.legado.app.utils.StringUtils.wordCountFormat import io.legado.app.utils.htmlFormat import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ensureActive object BookInfo { @@ -32,11 +33,13 @@ object BookInfo { analyzeRule.setContent(body).setBaseUrl(baseUrl) infoRule.init?.let { if (it.isNotBlank()) { + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "≡执行详情页初始化规则") analyzeRule.setContent(analyzeRule.getElement(it)) } } val mCanReName = canReName && !infoRule.canReName.isNullOrBlank() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取书名") BookHelp.formatBookName(analyzeRule.getString(infoRule.name)).let { if (it.isNotEmpty() && (mCanReName || book.name.isEmpty())) { @@ -44,6 +47,7 @@ object BookInfo { } Debug.log(bookSource.bookSourceUrl, "└${it}") } + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取作者") BookHelp.formatBookAuthor(analyzeRule.getString(infoRule.author)).let { if (it.isNotEmpty() && (mCanReName || book.author.isEmpty())) { @@ -51,6 +55,7 @@ object BookInfo { } Debug.log(bookSource.bookSourceUrl, "└${it}") } + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取分类") analyzeRule.getStringList(infoRule.kind) ?.joinToString(",") @@ -58,27 +63,31 @@ object BookInfo { if (it.isNotEmpty()) book.kind = it } Debug.log(bookSource.bookSourceUrl, "└${book.kind}") + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取字数") wordCountFormat(analyzeRule.getString(infoRule.wordCount)).let { if (it.isNotEmpty()) book.wordCount = it } Debug.log(bookSource.bookSourceUrl, "└${book.wordCount}") + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取最新章节") analyzeRule.getString(infoRule.lastChapter).let { if (it.isNotEmpty()) book.latestChapterTitle = it } Debug.log(bookSource.bookSourceUrl, "└${book.latestChapterTitle}") + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取简介") analyzeRule.getString(infoRule.intro).let { if (it.isNotEmpty()) book.intro = it.htmlFormat() } Debug.log(bookSource.bookSourceUrl, "└${book.intro}") - + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取封面链接") analyzeRule.getString(infoRule.coverUrl).let { if (it.isNotEmpty()) book.coverUrl = NetworkUtils.getAbsoluteURL(baseUrl, it) } Debug.log(bookSource.bookSourceUrl, "└${book.coverUrl}") + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取目录链接") book.tocUrl = analyzeRule.getString(infoRule.tocUrl, true) if (book.tocUrl.isEmpty()) book.tocUrl = baseUrl diff --git a/app/src/main/java/io/legado/app/model/webBook/BookList.kt b/app/src/main/java/io/legado/app/model/webBook/BookList.kt index 464580854..25ab215e6 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookList.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookList.kt @@ -12,9 +12,8 @@ import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.utils.NetworkUtils import io.legado.app.utils.StringUtils.wordCountFormat import io.legado.app.utils.htmlFormat -import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.isActive +import kotlinx.coroutines.ensureActive object BookList { @@ -36,10 +35,10 @@ object BookList { ) ) Debug.log(bookSource.bookSourceUrl, "≡获取成功:${analyzeUrl.ruleUrl}") - if (!scope.isActive) throw CancellationException() val analyzeRule = AnalyzeRule(variableBook) analyzeRule.setContent(body).setBaseUrl(baseUrl) bookSource.bookUrlPattern?.let { + scope.ensureActive() if (baseUrl.matches(it.toRegex())) { Debug.log(bookSource.bookSourceUrl, "≡链接为详情页") getInfoItem(scope, analyzeRule, bookSource, baseUrl, variableBook.variable) @@ -67,6 +66,7 @@ object BookList { } Debug.log(bookSource.bookSourceUrl, "┌获取书籍列表") collections = analyzeRule.getElements(ruleList) + scope.ensureActive() if (collections.isEmpty() && bookSource.bookUrlPattern.isNullOrEmpty()) { Debug.log(bookSource.bookSourceUrl, "└列表为空,按详情页解析") getInfoItem(scope, analyzeRule, bookSource, baseUrl, variableBook.variable) @@ -85,7 +85,6 @@ object BookList { val ruleWordCount = analyzeRule.splitSourceRule(bookListRule.wordCount) Debug.log(bookSource.bookSourceUrl, "└列表大小:${collections.size}") for ((index, item) in collections.withIndex()) { - if (!scope.isActive) throw CancellationException() getSearchItem( scope, item, @@ -134,37 +133,37 @@ object BookList { with(bookSource.getBookInfoRule()) { init?.let { if (it.isNotEmpty()) { - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "≡执行详情页初始化规则") analyzeRule.setContent(analyzeRule.getElement(it)) } } - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取书名") searchBook.name = BookHelp.formatBookName(analyzeRule.getString(name)) Debug.log(bookSource.bookSourceUrl, "└${searchBook.name}") if (searchBook.name.isNotEmpty()) { - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取作者") searchBook.author = BookHelp.formatBookAuthor(analyzeRule.getString(author)) Debug.log(bookSource.bookSourceUrl, "└${searchBook.author}") - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取分类") searchBook.kind = analyzeRule.getStringList(kind)?.joinToString(",") Debug.log(bookSource.bookSourceUrl, "└${searchBook.kind}") - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取字数") searchBook.wordCount = wordCountFormat(analyzeRule.getString(wordCount)) Debug.log(bookSource.bookSourceUrl, "└${searchBook.wordCount}") - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取最新章节") searchBook.latestChapterTitle = analyzeRule.getString(lastChapter) Debug.log(bookSource.bookSourceUrl, "└${searchBook.latestChapterTitle}") - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取简介") searchBook.intro = analyzeRule.getString(intro).htmlFormat() Debug.log(bookSource.bookSourceUrl, "└${searchBook.intro}") - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取封面链接") searchBook.coverUrl = analyzeRule.getString(coverUrl, true) Debug.log(bookSource.bookSourceUrl, "└${searchBook.coverUrl}") @@ -199,38 +198,38 @@ object BookList { searchBook.originOrder = bookSource.customOrder analyzeRule.book = searchBook analyzeRule.setContent(item) - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取书名", log) searchBook.name = BookHelp.formatBookName(analyzeRule.getString(ruleName)) Debug.log(bookSource.bookSourceUrl, "└${searchBook.name}", log) if (searchBook.name.isNotEmpty()) { - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取作者", log) searchBook.author = BookHelp.formatBookAuthor(analyzeRule.getString(ruleAuthor)) Debug.log(bookSource.bookSourceUrl, "└${searchBook.author}", log) - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取分类", log) searchBook.kind = analyzeRule.getStringList(ruleKind)?.joinToString(",") Debug.log(bookSource.bookSourceUrl, "└${searchBook.kind}", log) - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取字数", log) searchBook.wordCount = wordCountFormat(analyzeRule.getString(ruleWordCount)) Debug.log(bookSource.bookSourceUrl, "└${searchBook.wordCount}", log) - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取最新章节", log) searchBook.latestChapterTitle = analyzeRule.getString(ruleLastChapter) Debug.log(bookSource.bookSourceUrl, "└${searchBook.latestChapterTitle}", log) - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取简介", log) searchBook.intro = analyzeRule.getString(ruleIntro).htmlFormat() Debug.log(bookSource.bookSourceUrl, "└${searchBook.intro}", log) - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取封面链接", log) analyzeRule.getString(ruleCoverUrl).let { if (it.isNotEmpty()) searchBook.coverUrl = NetworkUtils.getAbsoluteURL(baseUrl, it) } Debug.log(bookSource.bookSourceUrl, "└${searchBook.coverUrl}", log) - if (!scope.isActive) throw CancellationException() + scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取详情页链接", log) searchBook.bookUrl = analyzeRule.getString(ruleBookUrl, true) if (searchBook.bookUrl.isEmpty()) {