|
@@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope
|
|
|
import io.legado.app.base.BaseViewModel
|
|
import io.legado.app.base.BaseViewModel
|
|
|
import io.legado.app.constant.AppConst
|
|
import io.legado.app.constant.AppConst
|
|
|
import io.legado.app.constant.AppPattern
|
|
import io.legado.app.constant.AppPattern
|
|
|
|
|
+import io.legado.app.constant.EventBus
|
|
|
import io.legado.app.constant.PreferKey
|
|
import io.legado.app.constant.PreferKey
|
|
|
import io.legado.app.data.appDb
|
|
import io.legado.app.data.appDb
|
|
|
import io.legado.app.data.entities.Book
|
|
import io.legado.app.data.entities.Book
|
|
@@ -19,17 +20,17 @@ import io.legado.app.help.config.AppConfig
|
|
|
import io.legado.app.help.config.SourceConfig
|
|
import io.legado.app.help.config.SourceConfig
|
|
|
import io.legado.app.help.coroutine.CompositeCoroutine
|
|
import io.legado.app.help.coroutine.CompositeCoroutine
|
|
|
import io.legado.app.help.coroutine.Coroutine
|
|
import io.legado.app.help.coroutine.Coroutine
|
|
|
|
|
+import io.legado.app.model.ReadBook
|
|
|
import io.legado.app.model.webBook.WebBook
|
|
import io.legado.app.model.webBook.WebBook
|
|
|
import io.legado.app.utils.getPrefBoolean
|
|
import io.legado.app.utils.getPrefBoolean
|
|
|
|
|
+import io.legado.app.utils.postEvent
|
|
|
import io.legado.app.utils.toastOnUi
|
|
import io.legado.app.utils.toastOnUi
|
|
|
|
|
+import kotlinx.coroutines.*
|
|
|
import kotlinx.coroutines.Dispatchers.IO
|
|
import kotlinx.coroutines.Dispatchers.IO
|
|
|
-import kotlinx.coroutines.ExecutorCoroutineDispatcher
|
|
|
|
|
-import kotlinx.coroutines.asCoroutineDispatcher
|
|
|
|
|
import kotlinx.coroutines.channels.awaitClose
|
|
import kotlinx.coroutines.channels.awaitClose
|
|
|
import kotlinx.coroutines.flow.callbackFlow
|
|
import kotlinx.coroutines.flow.callbackFlow
|
|
|
import kotlinx.coroutines.flow.flowOn
|
|
import kotlinx.coroutines.flow.flowOn
|
|
|
import kotlinx.coroutines.flow.map
|
|
import kotlinx.coroutines.flow.map
|
|
|
-import kotlinx.coroutines.withContext
|
|
|
|
|
import java.util.*
|
|
import java.util.*
|
|
|
import java.util.concurrent.ConcurrentHashMap
|
|
import java.util.concurrent.ConcurrentHashMap
|
|
|
import java.util.concurrent.Executors
|
|
import java.util.concurrent.Executors
|
|
@@ -146,6 +147,7 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
|
|
|
stopSearch()
|
|
stopSearch()
|
|
|
appDb.searchBookDao.clear(name, author)
|
|
appDb.searchBookDao.clear(name, author)
|
|
|
searchBooks.clear()
|
|
searchBooks.clear()
|
|
|
|
|
+ searchCallback?.upAdapter()
|
|
|
bookSourceList.clear()
|
|
bookSourceList.clear()
|
|
|
val searchGroup = AppConfig.searchGroup
|
|
val searchGroup = AppConfig.searchGroup
|
|
|
if (searchGroup.isBlank()) {
|
|
if (searchGroup.isBlank()) {
|
|
@@ -182,14 +184,17 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
|
|
|
if ((AppConfig.changeSourceCheckAuthor && searchBook.author.contains(author))
|
|
if ((AppConfig.changeSourceCheckAuthor && searchBook.author.contains(author))
|
|
|
|| !AppConfig.changeSourceCheckAuthor
|
|
|| !AppConfig.changeSourceCheckAuthor
|
|
|
) {
|
|
) {
|
|
|
- if (searchBook.latestChapterTitle.isNullOrEmpty()) {
|
|
|
|
|
- if (AppConfig.changeSourceLoadInfo || AppConfig.changeSourceLoadToc) {
|
|
|
|
|
|
|
+ when {
|
|
|
|
|
+ searchBook.latestChapterTitle.isNullOrEmpty() &&
|
|
|
|
|
+ (AppConfig.changeSourceLoadInfo || AppConfig.changeSourceLoadToc) -> {
|
|
|
loadBookInfo(source, searchBook.toBook())
|
|
loadBookInfo(source, searchBook.toBook())
|
|
|
- } else {
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ searchBook.chapterWordCountText.isNullOrBlank() && AppConfig.changeSourceLoadWordCount -> {
|
|
|
|
|
+ loadBookToc(source, searchBook.toBook())
|
|
|
|
|
+ }
|
|
|
|
|
+ else -> {
|
|
|
searchCallback?.searchSuccess(searchBook)
|
|
searchCallback?.searchSuccess(searchBook)
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- searchCallback?.searchSuccess(searchBook)
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -219,7 +224,37 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
|
|
|
val chapters = WebBook.getChapterListAwait(source, book).getOrThrow()
|
|
val chapters = WebBook.getChapterListAwait(source, book).getOrThrow()
|
|
|
tocMap[book.bookUrl] = chapters
|
|
tocMap[book.bookUrl] = chapters
|
|
|
bookMap[book.bookUrl] = book
|
|
bookMap[book.bookUrl] = book
|
|
|
- val searchBook: SearchBook = book.toSearchBook()
|
|
|
|
|
|
|
+ if (context.getPrefBoolean(PreferKey.changeSourceLoadWordCount)) {
|
|
|
|
|
+ loadBookWordCount(source, book, chapters)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ val searchBook = book.toSearchBook()
|
|
|
|
|
+ searchCallback?.searchSuccess(searchBook)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private suspend fun loadBookWordCount(
|
|
|
|
|
+ source: BookSource,
|
|
|
|
|
+ book: Book,
|
|
|
|
|
+ chapters: List<BookChapter>
|
|
|
|
|
+ ) = coroutineScope {
|
|
|
|
|
+ val chapterIndex = ReadBook.curTextChapter?.chapter?.index ?: (chapters.size - 1)
|
|
|
|
|
+ val bookChapter = chapters.getOrNull(chapterIndex)
|
|
|
|
|
+ val startTime = System.currentTimeMillis()
|
|
|
|
|
+ val pair = try {
|
|
|
|
|
+ if (bookChapter == null) throw NoStackTraceException("章节缺失,总章节数${chapters.size}")
|
|
|
|
|
+ if (!isActive) return@coroutineScope
|
|
|
|
|
+ WebBook.getContentAwait(source, book, bookChapter, null, false).length.let {
|
|
|
|
|
+ it to "第${chapterIndex + 1}章 字数:${it}"
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (t: Throwable) {
|
|
|
|
|
+ -1 to "第${chapterIndex + 1}章 获取字数失败:${t.localizedMessage}"
|
|
|
|
|
+ }
|
|
|
|
|
+ val endTime = System.currentTimeMillis()
|
|
|
|
|
+ val searchBook = book.toSearchBook().apply {
|
|
|
|
|
+ chapterWordCountText = pair.second
|
|
|
|
|
+ chapterWordCount = pair.first
|
|
|
|
|
+ respondTime = (endTime - startTime).toInt()
|
|
|
|
|
+ }
|
|
|
searchCallback?.searchSuccess(searchBook)
|
|
searchCallback?.searchSuccess(searchBook)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -240,15 +275,24 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ fun onLoadWordCountChecked(isChecked: Boolean) {
|
|
|
|
|
+ postEvent(EventBus.SOURCE_CHANGED, "")
|
|
|
|
|
+ if (isChecked) {
|
|
|
|
|
+ startRefreshList(searchBooks.filter { it.chapterWordCountText == null })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 刷新列表
|
|
* 刷新列表
|
|
|
*/
|
|
*/
|
|
|
- fun startRefreshList() {
|
|
|
|
|
|
|
+ fun startRefreshList(refreshList: List<SearchBook> = searchBooks) {
|
|
|
execute {
|
|
execute {
|
|
|
|
|
+ if (refreshList.isEmpty()) return@execute
|
|
|
stopSearch()
|
|
stopSearch()
|
|
|
searchBookList.clear()
|
|
searchBookList.clear()
|
|
|
- searchBookList.addAll(searchBooks)
|
|
|
|
|
- searchBooks.clear()
|
|
|
|
|
|
|
+ searchBookList.addAll(refreshList)
|
|
|
|
|
+ searchBooks.removeAll(refreshList)
|
|
|
|
|
+ searchCallback?.upAdapter()
|
|
|
searchStateData.postValue(true)
|
|
searchStateData.postValue(true)
|
|
|
initSearchPool()
|
|
initSearchPool()
|
|
|
for (i in 0 until threadCount) {
|
|
for (i in 0 until threadCount) {
|