pull/2146/head
kunfei 2 years ago
parent e8f947b87a
commit 6356991920
  1. 4
      app/src/main/java/io/legado/app/api/controller/BookController.kt
  2. 8
      app/src/main/java/io/legado/app/help/BookHelp.kt
  3. 2
      app/src/main/java/io/legado/app/model/CacheBook.kt
  4. 6
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
  5. 22
      app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt
  6. 11
      app/src/main/java/io/legado/app/model/webBook/BookContent.kt
  7. 30
      app/src/main/java/io/legado/app/model/webBook/BookInfo.kt
  8. 37
      app/src/main/java/io/legado/app/model/webBook/BookList.kt
  9. 24
      app/src/main/java/io/legado/app/model/webBook/WebBook.kt
  10. 13
      app/src/main/java/io/legado/app/service/CheckSourceService.kt
  11. 32
      app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt
  12. 19
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt
  13. 2
      app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageViewModel.kt
  14. 4
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  15. 2
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt
  16. 15
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt
  17. 4
      app/src/main/java/io/legado/app/ui/main/MainViewModel.kt

@ -110,9 +110,9 @@ object BookController {
?: return returnData.setErrorMsg("未找到对应书源,请换源")
val toc = runBlocking {
if (book.tocUrl.isBlank()) {
WebBook.getBookInfoAwait(this, bookSource, book)
WebBook.getBookInfoAwait(bookSource, book)
}
WebBook.getChapterListAwait(this, bookSource, book).getOrThrow()
WebBook.getChapterListAwait(bookSource, book).getOrThrow()
}
appDb.bookChapterDao.delByBook(book.bookUrl)
appDb.bookChapterDao.insert(*toc.toTypedArray())

@ -68,14 +68,13 @@ object BookHelp {
}
suspend fun saveContent(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
bookChapter: BookChapter,
content: String
) {
saveText(book, bookChapter, content)
saveImages(scope, bookSource, book, bookChapter, content)
saveImages(bookSource, book, bookChapter, content)
postEvent(EventBus.SAVE_CONTENT, bookChapter)
}
@ -95,7 +94,6 @@ object BookHelp {
}
private suspend fun saveImages(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
bookChapter: BookChapter,
@ -107,8 +105,10 @@ object BookHelp {
if (matcher.find()) {
matcher.group(1)?.let { src ->
val mSrc = NetworkUtils.getAbsoluteURL(bookChapter.url, src)
awaitList.add(scope.async {
awaitList.add(coroutineScope {
async {
saveImage(bookSource, book, mSrc)
}
})
}
}

@ -179,7 +179,7 @@ object CacheBook {
val chapterCount = appDb.bookChapterDao.getChapterCount(book.bookUrl)
if (chapterCount == 0) {
runBlocking {
WebBook.getChapterListAwait(this, bookSource, book)
WebBook.getChapterListAwait(bookSource, book)
.onFailure {
AppLog.put("缓存书籍没有目录且加载目录失败\n${it.localizedMessage}", it)
appCtx.toastOnUi("缓存书籍没有目录且加载目录失败\n${it.localizedMessage}")

@ -700,7 +700,7 @@ class AnalyzeRule(
book.putVariable(entry.key, entry.value)
}
}
WebBook.getBookInfoAwait(this, bookSource, book, false)
WebBook.getBookInfoAwait(bookSource, book, false)
}
}
}
@ -714,7 +714,7 @@ class AnalyzeRule(
if (bookSource == null || book == null) return
runBlocking {
withTimeout(1800000) {
WebBook.getBookInfoAwait(this, bookSource, book)
WebBook.getBookInfoAwait(bookSource, book)
}
}
}
@ -728,7 +728,7 @@ class AnalyzeRule(
if (bookSource == null || book == null) return
runBlocking {
withTimeout(1800000) {
WebBook.getBookInfoAwait(this, bookSource, book)
WebBook.getBookInfoAwait(bookSource, book)
}
}
}

@ -13,12 +13,12 @@ import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.utils.isTrue
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 kotlin.coroutines.coroutineContext
/**
* 获取目录
@ -26,7 +26,6 @@ import splitties.init.appCtx
object BookChapterList {
suspend fun analyzeChapterList(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
baseUrl: String,
@ -52,7 +51,7 @@ object BookChapterList {
}
var chapterData =
analyzeChapterList(
scope, book, baseUrl, redirectUrl, body,
book, baseUrl, redirectUrl, body,
tocRule, listRule, bookSource, log = true
)
chapterList.addAll(chapterData.first)
@ -69,7 +68,7 @@ object BookChapterList {
headerMapF = bookSource.getHeaderMap()
).getStrResponseAwait().body?.let { nextBody ->
chapterData = analyzeChapterList(
scope, book, nextUrl, nextUrl,
book, nextUrl, nextUrl,
nextBody, tocRule, listRule, bookSource
)
nextUrl = chapterData.second.firstOrNull() ?: ""
@ -92,7 +91,7 @@ object BookChapterList {
)
val res = analyzeUrl.getStrResponseAwait()
analyzeChapterList(
this, book, urlStr, res.url,
book, urlStr, res.url,
res.body!!, tocRule, listRule, bookSource, false
).first
}
@ -110,14 +109,14 @@ object BookChapterList {
if (!reverse) {
chapterList.reverse()
}
scope.ensureActive()
coroutineContext.ensureActive()
val lh = LinkedHashSet(chapterList)
val list = ArrayList(lh)
if (!book.getReverseToc()) {
list.reverse()
}
Debug.log(book.origin, "◇目录总数:${list.size}")
scope.ensureActive()
coroutineContext.ensureActive()
list.forEachIndexed { index, bookChapter ->
bookChapter.index = index
}
@ -131,12 +130,11 @@ object BookChapterList {
}
book.lastCheckTime = System.currentTimeMillis()
book.totalChapterNum = list.size
scope.ensureActive()
coroutineContext.ensureActive()
return list
}
private fun analyzeChapterList(
scope: CoroutineScope,
private suspend fun analyzeChapterList(
book: Book,
baseUrl: String,
redirectUrl: String,
@ -173,7 +171,7 @@ object BookChapterList {
log
)
}
scope.ensureActive()
coroutineContext.ensureActive()
if (elements.isNotEmpty()) {
Debug.log(bookSource.bookSourceUrl, "┌解析目录列表", log)
val nameRule = analyzeRule.splitSourceRule(tocRule.chapterName)
@ -183,7 +181,7 @@ object BookChapterList {
val upTimeRule = analyzeRule.splitSourceRule(tocRule.updateTime)
val isVolumeRule = analyzeRule.splitSourceRule(tocRule.isVolume)
elements.forEachIndexed { index, item ->
scope.ensureActive()
coroutineContext.ensureActive()
analyzeRule.setContent(item)
val bookChapter = BookChapter(bookUrl = book.bookUrl, baseUrl = redirectUrl)
analyzeRule.chapter = bookChapter

@ -14,12 +14,12 @@ import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.utils.HtmlFormatter
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 kotlin.coroutines.coroutineContext
/**
* 获取正文
@ -28,7 +28,6 @@ object BookContent {
@Throws(Exception::class)
suspend fun analyzeContent(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
bookChapter: BookChapter,
@ -55,7 +54,7 @@ object BookContent {
val analyzeRule = AnalyzeRule(book, bookSource).setContent(body, baseUrl)
analyzeRule.setRedirectUrl(redirectUrl)
analyzeRule.nextChapterUrl = mNextChapterUrl
scope.ensureActive()
coroutineContext.ensureActive()
var contentData = analyzeContent(
book, baseUrl, redirectUrl, body, contentRule, bookChapter, bookSource, mNextChapterUrl
)
@ -68,7 +67,7 @@ object BookContent {
== NetworkUtils.getAbsoluteURL(redirectUrl, mNextChapterUrl)
) break
nextUrlList.add(nextUrl)
scope.ensureActive()
coroutineContext.ensureActive()
val res = AnalyzeUrl(
mUrl = nextUrl,
source = bookSource,
@ -106,7 +105,7 @@ object BookContent {
}
}
asyncArray.forEach { coroutine ->
scope.ensureActive()
coroutineContext.ensureActive()
content.append("\n").append(coroutine.await())
}
}
@ -124,7 +123,7 @@ object BookContent {
throw ContentEmptyException("内容为空")
}
if (needSave) {
BookHelp.saveContent(scope, bookSource, book, bookChapter, contentStr)
BookHelp.saveContent(bookSource, book, bookChapter, contentStr)
}
return contentStr
}

@ -13,9 +13,9 @@ import io.legado.app.utils.DebugLog
import io.legado.app.utils.HtmlFormatter
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 kotlin.coroutines.coroutineContext
/**
@ -24,8 +24,7 @@ import splitties.init.appCtx
object BookInfo {
@Throws(Exception::class)
fun analyzeBookInfo(
scope: CoroutineScope,
suspend fun analyzeBookInfo(
bookSource: BookSource,
book: Book,
baseUrl: String,
@ -41,11 +40,10 @@ object BookInfo {
val analyzeRule = AnalyzeRule(book, bookSource)
analyzeRule.setContent(body).setBaseUrl(baseUrl)
analyzeRule.setRedirectUrl(redirectUrl)
analyzeBookInfo(scope, book, body, analyzeRule, bookSource, baseUrl, redirectUrl, canReName)
analyzeBookInfo(book, body, analyzeRule, bookSource, baseUrl, redirectUrl, canReName)
}
fun analyzeBookInfo(
scope: CoroutineScope,
suspend fun analyzeBookInfo(
book: Book,
body: String,
analyzeRule: AnalyzeRule,
@ -57,13 +55,13 @@ object BookInfo {
val infoRule = bookSource.getBookInfoRule()
infoRule.init?.let {
if (it.isNotBlank()) {
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "≡执行详情页初始化规则")
analyzeRule.setContent(analyzeRule.getElement(it))
}
}
val mCanReName = canReName && !infoRule.canReName.isNullOrBlank()
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取书名")
BookHelp.formatBookName(analyzeRule.getString(infoRule.name)).let {
if (it.isNotEmpty() && (mCanReName || book.name.isEmpty())) {
@ -71,7 +69,7 @@ object BookInfo {
}
Debug.log(bookSource.bookSourceUrl, "${it}")
}
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取作者")
BookHelp.formatBookAuthor(analyzeRule.getString(infoRule.author)).let {
if (it.isNotEmpty() && (mCanReName || book.author.isEmpty())) {
@ -79,7 +77,7 @@ object BookInfo {
}
Debug.log(bookSource.bookSourceUrl, "${it}")
}
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取分类")
try {
analyzeRule.getStringList(infoRule.kind)
@ -92,7 +90,7 @@ object BookInfo {
Debug.log(bookSource.bookSourceUrl, "${e.localizedMessage}")
DebugLog.e("获取分类出错", e)
}
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取字数")
try {
wordCountFormat(analyzeRule.getString(infoRule.wordCount)).let {
@ -103,7 +101,7 @@ object BookInfo {
Debug.log(bookSource.bookSourceUrl, "${e.localizedMessage}")
DebugLog.e("获取字数出错", e)
}
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取最新章节")
try {
analyzeRule.getString(infoRule.lastChapter).let {
@ -114,7 +112,7 @@ object BookInfo {
Debug.log(bookSource.bookSourceUrl, "${e.localizedMessage}")
DebugLog.e("获取最新章节出错", e)
}
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取简介")
try {
HtmlFormatter.format(analyzeRule.getString(infoRule.intro)).let {
@ -125,7 +123,7 @@ object BookInfo {
Debug.log(bookSource.bookSourceUrl, "${e.localizedMessage}")
DebugLog.e("获取简介出错", e)
}
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取封面链接")
try {
analyzeRule.getString(infoRule.coverUrl).let {
@ -140,7 +138,7 @@ object BookInfo {
DebugLog.e("获取封面出错", e)
}
if (book.type != BookType.file) {
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取目录链接")
book.tocUrl = analyzeRule.getString(infoRule.tocUrl, isUrl = true)
if (book.tocUrl.isEmpty()) book.tocUrl = baseUrl
@ -149,7 +147,7 @@ object BookInfo {
}
Debug.log(bookSource.bookSourceUrl, "${book.tocUrl}")
} else {
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取文件下载链接")
book.downloadUrls = analyzeRule.getStringList(infoRule.downloadUrls, isUrl = true)
if (book.downloadUrls == null) {

@ -14,9 +14,9 @@ import io.legado.app.model.analyzeRule.RuleData
import io.legado.app.utils.HtmlFormatter
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 kotlin.coroutines.coroutineContext
/**
* 获取书籍列表
@ -24,8 +24,7 @@ import splitties.init.appCtx
object BookList {
@Throws(Exception::class)
fun analyzeBookList(
scope: CoroutineScope,
suspend fun analyzeBookList(
bookSource: BookSource,
ruleData: RuleData,
analyzeUrl: AnalyzeUrl,
@ -46,11 +45,10 @@ object BookList {
analyzeRule.setContent(body).setBaseUrl(baseUrl)
analyzeRule.setRedirectUrl(baseUrl)
bookSource.bookUrlPattern?.let {
scope.ensureActive()
coroutineContext.ensureActive()
if (baseUrl.matches(it.toRegex())) {
Debug.log(bookSource.bookSourceUrl, "≡链接为详情页")
getInfoItem(
scope,
bookSource,
analyzeRule,
analyzeUrl,
@ -81,11 +79,11 @@ object BookList {
}
Debug.log(bookSource.bookSourceUrl, "┌获取书籍列表")
collections = analyzeRule.getElements(ruleList)
scope.ensureActive()
coroutineContext.ensureActive()
if (collections.isEmpty() && bookSource.bookUrlPattern.isNullOrEmpty()) {
Debug.log(bookSource.bookSourceUrl, "└列表为空,按详情页解析")
getInfoItem(
scope, bookSource, analyzeRule, analyzeUrl, body, baseUrl, ruleData.getVariable()
bookSource, analyzeRule, analyzeUrl, body, baseUrl, ruleData.getVariable()
)?.let { searchBook ->
searchBook.infoHtml = body
bookList.add(searchBook)
@ -102,7 +100,7 @@ object BookList {
Debug.log(bookSource.bookSourceUrl, "└列表大小:${collections.size}")
for ((index, item) in collections.withIndex()) {
getSearchItem(
scope, bookSource, analyzeRule, item, baseUrl, ruleData.getVariable(),
bookSource, analyzeRule, item, baseUrl, ruleData.getVariable(),
index == 0,
ruleName = ruleName,
ruleBookUrl = ruleBookUrl,
@ -127,8 +125,7 @@ object BookList {
}
@Throws(Exception::class)
private fun getInfoItem(
scope: CoroutineScope,
private suspend fun getInfoItem(
bookSource: BookSource,
analyzeRule: AnalyzeRule,
analyzeUrl: AnalyzeUrl,
@ -144,7 +141,6 @@ object BookList {
book.type = bookSource.bookSourceType
analyzeRule.ruleData = book
BookInfo.analyzeBookInfo(
scope,
book,
body,
analyzeRule,
@ -160,8 +156,7 @@ object BookList {
}
@Throws(Exception::class)
private fun getSearchItem(
scope: CoroutineScope,
private suspend fun getSearchItem(
bookSource: BookSource,
analyzeRule: AnalyzeRule,
item: Any,
@ -184,16 +179,16 @@ object BookList {
searchBook.originOrder = bookSource.customOrder
analyzeRule.ruleData = searchBook
analyzeRule.setContent(item)
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取书名", log)
searchBook.name = BookHelp.formatBookName(analyzeRule.getString(ruleName))
Debug.log(bookSource.bookSourceUrl, "${searchBook.name}", log)
if (searchBook.name.isNotEmpty()) {
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取作者", log)
searchBook.author = BookHelp.formatBookAuthor(analyzeRule.getString(ruleAuthor))
Debug.log(bookSource.bookSourceUrl, "${searchBook.author}", log)
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取分类", log)
try {
searchBook.kind = analyzeRule.getStringList(ruleKind)?.joinToString(",")
@ -201,7 +196,7 @@ object BookList {
} catch (e: Exception) {
Debug.log(bookSource.bookSourceUrl, "${e.localizedMessage}", log)
}
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取字数", log)
try {
searchBook.wordCount = wordCountFormat(analyzeRule.getString(ruleWordCount))
@ -209,7 +204,7 @@ object BookList {
} catch (e: java.lang.Exception) {
Debug.log(bookSource.bookSourceUrl, "${e.localizedMessage}", log)
}
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取最新章节", log)
try {
searchBook.latestChapterTitle = analyzeRule.getString(ruleLastChapter)
@ -217,7 +212,7 @@ object BookList {
} catch (e: java.lang.Exception) {
Debug.log(bookSource.bookSourceUrl, "${e.localizedMessage}", log)
}
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取简介", log)
try {
searchBook.intro = HtmlFormatter.format(analyzeRule.getString(ruleIntro))
@ -225,7 +220,7 @@ object BookList {
} catch (e: java.lang.Exception) {
Debug.log(bookSource.bookSourceUrl, "${e.localizedMessage}", log)
}
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取封面链接", log)
try {
analyzeRule.getString(ruleCoverUrl).let {
@ -237,7 +232,7 @@ object BookList {
} catch (e: java.lang.Exception) {
Debug.log(bookSource.bookSourceUrl, "${e.localizedMessage}", log)
}
scope.ensureActive()
coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取详情页链接", log)
searchBook.bookUrl = analyzeRule.getString(ruleBookUrl, isUrl = true)
if (searchBook.bookUrl.isEmpty()) {

@ -29,12 +29,11 @@ object WebBook {
context: CoroutineContext = Dispatchers.IO,
): Coroutine<ArrayList<SearchBook>> {
return Coroutine.async(scope, context) {
searchBookAwait(scope, bookSource, key, page)
searchBookAwait(bookSource, key, page)
}
}
suspend fun searchBookAwait(
scope: CoroutineScope,
bookSource: BookSource,
key: String,
page: Int? = 1,
@ -58,7 +57,6 @@ object WebBook {
}
}
return BookList.analyzeBookList(
scope = scope,
bookSource = bookSource,
ruleData = ruleData,
analyzeUrl = analyzeUrl,
@ -81,12 +79,11 @@ object WebBook {
context: CoroutineContext = Dispatchers.IO,
): Coroutine<List<SearchBook>> {
return Coroutine.async(scope, context) {
exploreBookAwait(scope, bookSource, url, page)
exploreBookAwait(bookSource, url, page)
}
}
suspend fun exploreBookAwait(
scope: CoroutineScope,
bookSource: BookSource,
url: String,
page: Int? = 1,
@ -108,7 +105,6 @@ object WebBook {
}
}
return BookList.analyzeBookList(
scope = scope,
bookSource = bookSource,
ruleData = ruleData,
analyzeUrl = analyzeUrl,
@ -129,12 +125,11 @@ object WebBook {
canReName: Boolean = true,
): Coroutine<Book> {
return Coroutine.async(scope, context) {
getBookInfoAwait(scope, bookSource, book, canReName)
getBookInfoAwait(bookSource, book, canReName)
}
}
suspend fun getBookInfoAwait(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
canReName: Boolean = true,
@ -142,7 +137,6 @@ object WebBook {
book.type = bookSource.bookSourceType
if (!book.infoHtml.isNullOrEmpty()) {
BookInfo.analyzeBookInfo(
scope = scope,
bookSource = bookSource,
book = book,
baseUrl = book.bookUrl,
@ -166,7 +160,6 @@ object WebBook {
}
}
BookInfo.analyzeBookInfo(
scope = scope,
bookSource = bookSource,
book = book,
baseUrl = book.bookUrl,
@ -188,12 +181,11 @@ object WebBook {
context: CoroutineContext = Dispatchers.IO
): Coroutine<List<BookChapter>> {
return Coroutine.async(scope, context) {
getChapterListAwait(scope, bookSource, book).getOrThrow()
getChapterListAwait(bookSource, book).getOrThrow()
}
}
suspend fun getChapterListAwait(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
): Result<List<BookChapter>> {
@ -201,7 +193,6 @@ object WebBook {
return kotlin.runCatching {
if (book.bookUrl == book.tocUrl && !book.tocHtml.isNullOrEmpty()) {
BookChapterList.analyzeChapterList(
scope = scope,
bookSource = bookSource,
book = book,
baseUrl = book.tocUrl,
@ -224,7 +215,6 @@ object WebBook {
}
}
BookChapterList.analyzeChapterList(
scope = scope,
bookSource = bookSource,
book = book,
baseUrl = book.tocUrl,
@ -270,7 +260,6 @@ object WebBook {
}
return if (bookChapter.url == book.bookUrl && !book.tocHtml.isNullOrEmpty()) {
BookContent.analyzeContent(
scope = scope,
bookSource = bookSource,
book = book,
bookChapter = bookChapter,
@ -300,7 +289,6 @@ object WebBook {
}
}
BookContent.analyzeContent(
scope = scope,
bookSource = bookSource,
book = book,
bookChapter = bookChapter,
@ -342,13 +330,13 @@ object WebBook {
): Result<Book> {
return kotlin.runCatching {
scope.isActive
searchBookAwait(scope, bookSource, name).firstOrNull {
searchBookAwait(bookSource, name).firstOrNull {
it.name == name && it.author == author
}?.let { searchBook ->
scope.isActive
var book = searchBook.toBook()
if (book.tocUrl.isBlank()) {
book = getBookInfoAwait(scope, bookSource, book)
book = getBookInfoAwait(bookSource, book)
}
return@runCatching book
}

@ -24,8 +24,11 @@ import io.legado.app.utils.activityPendingIntent
import io.legado.app.utils.postEvent
import io.legado.app.utils.servicePendingIntent
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.launch
import org.mozilla.javascript.WrappedException
import java.util.concurrent.Executors
import kotlin.math.min
@ -139,7 +142,7 @@ class CheckSourceService : BaseService() {
if (CheckSource.checkSearch) {
if (!source.searchUrl.isNullOrBlank()) {
source.removeGroup("搜索链接规则为空")
val searchBooks = WebBook.searchBookAwait(this, source, searchWord)
val searchBooks = WebBook.searchBookAwait(source, searchWord)
if (searchBooks.isEmpty()) {
source.addGroup("搜索失效")
} else {
@ -164,7 +167,7 @@ class CheckSourceService : BaseService() {
source.addGroup("发现规则为空")
} else {
source.removeGroup("发现规则为空")
val exploreBooks = WebBook.exploreBookAwait(this, source, url)
val exploreBooks = WebBook.exploreBookAwait(source, url)
if (exploreBooks.isEmpty()) {
source.addGroup("发现失效")
} else {
@ -205,13 +208,13 @@ class CheckSourceService : BaseService() {
//校验详情
if (CheckSource.checkInfo) {
if (mBook.tocUrl.isBlank()) {
mBook = WebBook.getBookInfoAwait(this, source, mBook)
mBook = WebBook.getBookInfoAwait(source, mBook)
}
//校验目录
if (CheckSource.checkCategory &&
source.bookSourceType != BookType.file
) {
val toc = WebBook.getChapterListAwait(this, source, mBook).getOrThrow()
val toc = WebBook.getChapterListAwait(source, mBook).getOrThrow()
val nextChapterUrl = toc.getOrNull(1)?.url ?: toc.first().url
//校验正文
if (CheckSource.checkContent) {

@ -23,7 +23,6 @@ import io.legado.app.help.BookHelp
import io.legado.app.help.ContentProcessor
import io.legado.app.help.config.AppConfig
import io.legado.app.utils.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.sync.Mutex
@ -37,6 +36,7 @@ import java.io.File
import java.io.FileOutputStream
import java.nio.charset.Charset
import java.util.concurrent.ConcurrentHashMap
import kotlin.coroutines.coroutineContext
class CacheViewModel(application: Application) : BaseViewModel(application) {
@ -79,9 +79,9 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
val uri = Uri.parse(path)
val doc = DocumentFile.fromTreeUri(context, uri)
?: throw NoStackTraceException("获取导出文档失败")
export(this, doc, book)
export(doc, book)
} else {
export(this, FileUtils.createFolderIfNotExist(path), book)
export(FileUtils.createFolderIfNotExist(path), book)
}
}.onError {
exportProgress.remove(book.bookUrl)
@ -98,14 +98,14 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
}
@Suppress("BlockingMethodInNonBlockingContext")
private suspend fun export(scope: CoroutineScope, doc: DocumentFile, book: Book) {
private suspend fun export(doc: DocumentFile, book: Book) {
val filename = "${getExportFileName(book)}.txt"
DocumentUtils.delete(doc, filename)
val bookDoc = DocumentUtils.createFileIfNotExist(doc, filename)
?: throw NoStackTraceException("创建文档失败,请尝试重新设置导出文件夹")
val stringBuilder = StringBuilder()
context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs ->
getAllContents(scope, book) { text, srcList ->
getAllContents(book) { text, srcList ->
bookOs.write(text.toByteArray(Charset.forName(AppConfig.exportCharset)))
stringBuilder.append(text)
srcList?.forEach {
@ -128,12 +128,12 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
}
}
private suspend fun export(scope: CoroutineScope, file: File, book: Book) {
private suspend fun export(file: File, book: Book) {
val filename = "${getExportFileName(book)}.txt"
val bookPath = FileUtils.getPath(file, filename)
val bookFile = FileUtils.createFileWithReplace(bookPath)
val stringBuilder = StringBuilder()
getAllContents(scope, book) { text, srcList ->
getAllContents(book) { text, srcList ->
bookFile.appendText(text, Charset.forName(AppConfig.exportCharset))
stringBuilder.append(text)
srcList?.forEach {
@ -157,7 +157,6 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
}
private suspend fun getAllContents(
scope: CoroutineScope,
book: Book,
append: (text: String, srcList: ArrayList<Triple<String, Int, String>>?) -> Unit
) {
@ -173,7 +172,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
}"
append(qy, null)
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter ->
scope.ensureActive()
coroutineContext.ensureActive()
upAdapterLiveData.postValue(book.bookUrl)
exportProgress[book.bookUrl] = index
BookHelp.getContent(book, chapter).let { content ->
@ -227,9 +226,9 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
val uri = Uri.parse(path)
val doc = DocumentFile.fromTreeUri(context, uri)
?: throw NoStackTraceException("获取导出文档失败")
exportEpub(this, doc, book)
exportEpub(doc, book)
} else {
exportEpub(this, FileUtils.createFolderIfNotExist(path), book)
exportEpub(FileUtils.createFolderIfNotExist(path), book)
}
}.onError {
exportProgress.remove(book.bookUrl)
@ -246,7 +245,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
}
@Suppress("BlockingMethodInNonBlockingContext")
private suspend fun exportEpub(scope: CoroutineScope, doc: DocumentFile, book: Book) {
private suspend fun exportEpub(doc: DocumentFile, book: Book) {
val filename = "${getExportFileName(book)}.epub"
DocumentUtils.delete(doc, filename)
val epubBook = EpubBook()
@ -259,7 +258,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
val contentModel = setAssets(doc, book, epubBook)
//设置正文
setEpubContent(scope, contentModel, book, epubBook)
setEpubContent(contentModel, book, epubBook)
DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc ->
context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs ->
EpubWriter().write(epubBook, bookOs)
@ -269,7 +268,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
}
private suspend fun exportEpub(scope: CoroutineScope, file: File, book: Book) {
private suspend fun exportEpub(file: File, book: Book) {
val filename = "${getExportFileName(book)}.epub"
val epubBook = EpubBook()
epubBook.version = "2.0"
@ -283,7 +282,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
val bookPath = FileUtils.getPath(file, filename)
val bookFile = FileUtils.createFileWithReplace(bookPath)
//设置正文
setEpubContent(scope, contentModel, book, epubBook)
setEpubContent(contentModel, book, epubBook)
@Suppress("BlockingMethodInNonBlockingContext")
EpubWriter().write(epubBook, FileOutputStream(bookFile))
}
@ -438,7 +437,6 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
}
private suspend fun setEpubContent(
scope: CoroutineScope,
contentModel: String,
book: Book,
epubBook: EpubBook
@ -447,7 +445,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
val useReplace = AppConfig.exportUseReplace && book.getUseReplaceRule()
val contentProcessor = ContentProcessor.get(book.name, book.origin)
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter ->
scope.ensureActive()
coroutineContext.ensureActive()
upAdapterLiveData.postValue(book.bookUrl)
exportProgress[book.bookUrl] = index
BookHelp.getContent(book, chapter).let { content ->

@ -21,7 +21,6 @@ import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
@ -155,7 +154,7 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
}
val source = bookSourceList[searchIndex]
val task = Coroutine.async(scope = viewModelScope, context = searchPool!!) {
val resultBooks = WebBook.searchBookAwait(this, source, name)
val resultBooks = WebBook.searchBookAwait(source, name)
resultBooks.forEach { searchBook ->
if (searchBook.name == name) {
if ((AppConfig.changeSourceCheckAuthor && searchBook.author.contains(author))
@ -163,7 +162,7 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
) {
if (searchBook.latestChapterTitle.isNullOrEmpty()) {
if (AppConfig.changeSourceLoadInfo || AppConfig.changeSourceLoadToc) {
loadBookInfo(this, source, searchBook.toBook())
loadBookInfo(source, searchBook.toBook())
} else {
searchCallback?.searchSuccess(searchBook)
}
@ -183,10 +182,10 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
tasks.add(task)
}
private suspend fun loadBookInfo(scope: CoroutineScope, source: BookSource, book: Book) {
WebBook.getBookInfoAwait(scope, source, book)
private suspend fun loadBookInfo(source: BookSource, book: Book) {
WebBook.getBookInfoAwait(source, book)
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
loadBookToc(scope, source, book)
loadBookToc(source, book)
} else {
//从详情页里获取最新章节
val searchBook = book.toSearchBook()
@ -194,8 +193,8 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
}
}
private suspend fun loadBookToc(scope: CoroutineScope, source: BookSource, book: Book) {
val chapters = WebBook.getChapterListAwait(scope, source, book).getOrThrow()
private suspend fun loadBookToc(source: BookSource, book: Book) {
val chapters = WebBook.getChapterListAwait(source, book).getOrThrow()
tocMap[book.bookUrl] = chapters
book.latestChapterTitle = chapters.last().title
val searchBook: SearchBook = book.toSearchBook()
@ -298,9 +297,9 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
val source = appDb.bookSourceDao.getBookSource(book.origin)
?: throw NoStackTraceException("书源不存在")
if (book.tocUrl.isEmpty()) {
WebBook.getBookInfoAwait(this, source, book)
WebBook.getBookInfoAwait(source, book)
}
val toc = WebBook.getChapterListAwait(this, source, book).getOrThrow()
val toc = WebBook.getChapterListAwait(source, book).getOrThrow()
Pair(toc, source)
}
}

@ -51,7 +51,7 @@ class BookshelfManageViewModel(application: Application) : BaseViewModel(applica
.onFailure {
context.toastOnUi("获取书籍出错\n${it.localizedMessage}")
}.getOrNull()?.let { newBook ->
WebBook.getChapterListAwait(this, source, newBook)
WebBook.getChapterListAwait(source, newBook)
.onFailure {
context.toastOnUi("获取目录出错\n${it.localizedMessage}")
}.getOrNull()?.let { toc ->

@ -245,9 +245,9 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
sources.forEach { source ->
WebBook.preciseSearchAwait(this, source, name, author).getOrNull()?.let { book ->
if (book.tocUrl.isEmpty()) {
WebBook.getBookInfoAwait(this, source, book)
WebBook.getBookInfoAwait(source, book)
}
val toc = WebBook.getChapterListAwait(this, source, book).getOrThrow()
val toc = WebBook.getChapterListAwait(source, book).getOrThrow()
changeTo(source, book, toc)
return@execute
}

@ -160,7 +160,7 @@ class SearchContentActivity :
binding.fbStop.visible()
val searchResults = withContext(IO) {
if (isLocalBook || viewModel.cacheChapterNames.contains(bookChapter.getFileName())) {
viewModel.searchChapter(this, query, bookChapter)
viewModel.searchChapter(query, bookChapter)
} else {
null
}

@ -10,10 +10,10 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
import io.legado.app.help.ContentProcessor
import io.legado.app.help.config.AppConfig
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext
import kotlin.coroutines.coroutineContext
class SearchContentViewModel(application: Application) : BaseViewModel(application) {
var bookUrl: String = ""
@ -38,7 +38,6 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
}
suspend fun searchChapter(
scope: CoroutineScope,
query: String,
chapter: BookChapter?
): List<SearchResult> {
@ -46,7 +45,7 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
if (chapter != null) {
book?.let { book ->
val chapterContent = BookHelp.getContent(book, chapter)
scope.ensureActive()
coroutineContext.ensureActive()
if (chapterContent != null) {
//先搜索没有启用净化的正文
withContext(Dispatchers.IO) {
@ -55,7 +54,7 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
2 -> ChineseUtils.s2t(chapter.title)
else -> chapter.title
}
scope.ensureActive()
kotlin.coroutines.coroutineContext.ensureActive()
mContent = contentProcessor!!.getContent(
book, chapter, chapterContent,
chineseConvert = true,
@ -63,9 +62,9 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
useReplace = false
).joinToString("")
}
val positions = searchPosition(scope, query)
val positions = searchPosition(query)
positions.forEachIndexed { index, position ->
scope.ensureActive()
coroutineContext.ensureActive()
val construct = getResultAndQueryIndex(mContent, position, query)
val result = SearchResult(
resultCountWithinChapter = index,
@ -85,7 +84,7 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
return searchResultsWithinChapter
}
private suspend fun searchPosition(scope: CoroutineScope, pattern: String): List<Int> {
private suspend fun searchPosition(pattern: String): List<Int> {
val position: MutableList<Int> = mutableListOf()
var index = mContent.indexOf(pattern)
if (index >= 0) {
@ -95,7 +94,7 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
index = mContent.indexOf(pattern)
}
while (index >= 0) {
scope.ensureActive()
coroutineContext.ensureActive()
position.add(index)
index = mContent.indexOf(pattern, index + pattern.length)
}

@ -123,9 +123,9 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
AnalyzeRule(book, source).evalJS(preUpdateJs)
}
if (book.tocUrl.isBlank()) {
WebBook.getBookInfoAwait(this, source, book)
WebBook.getBookInfoAwait(source, book)
}
val toc = WebBook.getChapterListAwait(this, source, book).getOrThrow()
val toc = WebBook.getChapterListAwait(source, book).getOrThrow()
if (book.bookUrl == bookUrl) {
appDb.bookDao.update(book)
} else {

Loading…
Cancel
Save