优化
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+15
-19
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user