更新时检测webDav书籍是否比本地新,自动重新下载

pull/2380/head
kunfei 2 years ago
parent ed8d88ffd9
commit 4c53700bb1
  1. 19
      app/src/main/java/io/legado/app/data/entities/RemoteBook.kt
  2. 7
      app/src/main/java/io/legado/app/help/book/BookExtensions.kt
  3. 9
      app/src/main/java/io/legado/app/lib/webdav/WebDav.kt
  4. 31
      app/src/main/java/io/legado/app/model/remote/RemoteBook.kt
  5. 18
      app/src/main/java/io/legado/app/model/remote/RemoteBookManager.kt
  6. 33
      app/src/main/java/io/legado/app/model/remote/RemoteBookWebDav.kt
  7. 7
      app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
  8. 9
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  9. 2
      app/src/main/java/io/legado/app/ui/book/remote/RemoteBookActivity.kt
  10. 2
      app/src/main/java/io/legado/app/ui/book/remote/RemoteBookAdapter.kt
  11. 4
      app/src/main/java/io/legado/app/ui/book/remote/RemoteBookViewModel.kt

@ -1,19 +0,0 @@
package io.legado.app.data.entities
import com.android.tools.r8.Keep
@Keep
data class RemoteBook(
val filename: String,
val path: String,
val size: Long,
val contentType: String,
val lastModify: Long,
var isOnBookShelf: Boolean = false
) {
val isDir by lazy {
contentType == "folder"
}
}

@ -44,6 +44,13 @@ val Book.isOnLineTxt: Boolean
return !isLocal && type and BookType.text > 0 return !isLocal && type and BookType.text > 0
} }
fun Book.getRemoteUrl(): String? {
if (origin.startsWith(BookType.webDavTag)) {
return origin.substring(8)
}
return null
}
fun Book.upType() { fun Book.upType() {
if (type < 8) { if (type < 8) {
type = when (type) { type = when (type) {

@ -92,12 +92,11 @@ open class WebDav(val path: String, val authorization: Authorization) {
/** /**
* 获取当前url文件信息 * 获取当前url文件信息
*/ */
@Throws(WebDavException::class)
suspend fun getWebDavFile(): WebDavFile? { suspend fun getWebDavFile(): WebDavFile? {
return kotlin.runCatching { return propFindResponse(depth = 0)?.let {
propFindResponse(depth = 0)?.let { parseBody(it).firstOrNull()
return parseBody(it).firstOrNull() }
}
}.getOrNull()
} }
/** /**

@ -0,0 +1,31 @@
package io.legado.app.model.remote
import com.android.tools.r8.Keep
import io.legado.app.lib.webdav.WebDavFile
import io.legado.app.model.localBook.LocalBook
@Keep
data class RemoteBook(
val filename: String,
val path: String,
val size: Long,
val lastModify: Long,
var contentType: String = "folder",
var isOnBookShelf: Boolean = false
) {
val isDir get() = contentType == "folder"
constructor(webDavFile: WebDavFile) : this(
webDavFile.displayName,
webDavFile.path,
webDavFile.size,
webDavFile.lastModify
) {
if (!webDavFile.isDir) {
contentType = webDavFile.displayName.substringAfterLast(".")
isOnBookShelf = LocalBook.isOnBookShelf(webDavFile.displayName)
}
}
}

@ -2,7 +2,6 @@ package io.legado.app.model.remote
import android.net.Uri import android.net.Uri
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.RemoteBook
abstract class RemoteBookManager { abstract class RemoteBookManager {
protected val remoteBookFolder: String = "books" protected val remoteBookFolder: String = "books"
@ -15,6 +14,18 @@ abstract class RemoteBookManager {
@Throws(Exception::class) @Throws(Exception::class)
abstract suspend fun getRemoteBookList(path: String): MutableList<RemoteBook> abstract suspend fun getRemoteBookList(path: String): MutableList<RemoteBook>
/**
* 根据书籍地址获取书籍信息
*/
@Throws(Exception::class)
abstract suspend fun getRemoteBook(path: String): RemoteBook
/**
* @return Uri下载到本地的路径
*/
@Throws(Exception::class)
abstract suspend fun downloadRemoteBook(remoteBook: RemoteBook): Uri
/** /**
* 上传书籍 * 上传书籍
*/ */
@ -27,9 +38,4 @@ abstract class RemoteBookManager {
@Throws(Exception::class) @Throws(Exception::class)
abstract suspend fun delete(remoteBookUrl: String) abstract suspend fun delete(remoteBookUrl: String)
/**
* @return Uri下载到本地的路径
*/
@Throws(Exception::class)
abstract suspend fun getRemoteBook(remoteBook: RemoteBook): Uri
} }

@ -4,7 +4,6 @@ import android.net.Uri
import io.legado.app.constant.AppPattern.bookFileRegex import io.legado.app.constant.AppPattern.bookFileRegex
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.RemoteBook
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.AppWebDav import io.legado.app.help.AppWebDav
import io.legado.app.lib.webdav.WebDav import io.legado.app.lib.webdav.WebDav
@ -40,34 +39,24 @@ object RemoteBookWebDav : RemoteBookManager() {
val remoteWebDavFileList: List<WebDavFile> = WebDav(path, it).listFiles() val remoteWebDavFileList: List<WebDavFile> = WebDav(path, it).listFiles()
//转化远程文件信息到本地对象 //转化远程文件信息到本地对象
remoteWebDavFileList.forEach { webDavFile -> remoteWebDavFileList.forEach { webDavFile ->
if (webDavFile.isDir) { if (webDavFile.isDir || bookFileRegex.matches(webDavFile.displayName)) {
remoteBooks.add(
RemoteBook(
webDavFile.displayName, webDavFile.path, webDavFile.size,
"folder", webDavFile.lastModify
)
)
} else {
//分割后缀
val fileExtension = webDavFile.displayName.substringAfterLast(".")
//扩展名符合阅读的格式则认为是书籍 //扩展名符合阅读的格式则认为是书籍
if (bookFileRegex.matches(webDavFile.displayName)) { remoteBooks.add(RemoteBook(webDavFile))
val isOnBookShelf = LocalBook.isOnBookShelf(webDavFile.displayName)
remoteBooks.add(
RemoteBook(
webDavFile.displayName, webDavFile.path, webDavFile.size,
fileExtension, webDavFile.lastModify, isOnBookShelf
)
)
}
} }
} }
} ?: throw NoStackTraceException("webDav没有配置") } ?: throw NoStackTraceException("webDav没有配置")
return remoteBooks return remoteBooks
} }
override suspend fun getRemoteBook(remoteBook: RemoteBook): Uri { override suspend fun getRemoteBook(path: String): RemoteBook {
AppWebDav.authorization?.let {
val webDavFile = WebDav(path, it).getWebDavFile()
?: throw NoStackTraceException("远程书籍不存在")
return RemoteBook(webDavFile)
} ?: throw NoStackTraceException("webDav没有配置")
}
override suspend fun downloadRemoteBook(remoteBook: RemoteBook): Uri {
return AppWebDav.authorization?.let { return AppWebDav.authorization?.let {
val webdav = WebDav(remoteBook.path, it) val webdav = WebDav(remoteBook.path, it)
webdav.downloadInputStream().let { inputStream -> webdav.downloadInputStream().let { inputStream ->

@ -81,7 +81,7 @@ class BookInfoActivity :
private val readBookResult = registerForActivityResult( private val readBookResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult() ActivityResultContracts.StartActivityForResult()
) { ) {
viewModel.refreshData(intent) viewModel.upBook(intent)
if (it.resultCode == RESULT_OK) { if (it.resultCode == RESULT_OK) {
viewModel.inBookshelf = true viewModel.inBookshelf = true
upTvBookshelf() upTvBookshelf()
@ -162,10 +162,7 @@ class BookInfoActivity :
R.id.menu_refresh -> { R.id.menu_refresh -> {
upLoading(true) upLoading(true)
viewModel.bookData.value?.let { viewModel.bookData.value?.let {
if (it.isLocal) { viewModel.refreshBook(it)
it.tocUrl = ""
}
viewModel.loadBookInfo(it, false)
} }
} }
R.id.menu_login -> viewModel.bookSource?.let { R.id.menu_login -> viewModel.bookSource?.let {

@ -62,7 +62,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
} }
} }
fun refreshData(intent: Intent) { fun upBook(intent: Intent) {
execute { execute {
val name = intent.getStringExtra("name") ?: "" val name = intent.getStringExtra("name") ?: ""
val author = intent.getStringExtra("author") ?: "" val author = intent.getStringExtra("author") ?: ""
@ -107,6 +107,13 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
} }
} }
fun refreshBook(book: Book) {
if (book.isLocal) {
book.tocUrl = ""
}
loadBookInfo(book, false)
}
fun loadBookInfo( fun loadBookInfo(
book: Book, book: Book,
canReName: Boolean = true, canReName: Boolean = true,

@ -10,9 +10,9 @@ import androidx.core.view.isGone
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.data.entities.RemoteBook
import io.legado.app.databinding.ActivityImportBookBinding import io.legado.app.databinding.ActivityImportBookBinding
import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.backgroundColor
import io.legado.app.model.remote.RemoteBook
import io.legado.app.ui.about.AppLogDialog import io.legado.app.ui.about.AppLogDialog
import io.legado.app.ui.widget.SelectActionBar import io.legado.app.ui.widget.SelectActionBar
import io.legado.app.ui.widget.dialog.WaitDialog import io.legado.app.ui.widget.dialog.WaitDialog

@ -7,8 +7,8 @@ import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.data.entities.RemoteBook
import io.legado.app.databinding.ItemImportBookBinding import io.legado.app.databinding.ItemImportBookBinding
import io.legado.app.model.remote.RemoteBook
import io.legado.app.utils.ConvertUtils import io.legado.app.utils.ConvertUtils
import io.legado.app.utils.gone import io.legado.app.utils.gone
import io.legado.app.utils.invisible import io.legado.app.utils.invisible

@ -4,8 +4,8 @@ import android.app.Application
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.entities.RemoteBook
import io.legado.app.model.localBook.LocalBook import io.legado.app.model.localBook.LocalBook
import io.legado.app.model.remote.RemoteBook
import io.legado.app.model.remote.RemoteBookWebDav import io.legado.app.model.remote.RemoteBookWebDav
import io.legado.app.utils.toastOnUi import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -95,7 +95,7 @@ class RemoteBookViewModel(application: Application) : BaseViewModel(application)
fun addToBookshelf(remoteBooks: HashSet<RemoteBook>, finally: () -> Unit) { fun addToBookshelf(remoteBooks: HashSet<RemoteBook>, finally: () -> Unit) {
execute { execute {
remoteBooks.forEach { remoteBook -> remoteBooks.forEach { remoteBook ->
val downloadBookPath = RemoteBookWebDav.getRemoteBook(remoteBook) val downloadBookPath = RemoteBookWebDav.downloadRemoteBook(remoteBook)
downloadBookPath.let { downloadBookPath.let {
val localBook = LocalBook.importFile(it) val localBook = LocalBook.importFile(it)
localBook.origin = BookType.webDavTag + remoteBook.path localBook.origin = BookType.webDavTag + remoteBook.path

Loading…
Cancel
Save