|  |  |  | @ -6,16 +6,16 @@ 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.Dispatchers.IO | 
			
		
	
		
			
				
					|  |  |  |  | import kotlinx.coroutines.async | 
			
		
	
		
			
				
					|  |  |  |  | import kotlinx.coroutines.ensureActive | 
			
		
	
		
			
				
					|  |  |  |  | import kotlinx.coroutines.suspendCancellableCoroutine | 
			
		
	
		
			
				
					|  |  |  |  | import kotlinx.coroutines.withContext | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | import splitties.init.appCtx | 
			
		
	
		
			
				
					|  |  |  |  | import kotlin.coroutines.resume | 
			
		
	
		
			
				
					|  |  |  |  | import kotlin.coroutines.resumeWithException | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | object BookChapterList { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -26,153 +26,81 @@ object BookChapterList { | 
			
		
	
		
			
				
					|  |  |  |  |         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}") | 
			
		
	
		
			
				
					|  |  |  |  |     ): List<BookChapter> { | 
			
		
	
		
			
				
					|  |  |  |  |         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 = false | 
			
		
	
		
			
				
					|  |  |  |  |             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) | 
			
		
	
		
			
				
					|  |  |  |  |                                 } | 
			
		
	
		
			
				
					|  |  |  |  |                             } | 
			
		
	
		
			
				
					|  |  |  |  |         val tocRule = bookSource.getTocRule() | 
			
		
	
		
			
				
					|  |  |  |  |         val nextUrlList = arrayListOf(baseUrl) | 
			
		
	
		
			
				
					|  |  |  |  |         var reverse = false | 
			
		
	
		
			
				
					|  |  |  |  |         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 -> Unit | 
			
		
	
		
			
				
					|  |  |  |  |             1 -> { | 
			
		
	
		
			
				
					|  |  |  |  |                 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( | 
			
		
	
		
			
				
					|  |  |  |  |                             scope, 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) | 
			
		
	
		
			
				
					|  |  |  |  |                 Debug.log(bookSource.bookSourceUrl, "◇目录总页数:${nextUrlList.size}") | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             else -> { | 
			
		
	
		
			
				
					|  |  |  |  |                 Debug.log(bookSource.bookSourceUrl, "◇目录总页数:${nextUrlList.size}") | 
			
		
	
		
			
				
					|  |  |  |  |                 withContext(IO) { | 
			
		
	
		
			
				
					|  |  |  |  |                     val asyncArray = Array(chapterData.nextUrl.size) { | 
			
		
	
		
			
				
					|  |  |  |  |                         async { | 
			
		
	
		
			
				
					|  |  |  |  |                             val urlStr = chapterData.nextUrl[it] | 
			
		
	
		
			
				
					|  |  |  |  |                             val analyzeUrl = AnalyzeUrl( | 
			
		
	
		
			
				
					|  |  |  |  |                                 ruleUrl = urlStr, | 
			
		
	
		
			
				
					|  |  |  |  |                                 book = book, | 
			
		
	
		
			
				
					|  |  |  |  |                                 headerMapF = bookSource.getHeaderMap() | 
			
		
	
		
			
				
					|  |  |  |  |                             ) | 
			
		
	
		
			
				
					|  |  |  |  |                             val res = analyzeUrl.getStrResponse(bookSource.bookSourceUrl) | 
			
		
	
		
			
				
					|  |  |  |  |                             analyzeChapterList( | 
			
		
	
		
			
				
					|  |  |  |  |                                 this, book, urlStr, res.url, | 
			
		
	
		
			
				
					|  |  |  |  |                                 res.body!!, tocRule, listRule, bookSource, false | 
			
		
	
		
			
				
					|  |  |  |  |                             ).chapterList | 
			
		
	
		
			
				
					|  |  |  |  |                         } | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |         }.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 { | 
			
		
	
		
			
				
					|  |  |  |  |                     asyncArray.forEach { coroutine -> | 
			
		
	
		
			
				
					|  |  |  |  |                         coroutine.await()?.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() | 
			
		
	
	
		
			
				
					|  |  |  | 
 |