diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index 4446d36eb..8a3a632d5 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -6,6 +6,8 @@ import io.legado.app.constant.AppPattern import io.legado.app.constant.BookType import io.legado.app.constant.PageAnim import io.legado.app.data.appDb +import io.legado.app.help.BookHelp +import io.legado.app.help.ContentProcessor import io.legado.app.help.config.AppConfig import io.legado.app.help.config.ReadBookConfig import io.legado.app.model.ReadBook @@ -271,7 +273,13 @@ data class Book( this.tocHtml = this@Book.tocHtml } - fun changeTo(newBook: Book) { + fun changeTo(newBook: Book, toc: List): Book { + newBook.durChapterIndex = BookHelp + .getDurChapter(durChapterIndex, durChapterTitle, toc, totalChapterNum) + newBook.durChapterTitle = toc[newBook.durChapterIndex].getDisplayTitle( + ContentProcessor.get(newBook.name, newBook.origin).getTitleReplaceRules() + ) + newBook.durChapterPos = durChapterPos newBook.group = group newBook.order = order newBook.customCoverUrl = customCoverUrl @@ -279,23 +287,11 @@ data class Book( newBook.customTag = customTag newBook.canUpdate = canUpdate newBook.readConfig = readConfig - delete(this) - appDb.bookDao.insert(newBook) - } - - fun upInfoFromOld(oldBook: Book?) { - oldBook?.let { - group = oldBook.group - durChapterIndex = oldBook.durChapterIndex - durChapterPos = oldBook.durChapterPos - durChapterTitle = oldBook.durChapterTitle - customCoverUrl = oldBook.customCoverUrl - customIntro = oldBook.customIntro - order = oldBook.order - if (coverUrl.isNullOrEmpty()) { - coverUrl = oldBook.getDisplayCover() - } + if (appDb.bookDao.has(bookUrl) == true) { + delete() + appDb.bookDao.insert(newBook) } + return newBook } fun createBookMark(): Bookmark { @@ -313,20 +309,19 @@ data class Book( } } + fun delete() { + if (ReadBook.book?.bookUrl == bookUrl) { + ReadBook.book = null + } + appDb.bookDao.delete(this) + } + companion object { const val hTag = 2L const val rubyTag = 4L const val imgStyleDefault = "DEFAULT" const val imgStyleFull = "FULL" const val imgStyleText = "TEXT" - - fun delete(book: Book?) { - book ?: return - if (ReadBook.book?.bookUrl == book.bookUrl) { - ReadBook.book = null - } - appDb.bookDao.delete(book) - } } @Parcelize diff --git a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt index 4120e45fa..04234cbd0 100644 --- a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt +++ b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt @@ -320,35 +320,38 @@ object WebBook { name: String, author: String, context: CoroutineContext = Dispatchers.IO, - ): Coroutine> { + ): Coroutine> { return Coroutine.async(scope, context) { - preciseSearchAwait(scope, name, author, *bookSources.toTypedArray()) - ?: throw NoStackTraceException("没有搜索到<$name>$author") + for (source in bookSources) { + val book = preciseSearchAwait(scope, source, name, author).getOrNull() + if (book != null) { + return@async Pair(book, source) + } + } + throw NoStackTraceException("没有搜索到<$name>$author") } } suspend fun preciseSearchAwait( scope: CoroutineScope, + bookSource: BookSource, name: String, author: String, - vararg bookSources: BookSource - ): Pair? { - bookSources.forEach { source -> - kotlin.runCatching { - if (!scope.isActive) return null - searchBookAwait(scope, source, name).firstOrNull { - it.name == name && it.author == author - }?.let { searchBook -> - if (!scope.isActive) return null - var book = searchBook.toBook() - if (book.tocUrl.isBlank()) { - book = getBookInfoAwait(scope, source, book) - } - return Pair(source, book) + ): Result { + return kotlin.runCatching { + if (!scope.isActive) return@runCatching null + searchBookAwait(scope, bookSource, name).firstOrNull { + it.name == name && it.author == author + }?.let { searchBook -> + if (!scope.isActive) return@runCatching null + var book = searchBook.toBook() + if (book.tocUrl.isBlank()) { + book = getBookInfoAwait(scope, bookSource, book) } + return@runCatching book } + return@runCatching null } - return null } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookViewModel.kt index 78523d72f..da6ef4862 100644 --- a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookViewModel.kt @@ -46,16 +46,12 @@ class ArrangeBookViewModel(application: Application) : BaseViewModel(application batchChangeSourcePosition.value = index + 1 if (book.isLocalBook()) return@forEachIndexed if (book.origin == source.bookSourceUrl) return@forEachIndexed - WebBook.preciseSearchAwait(this, book.name, book.author, source)?.let { - val newBook = it.second - newBook.upInfoFromOld(book) - book.changeTo(newBook) - if (newBook.tocUrl.isEmpty()) { - WebBook.getBookInfoAwait(this, source, newBook) + WebBook.preciseSearchAwait(this, source, book.name, book.author) + .getOrNull()?.let { newBook -> + val toc = WebBook.getChapterListAwait(this, source, newBook) + book.changeTo(newBook, toc) + appDb.bookChapterDao.insert(*toc.toTypedArray()) } - val toc = WebBook.getChapterListAwait(this, source, newBook) - appDb.bookChapterDao.insert(*toc.toTypedArray()) - } } }.onFinally { batchChangeSourceState.value = false diff --git a/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt b/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt index 7c2b23e03..70166c840 100644 --- a/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt @@ -16,6 +16,7 @@ import io.legado.app.constant.EventBus import io.legado.app.constant.Status import io.legado.app.constant.Theme 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.databinding.ActivityAudioPlayBinding import io.legado.app.lib.dialogs.alert @@ -198,8 +199,8 @@ class AudioPlayActivity : override val oldBook: Book? get() = AudioPlay.book - override fun changeTo(source: BookSource, book: Book) { - viewModel.changeTo(source, book) + override fun changeTo(source: BookSource, book: Book, toc: List) { + viewModel.changeTo(source, book, toc) } override fun finish() { diff --git a/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayViewModel.kt b/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayViewModel.kt index 2550c9feb..4ea1acad5 100644 --- a/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayViewModel.kt @@ -9,8 +9,6 @@ import io.legado.app.data.appDb 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.help.BookHelp -import io.legado.app.help.ContentProcessor import io.legado.app.model.AudioPlay import io.legado.app.model.webBook.WebBook import io.legado.app.utils.postEvent @@ -45,33 +43,23 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application) } } - private fun loadBookInfo( - book: Book, - changeDruChapterIndex: ((chapters: List) -> Unit)? = null - ) { + private fun loadBookInfo(book: Book) { execute { AudioPlay.bookSource?.let { WebBook.getBookInfo(this, it, book) .onSuccess { - loadChapterList(book, changeDruChapterIndex) + loadChapterList(book) } } } } - private fun loadChapterList( - book: Book, - changeDruChapterIndex: ((chapters: List) -> Unit)? = null - ) { + private fun loadChapterList(book: Book) { execute { AudioPlay.bookSource?.let { WebBook.getChapterList(this, it, book) .onSuccess(Dispatchers.IO) { cList -> - if (changeDruChapterIndex == null) { - appDb.bookChapterDao.insert(*cList.toTypedArray()) - } else { - changeDruChapterIndex(cList) - } + appDb.bookChapterDao.insert(*cList.toTypedArray()) AudioPlay.upDurChapter(book) }.onError { context.toastOnUi(R.string.error_load_toc) @@ -88,47 +76,17 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application) } } - fun changeTo(source: BookSource, book: Book) { + fun changeTo(source: BookSource, book: Book, toc: List) { execute { - var oldTocSize: Int = book.totalChapterNum - AudioPlay.book?.let { - oldTocSize = it.totalChapterNum - book.order = it.order - appDb.bookDao.delete(it) - } - appDb.bookDao.insert(book) - AudioPlay.book = book + AudioPlay.book = AudioPlay.book!!.changeTo(book, toc) AudioPlay.bookSource = source - if (book.tocUrl.isEmpty()) { - loadBookInfo(book) { upChangeDurChapterIndex(book, oldTocSize, it) } - } else { - loadChapterList(book) { upChangeDurChapterIndex(book, oldTocSize, it) } - } + appDb.bookChapterDao.insert(*toc.toTypedArray()) + AudioPlay.upDurChapter(book) }.onFinally { postEvent(EventBus.SOURCE_CHANGED, book.bookUrl) } } - private fun upChangeDurChapterIndex( - book: Book, - oldTocSize: Int, - chapters: List - ) { - execute { - book.durChapterIndex = BookHelp.getDurChapter( - book.durChapterIndex, - book.durChapterTitle, - chapters, - oldTocSize - ) - book.durChapterTitle = chapters[book.durChapterIndex].getDisplayTitle( - ContentProcessor.get(book.name, book.origin).getTitleReplaceRules() - ) - appDb.bookDao.update(book) - appDb.bookChapterDao.insert(*chapters.toTypedArray()) - } - } - fun removeFromBookshelf(success: (() -> Unit)?) { execute { AudioPlay.book?.let { diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceDialog.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceDialog.kt index a1197acd8..240da2a9b 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceDialog.kt @@ -19,6 +19,7 @@ import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey import io.legado.app.data.appDb 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.SearchBook import io.legado.app.databinding.DialogBookChangeSourceBinding @@ -27,6 +28,7 @@ import io.legado.app.lib.dialogs.alert import io.legado.app.lib.theme.primaryColor import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.book.source.manage.BookSourceActivity +import io.legado.app.ui.widget.dialog.WaitDialog import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding @@ -50,6 +52,7 @@ class ChangeBookSourceDialog() : BaseDialogFragment(R.layout.dialog_book_change_ private val groups = linkedSetOf() private val callBack: CallBack? get() = activity as? CallBack private val viewModel: ChangeBookSourceViewModel by viewModels() + private val waitDialog by lazy { WaitDialog(requireContext()) } private val adapter by lazy { ChangeBookSourceAdapter(requireContext(), viewModel, this) } private val editSourceResult = registerForActivityResult(StartActivityContract(BookSourceEditActivity::class.java)) { @@ -241,8 +244,9 @@ class ChangeBookSourceDialog() : BaseDialogFragment(R.layout.dialog_book_change_ } override fun changeTo(searchBook: SearchBook) { - changeSource(searchBook) - dismissAllowingStateLoss() + changeSource(searchBook) { + dismissAllowingStateLoss() + } } override val bookUrl: String? @@ -269,22 +273,22 @@ class ChangeBookSourceDialog() : BaseDialogFragment(R.layout.dialog_book_change_ override fun deleteSource(searchBook: SearchBook) { viewModel.del(searchBook) if (bookUrl == searchBook.bookUrl) { - viewModel.firstSourceOrNull(searchBook)?.let { - changeSource(it) + viewModel.autoChangeSource { book, toc, source -> + callBack?.changeTo(source, book, toc) } } } - private fun changeSource(searchBook: SearchBook) { - try { - val book = searchBook.toBook() - book.upInfoFromOld(callBack?.oldBook) - val source = appDb.bookSourceDao.getBookSource(book.origin) - callBack?.changeTo(source!!, book) - searchBook.time = System.currentTimeMillis() - viewModel.updateSource(searchBook) - } catch (e: Exception) { - toastOnUi("换源失败\n${e.localizedMessage}") + private fun changeSource(searchBook: SearchBook, onSuccess: (() -> Unit)? = null) { + waitDialog.setText(R.string.load_toc) + waitDialog.show() + val book = searchBook.toBook() + viewModel.getToc(book, { + waitDialog.dismiss() + toastOnUi(it) + }) { toc, source -> + callBack?.changeTo(source, book, toc) + onSuccess?.invoke() } } @@ -325,7 +329,7 @@ class ChangeBookSourceDialog() : BaseDialogFragment(R.layout.dialog_book_change_ interface CallBack { val oldBook: Book? - fun changeTo(source: BookSource, book: Book) + fun changeTo(source: BookSource, book: Book, toc: List) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt index 9234eaa2b..de8760317 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt @@ -11,14 +11,17 @@ import io.legado.app.constant.AppPattern import io.legado.app.constant.PreferKey import io.legado.app.data.appDb 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.SearchBook +import io.legado.app.exception.NoStackTraceException import io.legado.app.help.config.AppConfig import io.legado.app.help.coroutine.CompositeCoroutine 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.getPrefString +import io.legado.app.utils.toastOnUi import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.ExecutorCoroutineDispatcher @@ -27,6 +30,7 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.withContext import splitties.init.appCtx import java.util.* import java.util.concurrent.Executors @@ -86,6 +90,11 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a @Volatile private var searchIndex = -1 + override fun onCleared() { + super.onCleared() + searchPool?.close() + } + @CallSuper open fun initData(arguments: Bundle?) { arguments?.let { bundle -> @@ -253,9 +262,32 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a searchStateData.postValue(false) } - override fun onCleared() { - super.onCleared() - searchPool?.close() + open fun getToc( + book: Book, + onError: (msg: String) -> Unit, + onSuccess: (toc: List, source: BookSource) -> Unit + ) { + execute { + getToc(book).getOrThrow() + }.onError { + onError.invoke(it.localizedMessage ?: "加载目录错误") + }.onSuccess { + onSuccess.invoke(it.first, it.second) + } + } + + suspend fun getToc(book: Book): Result, BookSource>> { + return kotlin.runCatching { + withContext(IO) { + val source = appDb.bookSourceDao.getBookSource(book.origin) + ?: throw NoStackTraceException("书源不存在") + if (book.tocUrl.isEmpty()) { + WebBook.getBookInfoAwait(this, source, book) + } + val toc = WebBook.getChapterListAwait(this, source, book) + Pair(toc, source) + } + } } fun disableSource(searchBook: SearchBook) { @@ -310,8 +342,23 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a searchCallback?.upAdapter() } - fun firstSourceOrNull(searchBook: SearchBook): SearchBook? { - return searchBooks.firstOrNull { it.bookUrl != searchBook.bookUrl } + fun autoChangeSource( + onSuccess: (book: Book, toc: List, source: BookSource) -> Unit + ) { + execute { + searchBooks.forEach { + val book = it.toBook() + val result = getToc(book).getOrNull() + if (result != null) { + return@execute Triple(book, result.first, result.second) + } + } + throw NoStackTraceException("没有有效源") + }.onSuccess { + onSuccess.invoke(it.first, it.second, it.third) + }.onError { + context.toastOnUi("自动换源失败\n${it.localizedMessage}") + } } interface SourceCallback { diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceDialog.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceDialog.kt index a819b15d6..f2de2c707 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceDialog.kt @@ -67,13 +67,6 @@ class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_chapter_c private val tocAdapter by lazy { ChangeChapterTocAdapter(requireContext(), this) } - private val tocSuccess: (toc: List) -> Unit = { - tocAdapter.durChapterIndex = - BookHelp.getDurChapter(viewModel.chapterIndex, viewModel.chapterTitle, it) - binding.loadingToc.hide() - tocAdapter.setItems(it) - binding.recyclerViewToc.scrollToPosition(tocAdapter.durChapterIndex - 5) - } private val contentSuccess: (content: String) -> Unit = { binding.loadingToc.hide() callBack?.replaceContent(it) @@ -279,9 +272,16 @@ class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_chapter_c tocAdapter.setItems(null) binding.clToc.visible() binding.loadingToc.show() - viewModel.getToc(searchBook, tocSuccess) { + val book = searchBook.toBook() + viewModel.getToc(book, { binding.clToc.gone() toastOnUi(it) + }) { toc: List, _: BookSource -> + tocAdapter.durChapterIndex = + BookHelp.getDurChapter(viewModel.chapterIndex, viewModel.chapterTitle, toc) + binding.loadingToc.hide() + tocAdapter.setItems(toc) + binding.recyclerViewToc.scrollToPosition(tocAdapter.durChapterIndex - 5) } } @@ -309,8 +309,8 @@ class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_chapter_c override fun deleteSource(searchBook: SearchBook) { viewModel.del(searchBook) if (bookUrl == searchBook.bookUrl) { - viewModel.firstSourceOrNull(searchBook)?.let { - changeSource(it) + viewModel.autoChangeSource { book, toc, source -> + callBack?.changeTo(source, book, toc) } } } @@ -326,19 +326,6 @@ class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_chapter_c } } - private fun changeSource(searchBook: SearchBook) { - try { - val book = searchBook.toBook() - book.upInfoFromOld(callBack?.oldBook) - val source = appDb.bookSourceDao.getBookSource(book.origin) - callBack?.changeTo(source!!, book) - searchBook.time = System.currentTimeMillis() - viewModel.updateSource(searchBook) - } catch (e: Exception) { - toastOnUi("换源失败\n${e.localizedMessage}") - } - } - /** * 更新分组菜单 */ @@ -388,7 +375,7 @@ class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_chapter_c interface CallBack { val oldBook: Book? - fun changeTo(source: BookSource, book: Book) + fun changeTo(source: BookSource, book: Book, toc: List) fun replaceContent(content: String) } diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceViewModel.kt index 68797fab4..117cd26b5 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceViewModel.kt @@ -5,7 +5,7 @@ import android.os.Bundle import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter -import io.legado.app.data.entities.SearchBook +import io.legado.app.data.entities.BookSource import io.legado.app.exception.NoStackTraceException import io.legado.app.model.webBook.WebBook import java.util.concurrent.ConcurrentHashMap @@ -29,28 +29,24 @@ class ChangeChapterSourceViewModel(application: Application) : } } - fun getToc( - searchBook: SearchBook, - success: (toc: List) -> Unit, - error: (msg: String) -> Unit + override fun getToc( + book: Book, + onError: (msg: String) -> Unit, + onSuccess: (toc: List, source: BookSource) -> Unit ) { execute { - return@execute tocMap[searchBook.bookUrl] - ?: let { - val book = searchBook.toBook() - val source = appDb.bookSourceDao.getBookSource(book.origin) - ?: throw NoStackTraceException("书源不存在") - if (book.tocUrl.isEmpty()) { - WebBook.getBookInfoAwait(this, source, book) - } - val toc = WebBook.getChapterListAwait(this, source, book) - tocMap[book.bookUrl] = toc - toc - } + val toc = tocMap[book.bookUrl] + if (toc != null) { + val source = appDb.bookSourceDao.getBookSource(book.origin) + return@execute Pair(toc, source!!) + } + val result = getToc(book).getOrThrow() + tocMap[book.bookUrl] = result.first + return@execute result }.onSuccess { - success(it) + onSuccess.invoke(it.first, it.second) }.onError { - error(it.localizedMessage ?: "获取目录出错") + onError.invoke(it.localizedMessage ?: "获取目录出错") } } diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index eef1bb942..9cf8fa8f5 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -457,9 +457,8 @@ class BookInfoActivity : override val oldBook: Book? get() = viewModel.bookData.value - override fun changeTo(source: BookSource, book: Book) { - upLoading(true) - viewModel.changeTo(source, book) + override fun changeTo(source: BookSource, book: Book, toc: List) { + viewModel.changeTo(source, book, toc) } override fun coverChangeTo(coverUrl: String) { diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt index 1ed56f4ac..2aa8b9277 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt @@ -14,7 +14,6 @@ import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookSource import io.legado.app.exception.NoStackTraceException import io.legado.app.help.BookHelp -import io.legado.app.help.ContentProcessor import io.legado.app.help.coroutine.Coroutine import io.legado.app.model.BookCover import io.legado.app.model.ReadBook @@ -24,7 +23,6 @@ import io.legado.app.utils.postEvent import io.legado.app.utils.toastOnUi import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.ensureActive class BookInfoViewModel(application: Application) : BaseViewModel(application) { val bookData = MutableLiveData() @@ -107,12 +105,11 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { fun loadBookInfo( book: Book, canReName: Boolean = true, - scope: CoroutineScope = viewModelScope, - changeDruChapterIndex: ((chapters: List) -> Unit)? = null, + scope: CoroutineScope = viewModelScope ) { execute(scope) { if (book.isLocalBook()) { - loadChapter(book, scope, changeDruChapterIndex) + loadChapter(book, scope) } else { bookSource?.let { bookSource -> WebBook.getBookInfo(this, bookSource, book, canReName = canReName) @@ -121,7 +118,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { if (inBookshelf) { appDb.bookDao.update(book) } - loadChapter(it, scope, changeDruChapterIndex) + loadChapter(it, scope) }.onError { AppLog.put("获取数据信息失败\n${it.localizedMessage}", it) context.toastOnUi(R.string.error_get_book_info) @@ -136,8 +133,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { private fun loadChapter( book: Book, - scope: CoroutineScope = viewModelScope, - changeDruChapterIndex: ((chapters: List) -> Unit)? = null, + scope: CoroutineScope = viewModelScope ) { execute(scope) { if (book.isLocalBook()) { @@ -156,11 +152,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { appDb.bookChapterDao.delByBook(book.bookUrl) appDb.bookChapterDao.insert(*it.toTypedArray()) } - if (changeDruChapterIndex == null) { - chapterListData.postValue(it) - } else { - changeDruChapterIndex(it) - } + chapterListData.postValue(it) }.onError { chapterListData.postValue(emptyList()) AppLog.put("获取目录失败\n${it.localizedMessage}", it) @@ -184,58 +176,18 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { } } - fun changeTo(source: BookSource, newBook: Book) { + fun changeTo(source: BookSource, newBook: Book, toc: List) { changeSourceCoroutine?.cancel() changeSourceCoroutine = execute { - var oldTocSize: Int = newBook.totalChapterNum - if (inBookshelf) { - bookData.value?.let { - oldTocSize = it.totalChapterNum - it.changeTo(newBook) - } - } - bookData.postValue(newBook) bookSource = source - if (newBook.tocUrl.isEmpty()) { - loadBookInfo(newBook, false, this) { - ensureActive() - upChangeDurChapterIndex(newBook, oldTocSize, it) - } - } else { - loadChapter(newBook, this) { - ensureActive() - upChangeDurChapterIndex(newBook, oldTocSize, it) - } - } + bookData.value!!.changeTo(newBook, toc) + bookData.postValue(newBook) + chapterListData.postValue(toc) }.onFinally { postEvent(EventBus.SOURCE_CHANGED, newBook.bookUrl) } } - private fun upChangeDurChapterIndex( - book: Book, - oldTocSize: Int, - chapters: List - ) { - execute { - book.durChapterIndex = BookHelp.getDurChapter( - book.durChapterIndex, - book.durChapterTitle, - chapters, - oldTocSize - ) - book.durChapterTitle = chapters[book.durChapterIndex].getDisplayTitle( - ContentProcessor.get(book.name, book.origin).getTitleReplaceRules() - ) - if (inBookshelf) { - appDb.bookDao.update(book) - appDb.bookChapterDao.insert(*chapters.toTypedArray()) - } - bookData.postValue(book) - chapterListData.postValue(chapters) - } - } - fun topBook() { execute { bookData.value?.let { book -> @@ -300,7 +252,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { fun delBook(deleteOriginal: Boolean = false, success: (() -> Unit)? = null) { execute { bookData.value?.let { - Book.delete(it) + it.delete() inBookshelf = false if (it.isLocalBook()) { LocalBook.deleteBook(it, deleteOriginal) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 2c1c9f94b..7a2d9da26 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -715,8 +715,8 @@ class ReadBookActivity : BaseReadBookActivity(), override val oldBook: Book? get() = ReadBook.book - override fun changeTo(source: BookSource, book: Book) { - viewModel.changeTo(source, book) + override fun changeTo(source: BookSource, book: Book, toc: List) { + viewModel.changeTo(source, book, toc) } override fun replaceContent(content: String) { diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt index e67921b61..3293dba25 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt @@ -10,6 +10,7 @@ import io.legado.app.constant.AppLog import io.legado.app.constant.EventBus import io.legado.app.data.appDb import io.legado.app.data.entities.Book +import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookProgress import io.legado.app.data.entities.BookSource import io.legado.app.exception.NoStackTraceException @@ -30,7 +31,6 @@ import io.legado.app.utils.postEvent import io.legado.app.utils.toStringArray import io.legado.app.utils.toastOnUi import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.ensureActive class ReadBookViewModel(application: Application) : BaseViewModel(application) { val permissionDenialLiveData = MutableLiveData() @@ -191,40 +191,22 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { /** * 换源 */ - fun changeTo(source: BookSource, book: Book) { + fun changeTo(source: BookSource, book: Book, toc: List) { changeSourceCoroutine?.cancel() changeSourceCoroutine = execute { ReadBook.upMsg(context.getString(R.string.loading)) - if (book.tocUrl.isEmpty()) { - WebBook.getBookInfoAwait(this, source, book) - } - ensureActive() - val chapters = WebBook.getChapterListAwait(this, source, book) - ensureActive() - val oldBook = ReadBook.book!! - book.durChapterIndex = BookHelp.getDurChapter( - oldBook.durChapterIndex, - oldBook.durChapterTitle, - chapters, - oldBook.totalChapterNum - ) - book.durChapterTitle = chapters[book.durChapterIndex].getDisplayTitle( - ContentProcessor.get(book.name, book.origin).getTitleReplaceRules() - ) - ensureActive() - val nextChapter = chapters.getOrElse(book.durChapterIndex) { - chapters.first() + ReadBook.book!!.changeTo(book, toc) + val nextChapter = toc.getOrElse(book.durChapterIndex) { + toc.first() } WebBook.getContentAwait( this, bookSource = source, book = book, - bookChapter = chapters[book.durChapterIndex], + bookChapter = toc[book.durChapterIndex], nextChapterUrl = nextChapter.url ) - ensureActive() - oldBook.changeTo(book) - appDb.bookChapterDao.insert(*chapters.toTypedArray()) + appDb.bookChapterDao.insert(*toc.toTypedArray()) ReadBook.resetData(book) ReadBook.upMsg(null) ReadBook.loadContent(resetPageOffset = true) @@ -244,10 +226,17 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { if (!AppConfig.autoChangeSource) return execute { val sources = appDb.bookSourceDao.allTextEnabled - WebBook.preciseSearchAwait(this, name, author, *sources.toTypedArray())?.let { - it.second.upInfoFromOld(ReadBook.book) - changeTo(it.first, it.second) - } ?: throw NoStackTraceException("自动换源失败") + sources.forEach { source -> + WebBook.preciseSearchAwait(this, source, name, author).getOrNull()?.let { book -> + if (book.tocUrl.isEmpty()) { + WebBook.getBookInfoAwait(this, source, book) + } + val toc = WebBook.getChapterListAwait(this, source, book) + changeTo(source, book, toc) + return@execute + } + } + throw NoStackTraceException("自动换源失败") }.onStart { ReadBook.upMsg(context.getString(R.string.source_auto_changing)) }.onError { @@ -272,7 +261,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { fun removeFromBookshelf(success: (() -> Unit)?) { execute { - Book.delete(ReadBook.book) + ReadBook.book?.delete() }.onSuccess { success?.invoke() } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt index 3f7b8f720..43fddca9d 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt @@ -132,7 +132,7 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application) if (name.isNotEmpty() && appDb.bookDao.getBook(name, author) == null) { WebBook.preciseSearch(this, bookSources, name, author) .onSuccess { - val book = it.second + val book = it.first if (groupId > 0) { book.group = groupId }