mirror of
https://github.com/gedoor/legado.git
synced 2024-08-30 09:23:26 +08:00
优化webFile导入
This commit is contained in:
parent
6fa331c928
commit
ca3f6e26f4
@ -96,6 +96,7 @@ android {
|
||||
}
|
||||
|
||||
flavorDimensions.add("mode")
|
||||
|
||||
productFlavors {
|
||||
app {
|
||||
dimension "mode"
|
||||
@ -107,6 +108,7 @@ android {
|
||||
manifestPlaceholders.put("APP_CHANNEL_VALUE", "google")
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
// Flag to enable support for the new language APIs
|
||||
coreLibraryDesugaringEnabled true
|
||||
@ -115,6 +117,11 @@ android {
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
// fix found with path 'META-INF/INDEX.LIST' from inputs
|
||||
exclude 'META-INF/INDEX.LIST'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
// Adds exported schema location as test app assets.
|
||||
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
|
@ -49,9 +49,9 @@ data class Server(
|
||||
|
||||
@Parcelize
|
||||
data class WebDavConfig(
|
||||
var url: String? = null,
|
||||
var username: String? = null,
|
||||
var password: String? = null,
|
||||
var url: String,
|
||||
var username: String,
|
||||
var password: String
|
||||
) : Parcelable
|
||||
|
||||
}
|
@ -18,49 +18,39 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
|
||||
val Book.isAudio: Boolean
|
||||
get() {
|
||||
return type and BookType.audio > 0
|
||||
}
|
||||
get() = isType(BookType.audio)
|
||||
|
||||
val Book.isImage: Boolean
|
||||
get() {
|
||||
return type and BookType.image > 0
|
||||
}
|
||||
get() = isType(BookType.image)
|
||||
|
||||
val Book.isLocal: Boolean
|
||||
get() {
|
||||
if (type == 0) {
|
||||
return origin == BookType.localTag || origin.startsWith(BookType.webDavTag)
|
||||
}
|
||||
return type and BookType.local > 0
|
||||
return isType(BookType.local)
|
||||
}
|
||||
|
||||
val Book.isLocalTxt: Boolean
|
||||
get() {
|
||||
return isLocal && originName.endsWith(".txt", true)
|
||||
}
|
||||
get() = isLocal && originName.endsWith(".txt", true)
|
||||
|
||||
val Book.isEpub: Boolean
|
||||
get() {
|
||||
return isLocal && originName.endsWith(".epub", true)
|
||||
}
|
||||
get() = isLocal && originName.endsWith(".epub", true)
|
||||
|
||||
val Book.isUmd: Boolean
|
||||
get() {
|
||||
return isLocal && originName.endsWith(".umd", true)
|
||||
}
|
||||
get() = isLocal && originName.endsWith(".umd", true)
|
||||
|
||||
val Book.isPdf: Boolean
|
||||
get() {
|
||||
return isLocal && originName.endsWith(".pdf", true)
|
||||
}
|
||||
get() = isLocal && originName.endsWith(".pdf", true)
|
||||
|
||||
val Book.isOnLineTxt: Boolean
|
||||
get() {
|
||||
return !isLocal && type and BookType.text > 0
|
||||
}
|
||||
get() = !isLocal && isType(BookType.text)
|
||||
|
||||
val Book.isWebFile: Boolean
|
||||
get() = isType(BookType.webFile)
|
||||
|
||||
val Book.isUpError: Boolean
|
||||
get() = type and BookType.updateError > 0
|
||||
get() = isType(BookType.updateError)
|
||||
|
||||
fun Book.contains(word: String?): Boolean {
|
||||
if (word.isNullOrEmpty()) {
|
||||
@ -172,6 +162,8 @@ fun Book.clearType() {
|
||||
type = 0
|
||||
}
|
||||
|
||||
fun Book.isType(@BookType.Type bookType: Int): Boolean = type and bookType > 0
|
||||
|
||||
fun Book.upType() {
|
||||
if (type < 8) {
|
||||
type = when (type) {
|
||||
|
@ -24,7 +24,7 @@ data class Authorization(
|
||||
|
||||
constructor(serverID: Long?): this("","") {
|
||||
serverID ?: throw NoStackTraceException("Unexpected server ID")
|
||||
appDb.serverDao.get(serverID)?.getWebDavConfig().run {
|
||||
appDb.serverDao.get(serverID)?.getWebDavConfig()?.run {
|
||||
data = Credentials.basic(username, password, charset)
|
||||
} ?: throw WebDavException("Unexpected WebDav Authorization")
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import io.legado.app.data.entities.Book
|
||||
import io.legado.app.data.entities.BookSource
|
||||
import io.legado.app.exception.NoStackTraceException
|
||||
import io.legado.app.help.book.BookHelp
|
||||
import io.legado.app.help.book.isWebFile
|
||||
import io.legado.app.model.Debug
|
||||
import io.legado.app.model.analyzeRule.AnalyzeRule
|
||||
import io.legado.app.utils.DebugLog
|
||||
@ -137,7 +138,7 @@ object BookInfo {
|
||||
Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}")
|
||||
DebugLog.e("获取封面出错", e)
|
||||
}
|
||||
if (book.type and BookType.webFile == 0) {
|
||||
if (book.isWebFile) {
|
||||
coroutineContext.ensureActive()
|
||||
Debug.log(bookSource.bookSourceUrl, "┌获取目录链接")
|
||||
book.tocUrl = analyzeRule.getString(infoRule.tocUrl, isUrl = true)
|
||||
|
@ -1,131 +0,0 @@
|
||||
package io.legado.app.ui.association
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.legado.app.R
|
||||
import io.legado.app.base.BaseDialogFragment
|
||||
import io.legado.app.base.adapter.ItemViewHolder
|
||||
import io.legado.app.base.adapter.RecyclerAdapter
|
||||
import io.legado.app.databinding.DialogRecyclerViewBinding
|
||||
import io.legado.app.databinding.ItemBookFileImportBinding
|
||||
import io.legado.app.lib.dialogs.alert
|
||||
import io.legado.app.lib.theme.primaryColor
|
||||
import io.legado.app.ui.widget.dialog.WaitDialog
|
||||
import io.legado.app.utils.openFileUri
|
||||
import io.legado.app.utils.setLayout
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
import io.legado.app.utils.visible
|
||||
|
||||
|
||||
/**
|
||||
* 导入在线书籍文件弹出窗口
|
||||
*/
|
||||
class ImportOnLineBookFileDialog : BaseDialogFragment(R.layout.dialog_recycler_view) {
|
||||
|
||||
|
||||
private val binding by viewBinding(DialogRecyclerViewBinding::bind)
|
||||
private val viewModel by viewModels<ImportOnLineBookFileViewModel>()
|
||||
private val adapter by lazy { BookFileAdapter(requireContext()) }
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
}
|
||||
|
||||
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val bookUrl = arguments?.getString("bookUrl")
|
||||
viewModel.initData(bookUrl)
|
||||
binding.toolBar.setBackgroundColor(primaryColor)
|
||||
binding.toolBar.setTitle(R.string.download_and_import_file)
|
||||
binding.rotateLoading.visible()
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
binding.recyclerView.adapter = adapter
|
||||
viewModel.errorLiveData.observe(this) {
|
||||
binding.rotateLoading.gone()
|
||||
binding.tvMsg.apply {
|
||||
text = it
|
||||
visible()
|
||||
}
|
||||
}
|
||||
viewModel.successLiveData.observe(this) {
|
||||
binding.rotateLoading.gone()
|
||||
if (it > 0) {
|
||||
adapter.setItems(viewModel.allBookFiles)
|
||||
}
|
||||
}
|
||||
viewModel.savedFileUriData.observe(this) {
|
||||
requireContext().openFileUri(it, "*/*")
|
||||
}
|
||||
}
|
||||
|
||||
private fun importFileAndUpdate(url: String, fileName: String) {
|
||||
val waitDialog = WaitDialog(requireContext())
|
||||
waitDialog.show()
|
||||
viewModel.importOnLineBookFile(url, fileName) {
|
||||
waitDialog.dismiss()
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadFile(url: String, fileName: String) {
|
||||
val waitDialog = WaitDialog(requireContext())
|
||||
waitDialog.show()
|
||||
viewModel.downloadUrl(url, fileName) {
|
||||
waitDialog.dismiss()
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
inner class BookFileAdapter(context: Context) :
|
||||
RecyclerAdapter<Triple<String, String, Boolean>
|
||||
, ItemBookFileImportBinding>(context) {
|
||||
|
||||
override fun getViewBinding(parent: ViewGroup): ItemBookFileImportBinding {
|
||||
return ItemBookFileImportBinding.inflate(inflater, parent, false)
|
||||
}
|
||||
|
||||
override fun convert(
|
||||
holder: ItemViewHolder,
|
||||
binding: ItemBookFileImportBinding,
|
||||
item: Triple<String, String, Boolean>,
|
||||
payloads: MutableList<Any>
|
||||
) {
|
||||
binding.apply {
|
||||
cbFileName.text = item.second
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerListener(
|
||||
holder: ItemViewHolder,
|
||||
binding: ItemBookFileImportBinding
|
||||
) {
|
||||
binding.apply {
|
||||
cbFileName.setOnClickListener {
|
||||
val selectFile = viewModel.allBookFiles[holder.layoutPosition]
|
||||
if (selectFile.third) {
|
||||
importFileAndUpdate(selectFile.first, selectFile.second)
|
||||
} else {
|
||||
alert(
|
||||
title = getString(R.string.draw),
|
||||
message = getString(R.string.file_not_supported, selectFile.second)
|
||||
) {
|
||||
yesButton {
|
||||
importFileAndUpdate(selectFile.first, selectFile.second)
|
||||
}
|
||||
neutralButton(R.string.open_fun) {
|
||||
downloadFile(selectFile.first, selectFile.second)
|
||||
}
|
||||
noButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package io.legado.app.ui.association
|
||||
|
||||
import android.app.Application
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import io.legado.app.base.BaseViewModel
|
||||
import io.legado.app.constant.AppPattern
|
||||
import io.legado.app.constant.EventBus
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.data.entities.BookSource
|
||||
import io.legado.app.exception.NoStackTraceException
|
||||
import io.legado.app.model.analyzeRule.AnalyzeRule
|
||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||
import io.legado.app.model.localBook.LocalBook
|
||||
import io.legado.app.utils.postEvent
|
||||
import io.legado.app.utils.toastOnUi
|
||||
|
||||
class ImportOnLineBookFileViewModel(app: Application) : BaseViewModel(app) {
|
||||
|
||||
val allBookFiles = arrayListOf<Triple<String, String, Boolean>>()
|
||||
val errorLiveData = MutableLiveData<String>()
|
||||
val successLiveData = MutableLiveData<Int>()
|
||||
val savedFileUriData = MutableLiveData<Uri>()
|
||||
var bookSource: BookSource? = null
|
||||
|
||||
fun initData(bookUrl: String?) {
|
||||
execute {
|
||||
bookUrl ?: throw NoStackTraceException("书籍详情页链接为空")
|
||||
val book = appDb.searchBookDao.getSearchBook(bookUrl)?.toBook()
|
||||
?: throw NoStackTraceException("book is null")
|
||||
bookSource = appDb.bookSourceDao.getBookSource(book.origin)
|
||||
?: throw NoStackTraceException("bookSource is null")
|
||||
val ruleDownloadUrls = bookSource?.getBookInfoRule()?.downloadUrls
|
||||
val content = AnalyzeUrl(bookUrl, source = bookSource).getStrResponse().body
|
||||
val analyzeRule = AnalyzeRule(book, bookSource)
|
||||
analyzeRule.setContent(content).setBaseUrl(bookUrl)
|
||||
val fileName = "${book.name} 作者:${book.author}"
|
||||
analyzeRule.getStringList(ruleDownloadUrls, isUrl = true)?.let {
|
||||
it.forEach { url ->
|
||||
val mFileName = "${fileName}.${LocalBook.parseFileSuffix(url)}"
|
||||
val isSupportedFile = AppPattern.bookFileRegex.matches(mFileName)
|
||||
allBookFiles.add(Triple(url, mFileName, isSupportedFile))
|
||||
}
|
||||
} ?: throw NoStackTraceException("下载链接规则解析为空")
|
||||
}.onSuccess {
|
||||
successLiveData.postValue(allBookFiles.size)
|
||||
}.onError {
|
||||
errorLiveData.postValue(it.localizedMessage ?: "")
|
||||
context.toastOnUi("获取书籍下载链接失败\n${it.localizedMessage}")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun downloadUrl(url: String, fileName: String, success: () -> Unit) {
|
||||
execute {
|
||||
LocalBook.saveBookFile(url, fileName, bookSource).let {
|
||||
savedFileUriData.postValue(it)
|
||||
}
|
||||
}.onSuccess {
|
||||
success.invoke()
|
||||
}.onError {
|
||||
context.toastOnUi("下载书籍文件失败\n${it.localizedMessage}")
|
||||
}
|
||||
}
|
||||
|
||||
fun importOnLineBookFile(url: String, fileName: String, success: () -> Unit) {
|
||||
execute {
|
||||
LocalBook.importFileOnLine(url, fileName, bookSource).let {
|
||||
postEvent(EventBus.FILE_SOURCE_DOWNLOAD_DONE, it.bookUrl)
|
||||
}
|
||||
}.onSuccess {
|
||||
success.invoke()
|
||||
}.onError {
|
||||
context.toastOnUi("下载书籍文件失败\n${it.localizedMessage}")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -21,10 +21,7 @@ import io.legado.app.databinding.ActivityBookInfoBinding
|
||||
import io.legado.app.databinding.DialogEditTextBinding
|
||||
import io.legado.app.exception.NoStackTraceException
|
||||
import io.legado.app.help.AppWebDav
|
||||
import io.legado.app.help.book.isAudio
|
||||
import io.legado.app.help.book.isLocal
|
||||
import io.legado.app.help.book.isLocalTxt
|
||||
import io.legado.app.help.book.getRemoteUrl
|
||||
import io.legado.app.help.book.*
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.lib.dialogs.alert
|
||||
import io.legado.app.lib.theme.backgroundColor
|
||||
@ -33,7 +30,6 @@ import io.legado.app.lib.theme.getPrimaryTextColor
|
||||
import io.legado.app.model.BookCover
|
||||
import io.legado.app.model.remote.RemoteBookWebDav
|
||||
import io.legado.app.ui.about.AppLogDialog
|
||||
import io.legado.app.ui.association.ImportOnLineBookFileDialog
|
||||
import io.legado.app.ui.book.audio.AudioPlayActivity
|
||||
import io.legado.app.ui.book.changecover.ChangeCoverDialog
|
||||
import io.legado.app.ui.book.changesource.ChangeBookSourceDialog
|
||||
@ -231,7 +227,6 @@ class BookInfoActivity :
|
||||
waitDialog.setText("上传中.....")
|
||||
waitDialog.show()
|
||||
try {
|
||||
|
||||
bookWebDav
|
||||
?.upload(book)
|
||||
?: throw NoStackTraceException("未配置webDav")
|
||||
@ -253,6 +248,7 @@ class BookInfoActivity :
|
||||
tvOrigin.text = getString(R.string.origin_show, book.originName)
|
||||
tvLasted.text = getString(R.string.lasted_show, book.latestChapterTitle)
|
||||
tvIntro.text = book.getDisplayIntro()
|
||||
tvToc.visible(!book.isWebFile)
|
||||
upTvBookshelf()
|
||||
val kinds = book.getKindList()
|
||||
if (kinds.isEmpty()) {
|
||||
@ -278,8 +274,7 @@ class BookInfoActivity :
|
||||
binding.tvToc.text = getString(R.string.toc_s, getString(R.string.loading))
|
||||
}
|
||||
chapterList.isNullOrEmpty() -> {
|
||||
binding.tvToc.text =
|
||||
if (viewModel.isImportBookOnLine) getString(R.string.click_read_button_load) else getString(
|
||||
binding.tvToc.text = getString(
|
||||
R.string.toc_s,
|
||||
getString(R.string.error_load_toc)
|
||||
)
|
||||
@ -333,9 +328,9 @@ class BookInfoActivity :
|
||||
}
|
||||
tvRead.setOnClickListener {
|
||||
viewModel.bookData.value?.let { book ->
|
||||
if (viewModel.isImportBookOnLine) {
|
||||
showDialogFragment<ImportOnLineBookFileDialog> {
|
||||
putString("bookUrl", book.bookUrl)
|
||||
if (book.isWebFile) {
|
||||
showWebFileDownloadAlert {
|
||||
readBook(book)
|
||||
}
|
||||
} else {
|
||||
readBook(book)
|
||||
@ -343,13 +338,23 @@ class BookInfoActivity :
|
||||
} ?: toastOnUi("Book is null")
|
||||
}
|
||||
tvShelf.setOnClickListener {
|
||||
viewModel.bookData.value?.let { book ->
|
||||
if (viewModel.inBookshelf) {
|
||||
deleteBook()
|
||||
} else {
|
||||
if (book.isWebFile) {
|
||||
showWebFileDownloadAlert {
|
||||
viewModel.addToBookshelf {
|
||||
upTvBookshelf()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
viewModel.addToBookshelf {
|
||||
upTvBookshelf()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tvOrigin.setOnClickListener {
|
||||
viewModel.bookData.value?.let { book ->
|
||||
@ -493,6 +498,34 @@ class BookInfoActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun showWebFileDownloadAlert(onClick: (() -> Unit)?) {
|
||||
viewModel.webFileData ?: return
|
||||
alert(titleResource = R.string.download_and_import_file) {
|
||||
val webFileData = viewModel.webFileData
|
||||
webFileData?.let {
|
||||
items<BookInfoViewModel.WebFile>(it) { _, webFile, _ ->
|
||||
if (webFile.isSupported) {
|
||||
viewModel.importOrDownloadWebFile(webFile) {
|
||||
onClick?.invoke()
|
||||
}
|
||||
} else {
|
||||
alert(
|
||||
title = getString(R.string.draw),
|
||||
message = getString(R.string.file_not_supported, webFile.name)
|
||||
) {
|
||||
neutralButton(R.string.open_fun) {
|
||||
viewModel.importOrDownloadWebFile(webFile) { uri ->
|
||||
openFileUri(uri!!, "*/*")
|
||||
}
|
||||
}
|
||||
noButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readBook(book: Book) {
|
||||
if (!viewModel.inBookshelf) {
|
||||
viewModel.saveBook(book) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.legado.app.ui.book.info
|
||||
|
||||
import android.net.Uri
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
@ -7,6 +8,7 @@ import androidx.lifecycle.viewModelScope
|
||||
import io.legado.app.R
|
||||
import io.legado.app.base.BaseViewModel
|
||||
import io.legado.app.constant.AppLog
|
||||
import io.legado.app.constant.AppPattern
|
||||
import io.legado.app.constant.BookSourceType
|
||||
import io.legado.app.constant.BookType
|
||||
import io.legado.app.constant.EventBus
|
||||
@ -19,6 +21,8 @@ import io.legado.app.help.AppWebDav
|
||||
import io.legado.app.help.book.*
|
||||
import io.legado.app.help.coroutine.Coroutine
|
||||
import io.legado.app.lib.webdav.ObjectNotFoundException
|
||||
import io.legado.app.model.analyzeRule.AnalyzeRule
|
||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||
import io.legado.app.model.BookCover
|
||||
import io.legado.app.model.ReadBook
|
||||
import io.legado.app.model.localBook.LocalBook
|
||||
@ -32,11 +36,10 @@ import kotlinx.coroutines.Dispatchers.IO
|
||||
class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
||||
val bookData = MutableLiveData<Book>()
|
||||
val chapterListData = MutableLiveData<List<BookChapter>>()
|
||||
val webFileData: MutableList<WebFile>? = null
|
||||
var inBookshelf = false
|
||||
var bookSource: BookSource? = null
|
||||
private var changeSourceCoroutine: Coroutine<*>? = null
|
||||
val isImportBookOnLine: Boolean
|
||||
get() = bookSource?.bookSourceType == BookSourceType.file
|
||||
|
||||
fun initData(intent: Intent) {
|
||||
execute {
|
||||
@ -82,8 +85,6 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
||||
appDb.bookSourceDao.getBookSource(book.origin)
|
||||
if (book.tocUrl.isEmpty() && !book.isLocal) {
|
||||
loadBookInfo(book)
|
||||
} else if (isImportBookOnLine) {
|
||||
chapterListData.postValue(emptyList())
|
||||
} else {
|
||||
val chapterList = appDb.bookChapterDao.getChapterList(book.bookUrl)
|
||||
if (chapterList.isNotEmpty()) {
|
||||
@ -156,13 +157,14 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
||||
inBookshelf = true
|
||||
}
|
||||
bookData.postValue(book)
|
||||
if (isImportBookOnLine) {
|
||||
appDb.searchBookDao.update(book.toSearchBook())
|
||||
}
|
||||
if (inBookshelf) {
|
||||
appDb.bookDao.update(book)
|
||||
}
|
||||
if (it.isWebFile) {
|
||||
loadWebFile(book, bookSource, scope)
|
||||
} else {
|
||||
loadChapter(it, scope)
|
||||
}
|
||||
}.onError {
|
||||
AppLog.put("获取数据信息失败\n${it.localizedMessage}", it)
|
||||
context.toastOnUi(R.string.error_get_book_info)
|
||||
@ -187,7 +189,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
||||
appDb.bookChapterDao.insert(*it.toTypedArray())
|
||||
chapterListData.postValue(it)
|
||||
}
|
||||
} else if (isImportBookOnLine) {
|
||||
} else if (book.isWebFile) {
|
||||
chapterListData.postValue(emptyList())
|
||||
} else {
|
||||
bookSource?.let { bookSource ->
|
||||
@ -224,6 +226,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun loadGroup(groupId: Long, success: ((groupNames: String?) -> Unit)) {
|
||||
execute {
|
||||
appDb.bookGroupDao.getGroupNames(groupId).joinToString(",")
|
||||
@ -232,6 +235,54 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadWebFile(
|
||||
book: Book,
|
||||
bookSource: BookSource,
|
||||
scope: CoroutineScope = viewModelScope
|
||||
) {
|
||||
execute(scope) {
|
||||
webFileData?.clear()
|
||||
val fileName = "${book.name} 作者:${book.author}"
|
||||
if (book.downloadUrls.isNullOrEmpty()) {
|
||||
val ruleDownloadUrls = bookSource.getBookInfoRule().downloadUrls
|
||||
val content = AnalyzeUrl(book.bookUrl, source = bookSource).getStrResponse().body
|
||||
val analyzeRule = AnalyzeRule(book, bookSource)
|
||||
analyzeRule.setContent(content).setBaseUrl(book.bookUrl)
|
||||
analyzeRule.getStringList(ruleDownloadUrls, isUrl = true)?.let {
|
||||
parseDownloadUrls(it, fileName)
|
||||
} ?: throw NoStackTraceException("Unexpected ruleDownloadUrls")
|
||||
} else {
|
||||
parseDownloadUrls(book.downloadUrls, fileName)
|
||||
}
|
||||
}.onError {
|
||||
context.toastOnUi("LoadWebFileError\n${it.localizedMessage}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseDownloadUrls(downloadUrls: List<String>?, fileName: String) {
|
||||
val urls = downloadUrls
|
||||
urls?.forEach { url ->
|
||||
val mFileName = "${fileName}.${LocalBook.parseFileSuffix(url)}"
|
||||
val isSupportedFile = AppPattern.bookFileRegex.matches(mFileName)
|
||||
webFileData?.add(WebFile(url, mFileName, isSupportedFile))
|
||||
}
|
||||
}
|
||||
|
||||
fun importOrDownloadWebFile(webFile: WebFile, success: ((Uri?) -> Unit)?) {
|
||||
bookSource ?: return
|
||||
execute {
|
||||
if (webFile.isSupported) {
|
||||
LocalBook.importFileOnLine(webFile.url, webFile.name, bookSource)
|
||||
} else {
|
||||
LocalBook.saveBookFile(webFile.url, webFile.name, bookSource)
|
||||
}
|
||||
}.onSuccess {
|
||||
success?.invoke(it as? Uri)
|
||||
}.onError {
|
||||
context.toastOnUi("ImportWebFileError\n${it.localizedMessage}")
|
||||
}
|
||||
}
|
||||
|
||||
fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) {
|
||||
changeSourceCoroutine?.cancel()
|
||||
changeSourceCoroutine = execute {
|
||||
@ -356,4 +407,14 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
||||
}
|
||||
}
|
||||
|
||||
data class WebFile(
|
||||
val url: String,
|
||||
val name: String,
|
||||
val isSupported: Boolean
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user