This commit is contained in:
Horis 2024-02-08 23:13:56 +08:00
parent 1111e36c41
commit 4551281e6f

View File

@ -25,14 +25,19 @@ import io.legado.app.model.CacheBook
import io.legado.app.model.ReadBook import io.legado.app.model.ReadBook
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.CacheBookService import io.legado.app.service.CacheBookService
import io.legado.app.utils.onEachParallel
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.LinkedList import java.util.LinkedList
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.min import kotlin.math.min
@ -41,7 +46,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
private var poolSize = min(threadCount, AppConst.MAX_THREAD) private var poolSize = min(threadCount, AppConst.MAX_THREAD)
private var upTocPool = Executors.newFixedThreadPool(poolSize).asCoroutineDispatcher() private var upTocPool = Executors.newFixedThreadPool(poolSize).asCoroutineDispatcher()
private val waitUpTocBooks = LinkedList<String>() private val waitUpTocBooks = LinkedList<String>()
private val onUpTocBooks = ConcurrentHashMap.newKeySet<String>() private val onUpTocBooks = hashSetOf<String>()
val onUpBooksLiveData = MutableLiveData<Int>() val onUpBooksLiveData = MutableLiveData<Int>()
private var upTocJob: Job? = null private var upTocJob: Job? = null
private var cacheBookJob: Job? = null private var cacheBookJob: Job? = null
@ -101,86 +106,70 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
upPool() upPool()
postUpBooksLiveData() postUpBooksLiveData()
upTocJob = viewModelScope.launch(upTocPool) { upTocJob = viewModelScope.launch(upTocPool) {
while (isActive) { flow {
when { while (true) {
waitUpTocBooks.isEmpty() -> { emit(waitUpTocBooks.poll() ?: break)
upTocJob?.cancel()
upTocJob = null
}
onUpTocBooks.size < threadCount -> {
updateToc()
}
else -> {
delay(500)
}
} }
} }.onEach {
onUpTocBooks.add(it)
postEvent(EventBus.UP_BOOKSHELF, it)
}.onEachParallel(threadCount) {
updateToc(it)
}.onEach {
onUpTocBooks.remove(it)
postEvent(EventBus.UP_BOOKSHELF, it)
postUpBooksLiveData()
}.onCompletion {
upTocJob = null
if (it == null && cacheBookJob == null && !CacheBookService.isRun) {
//所有目录更新完再开始缓存章节
cacheBook()
}
}.catch {
AppLog.put("更新目录出错\n${it.localizedMessage}", it)
}.collect()
} }
} }
@Synchronized private suspend fun updateToc(bookUrl: String) {
private fun updateToc() { val book = appDb.bookDao.getBook(bookUrl) ?: return
val bookUrl = waitUpTocBooks.poll() ?: return
if (onUpTocBooks.contains(bookUrl)) {
postUpBooksLiveData()
return
}
val book = appDb.bookDao.getBook(bookUrl)
if (book == null) {
postUpBooksLiveData()
return
}
val source = appDb.bookSourceDao.getBookSource(book.origin) val source = appDb.bookSourceDao.getBookSource(book.origin)
if (source == null) { if (source == null) {
if (!book.isUpError) { if (!book.isUpError) {
book.addType(BookType.updateError) book.addType(BookType.updateError)
appDb.bookDao.update(book) appDb.bookDao.update(book)
} }
postUpBooksLiveData()
return return
} }
upTocAdd(bookUrl) kotlin.runCatching {
execute(context = upTocPool, executeContext = upTocPool) { val oldBook = book.copy()
kotlin.runCatching { WebBook.runPreUpdateJs(source, book)
val oldBook = book.copy() if (book.tocUrl.isBlank()) {
WebBook.runPreUpdateJs(source, book) WebBook.getBookInfoAwait(source, book)
if (book.tocUrl.isBlank()) { }
WebBook.getBookInfoAwait(source, book) val toc = WebBook.getChapterListAwait(source, book).getOrThrow()
} book.sync(oldBook)
val toc = WebBook.getChapterListAwait(source, book).getOrThrow() book.removeType(BookType.updateError)
book.sync(oldBook) if (book.bookUrl == bookUrl) {
book.removeType(BookType.updateError) appDb.bookDao.update(book)
if (book.bookUrl == bookUrl) { } else {
appDb.bookDao.update(book) appDb.bookDao.insert(book)
} else { BookHelp.updateCacheFolder(oldBook, book)
upTocAdd(book.bookUrl) }
appDb.bookDao.insert(book) appDb.bookChapterDao.delByBook(bookUrl)
BookHelp.updateCacheFolder(oldBook, book) appDb.bookChapterDao.insert(*toc.toTypedArray())
} if (book.isSameNameAuthor(ReadBook.book)) {
appDb.bookChapterDao.delByBook(bookUrl) ReadBook.book = book
appDb.bookChapterDao.insert(*toc.toTypedArray()) ReadBook.chapterSize = book.totalChapterNum
if (book.isSameNameAuthor(ReadBook.book)) { }
ReadBook.book = book addDownload(source, book)
ReadBook.chapterSize = book.totalChapterNum }.onFailure {
} AppLog.put("${book.name} 更新目录失败\n${it.localizedMessage}", it)
addDownload(source, book) //这里可能因为时间太长书籍信息已经更改,所以重新获取
}.onFailure { appDb.bookDao.getBook(book.bookUrl)?.let { book ->
AppLog.put("${book.name} 更新目录失败\n${it.localizedMessage}", it) book.addType(BookType.updateError)
//这里可能因为时间太长书籍信息已经更改,所以重新获取 appDb.bookDao.update(book)
appDb.bookDao.getBook(book.bookUrl)?.let { book ->
book.addType(BookType.updateError)
appDb.bookDao.update(book)
}
} }
}.onCancel {
upTocCancel(bookUrl)
upTocCancel(book.bookUrl)
}.onFinally {
upTocFinally(bookUrl)
upTocFinally(book.bookUrl)
postUpBooksLiveData()
} }
} }
@ -192,33 +181,6 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
} }
} }
@Synchronized
private fun upTocAdd(bookUrl: String) {
onUpTocBooks.add(bookUrl)
postEvent(EventBus.UP_BOOKSHELF, bookUrl)
}
@Synchronized
private fun upTocCancel(bookUrl: String) {
onUpTocBooks.remove(bookUrl)
waitUpTocBooks.add(bookUrl)
postEvent(EventBus.UP_BOOKSHELF, bookUrl)
}
@Synchronized
private fun upTocFinally(bookUrl: String) {
onUpTocBooks.remove(bookUrl)
postEvent(EventBus.UP_BOOKSHELF, bookUrl)
if (waitUpTocBooks.isEmpty()
&& onUpTocBooks.isEmpty()
&& cacheBookJob == null
&& !CacheBookService.isRun
) {
//所有目录更新完再开始缓存章节
cacheBook()
}
}
@Synchronized @Synchronized
private fun addDownload(source: BookSource, book: Book) { private fun addDownload(source: BookSource, book: Book) {
if (AppConfig.preDownloadNum == 0) return if (AppConfig.preDownloadNum == 0) return