pull/1751/head
kunfei 3 years ago
parent f666b0ec0c
commit ef59a1de90
  1. 45
      app/src/main/java/io/legado/app/data/entities/Book.kt
  2. 39
      app/src/main/java/io/legado/app/model/webBook/WebBook.kt
  3. 14
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookViewModel.kt
  4. 5
      app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt
  5. 58
      app/src/main/java/io/legado/app/ui/book/audio/AudioPlayViewModel.kt
  6. 34
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceDialog.kt
  7. 57
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt
  8. 35
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceDialog.kt
  9. 34
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceViewModel.kt
  10. 5
      app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
  11. 68
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  12. 4
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  13. 49
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  14. 2
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.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<BookChapter>): 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

@ -320,35 +320,38 @@ object WebBook {
name: String,
author: String,
context: CoroutineContext = Dispatchers.IO,
): Coroutine<Pair<BookSource, Book>> {
): Coroutine<Pair<Book, BookSource>> {
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<BookSource, Book>? {
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<Book?> {
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
}
}

@ -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

@ -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<BookChapter>) {
viewModel.changeTo(source, book, toc)
}
override fun finish() {

@ -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<BookChapter>) -> 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<BookChapter>) -> 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<BookChapter>) {
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<BookChapter>
) {
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 {

@ -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<String>()
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<BookChapter>)
}
}

@ -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<BookChapter>, 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<Pair<List<BookChapter>, 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<BookChapter>, 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 {

@ -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<BookChapter>) -> 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<BookChapter>, _: 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<BookChapter>)
fun replaceContent(content: String)
}

@ -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<BookChapter>) -> Unit,
error: (msg: String) -> Unit
override fun getToc(
book: Book,
onError: (msg: String) -> Unit,
onSuccess: (toc: List<BookChapter>, 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 ?: "获取目录出错")
}
}

@ -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<BookChapter>) {
viewModel.changeTo(source, book, toc)
}
override fun coverChangeTo(coverUrl: String) {

@ -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<Book>()
@ -107,12 +105,11 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
fun loadBookInfo(
book: Book,
canReName: Boolean = true,
scope: CoroutineScope = viewModelScope,
changeDruChapterIndex: ((chapters: List<BookChapter>) -> 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<BookChapter>) -> 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<BookChapter>) {
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<BookChapter>
) {
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)

@ -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<BookChapter>) {
viewModel.changeTo(source, book, toc)
}
override fun replaceContent(content: String) {

@ -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<Int>()
@ -191,40 +191,22 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
/**
* 换源
*/
fun changeTo(source: BookSource, book: Book) {
fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) {
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()
}

@ -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
}

Loading…
Cancel
Save