优化进度同步逻辑,减少同步次数

This commit is contained in:
kunfei
2023-08-02 00:22:19 +08:00
parent 3f46ccb3ae
commit 577ca2ad40
9 changed files with 1992 additions and 56 deletions
File diff suppressed because it is too large Load Diff
@@ -13,6 +13,7 @@ import com.google.gson.Gson
import io.legado.app.api.controller.BookController import io.legado.app.api.controller.BookController
import io.legado.app.api.controller.BookSourceController import io.legado.app.api.controller.BookSourceController
import io.legado.app.api.controller.RssSourceController import io.legado.app.api.controller.RssSourceController
import kotlinx.coroutines.runBlocking
/** /**
* Export book data to other app. * Export book data to other app.
@@ -76,29 +77,37 @@ class ReaderProvider : ContentProvider() {
override fun insert(uri: Uri, values: ContentValues?): Uri? { override fun insert(uri: Uri, values: ContentValues?): Uri? {
if (sMatcher.match(uri) < 0) return null if (sMatcher.match(uri) < 0) return null
runBlocking {
when (RequestCode.values()[sMatcher.match(uri)]) { when (RequestCode.values()[sMatcher.match(uri)]) {
RequestCode.SaveBookSource -> values?.let { RequestCode.SaveBookSource -> values?.let {
BookSourceController.saveSource(values.getAsString(postBodyKey)) BookSourceController.saveSource(values.getAsString(postBodyKey))
} }
RequestCode.SaveBookSources -> values?.let { RequestCode.SaveBookSources -> values?.let {
BookSourceController.saveSources(values.getAsString(postBodyKey)) BookSourceController.saveSources(values.getAsString(postBodyKey))
} }
RequestCode.SaveRssSource -> values?.let { RequestCode.SaveRssSource -> values?.let {
RssSourceController.saveSource(values.getAsString(postBodyKey)) RssSourceController.saveSource(values.getAsString(postBodyKey))
} }
RequestCode.SaveRssSources -> values?.let { RequestCode.SaveRssSources -> values?.let {
RssSourceController.saveSources(values.getAsString(postBodyKey)) RssSourceController.saveSources(values.getAsString(postBodyKey))
} }
RequestCode.SaveBook -> values?.let { RequestCode.SaveBook -> values?.let {
BookController.saveBook(values.getAsString(postBodyKey)) BookController.saveBook(values.getAsString(postBodyKey))
} }
RequestCode.SaveBookProgress -> values?.let { RequestCode.SaveBookProgress -> values?.let {
BookController.saveBookProgress(values.getAsString(postBodyKey)) BookController.saveBookProgress(values.getAsString(postBodyKey))
} }
else -> throw IllegalStateException( else -> throw IllegalStateException(
"Unexpected value: " + RequestCode.values()[sMatcher.match(uri)].name "Unexpected value: " + RequestCode.values()[sMatcher.match(uri)].name
) )
} }
}
return null return null
} }
@@ -204,8 +204,8 @@ object BookController {
fun saveBook(postData: String?): ReturnData { fun saveBook(postData: String?): ReturnData {
val returnData = ReturnData() val returnData = ReturnData()
GSON.fromJsonObject<Book>(postData).getOrNull()?.let { book -> GSON.fromJsonObject<Book>(postData).getOrNull()?.let { book ->
book.save()
AppWebDav.uploadBookProgress(book) AppWebDav.uploadBookProgress(book)
book.save()
return returnData.setData("") return returnData.setData("")
} }
return returnData.setErrorMsg("格式不对") return returnData.setErrorMsg("格式不对")
@@ -226,7 +226,7 @@ object BookController {
/** /**
* 保存进度 * 保存进度
*/ */
fun saveBookProgress(postData: String?): ReturnData { suspend fun saveBookProgress(postData: String?): ReturnData {
val returnData = ReturnData() val returnData = ReturnData()
GSON.fromJsonObject<BookProgress>(postData) GSON.fromJsonObject<BookProgress>(postData)
.onFailure { it.printOnDebug() } .onFailure { it.printOnDebug() }
@@ -236,8 +236,10 @@ object BookController {
book.durChapterPos = bookProgress.durChapterPos book.durChapterPos = bookProgress.durChapterPos
book.durChapterTitle = bookProgress.durChapterTitle book.durChapterTitle = bookProgress.durChapterTitle
book.durChapterTime = bookProgress.durChapterTime book.durChapterTime = bookProgress.durChapterTime
AppWebDav.uploadBookProgress(bookProgress) {
book.syncTime = System.currentTimeMillis()
}
appDb.bookDao.update(book) appDb.bookDao.update(book)
AppWebDav.uploadBookProgress(bookProgress)
ReadBook.book?.let { ReadBook.book?.let {
if (it.name == bookProgress.name && if (it.name == bookProgress.name &&
it.author == bookProgress.author it.author == bookProgress.author
@@ -24,7 +24,7 @@ val appDb by lazy {
} }
@Database( @Database(
version = 70, version = 71,
exportSchema = true, exportSchema = true,
entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class, entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class,
ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class, ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class,
@@ -58,7 +58,8 @@ val appDb by lazy {
AutoMigration(from = 66, to = 67), AutoMigration(from = 66, to = 67),
AutoMigration(from = 67, to = 68), AutoMigration(from = 67, to = 68),
AutoMigration(from = 68, to = 69), AutoMigration(from = 68, to = 69),
AutoMigration(from = 69, to = 70) AutoMigration(from = 69, to = 70),
AutoMigration(from = 70, to = 71),
] ]
) )
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
@@ -90,6 +90,7 @@ data class Book(
// 最近一次阅读书籍的时间(打开正文的时间) // 最近一次阅读书籍的时间(打开正文的时间)
@ColumnInfo(defaultValue = "0") @ColumnInfo(defaultValue = "0")
var durChapterTime: Long = System.currentTimeMillis(), var durChapterTime: Long = System.currentTimeMillis(),
//字数
override var wordCount: String? = null, override var wordCount: String? = null,
// 刷新书架时更新书籍信息 // 刷新书架时更新书籍信息
@ColumnInfo(defaultValue = "1") @ColumnInfo(defaultValue = "1")
@@ -102,7 +103,11 @@ data class Book(
var originOrder: Int = 0, var originOrder: Int = 0,
// 自定义书籍变量信息(用于书源规则检索书籍信息) // 自定义书籍变量信息(用于书源规则检索书籍信息)
override var variable: String? = null, override var variable: String? = null,
var readConfig: ReadConfig? = null //阅读设置
var readConfig: ReadConfig? = null,
//同步时间
@ColumnInfo(defaultValue = "0")
var syncTime: Long = 0L
) : Parcelable, BaseBook { ) : Parcelable, BaseBook {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@@ -211,21 +211,23 @@ object AppWebDav {
val json = GSON.toJson(bookProgress) val json = GSON.toJson(bookProgress)
val url = getProgressUrl(book.name, book.author) val url = getProgressUrl(book.name, book.author)
WebDav(url, authorization).upload(json.toByteArray(), "application/json") WebDav(url, authorization).upload(json.toByteArray(), "application/json")
book.syncTime = System.currentTimeMillis()
}.onError { }.onError {
AppLog.put("上传进度失败\n${it.localizedMessage}", it) AppLog.put("上传进度失败\n${it.localizedMessage}", it)
} }
} }
fun uploadBookProgress(bookProgress: BookProgress) { suspend fun uploadBookProgress(bookProgress: BookProgress, onSuccess: (() -> Unit)? = null) {
try {
val authorization = authorization ?: return val authorization = authorization ?: return
if (!AppConfig.syncBookProgress) return if (!AppConfig.syncBookProgress) return
if (!NetworkUtils.isAvailable()) return if (!NetworkUtils.isAvailable()) return
Coroutine.async {
val json = GSON.toJson(bookProgress) val json = GSON.toJson(bookProgress)
val url = getProgressUrl(bookProgress.name, bookProgress.author) val url = getProgressUrl(bookProgress.name, bookProgress.author)
WebDav(url, authorization).upload(json.toByteArray(), "application/json") WebDav(url, authorization).upload(json.toByteArray(), "application/json")
}.onError { onSuccess?.invoke()
AppLog.put("上传进度失败\n${it.localizedMessage}", it) } catch (e: Exception) {
AppLog.put("上传进度失败\n${e.localizedMessage}", e)
} }
} }
@@ -260,12 +262,19 @@ object AppWebDav {
suspend fun downloadAllBookProgress() { suspend fun downloadAllBookProgress() {
val authorization = authorization ?: return val authorization = authorization ?: return
if (!NetworkUtils.isAvailable()) return if (!NetworkUtils.isAvailable()) return
val bookProgressFiles = WebDav(bookProgressUrl, authorization).listFiles().map { val bookProgressFiles = WebDav(bookProgressUrl, authorization).listFiles()
it.displayName val map = hashMapOf<String, WebDavFile>()
}.toHashSet() bookProgressFiles.forEach {
map[it.displayName] = it
}
appDb.bookDao.all.forEach { book -> appDb.bookDao.all.forEach { book ->
val progressFileName = getProgressFileName(book.name, book.author) val progressFileName = getProgressFileName(book.name, book.author)
if (!bookProgressFiles.contains(progressFileName)) return@forEach val webDavFile = map[progressFileName]
webDavFile ?: return
if (webDavFile.lastModify <= book.syncTime) {
//本地同步时间大于上传时间不用同步
return
}
getBookProgress(book)?.let { bookProgress -> getBookProgress(book)?.let { bookProgress ->
if (bookProgress.durChapterIndex > book.durChapterIndex if (bookProgress.durChapterIndex > book.durChapterIndex
|| (bookProgress.durChapterIndex == book.durChapterIndex || (bookProgress.durChapterIndex == book.durChapterIndex
@@ -275,6 +284,7 @@ object AppWebDav {
book.durChapterPos = bookProgress.durChapterPos book.durChapterPos = bookProgress.durChapterPos
book.durChapterTitle = bookProgress.durChapterTitle book.durChapterTitle = bookProgress.durChapterTitle
book.durChapterTime = bookProgress.durChapterTime book.durChapterTime = bookProgress.durChapterTime
book.syncTime = System.currentTimeMillis()
appDb.bookDao.update(book) appDb.bookDao.update(book)
} }
} }
@@ -140,6 +140,7 @@ object ReadBook : CoroutineScope by MainScope() {
book?.let { book?.let {
Coroutine.async { Coroutine.async {
AppWebDav.uploadBookProgress(it) AppWebDav.uploadBookProgress(it)
it.save()
} }
} }
} }
@@ -1312,6 +1312,7 @@ class ReadBookActivity : BaseReadBookActivity(),
delay(300000) delay(300000)
ReadBook.book?.let { ReadBook.book?.let {
AppWebDav.uploadBookProgress(it) AppWebDav.uploadBookProgress(it)
it.save()
Backup.autoBack(this@ReadBookActivity) Backup.autoBack(this@ReadBookActivity)
} }
} }
@@ -9,11 +9,18 @@ import io.legado.app.api.controller.ReplaceRuleController
import io.legado.app.api.controller.RssSourceController import io.legado.app.api.controller.RssSourceController
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.service.WebService import io.legado.app.service.WebService
import io.legado.app.utils.* import io.legado.app.utils.GSON
import io.legado.app.web.utils.AssetsWeb import io.legado.app.web.utils.AssetsWeb
import kotlinx.coroutines.runBlocking
import okio.Pipe import okio.Pipe
import okio.buffer import okio.buffer
import java.io.* import java.io.BufferedWriter
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.OutputStreamWriter
import kotlin.collections.HashMap
import kotlin.collections.List
import kotlin.collections.set
class HttpServer(port: Int) : NanoHTTPD(port) { class HttpServer(port: Int) : NanoHTTPD(port) {
private val assetsWeb = AssetsWeb("web") private val assetsWeb = AssetsWeb("web")
@@ -41,7 +48,8 @@ class HttpServer(port: Int) : NanoHTTPD(port) {
session.parseBody(files) session.parseBody(files)
val postData = files["postData"] val postData = files["postData"]
returnData = when (uri) { returnData = runBlocking {
when (uri) {
"/saveBookSource" -> BookSourceController.saveSource(postData) "/saveBookSource" -> BookSourceController.saveSource(postData)
"/saveBookSources" -> BookSourceController.saveSources(postData) "/saveBookSources" -> BookSourceController.saveSources(postData)
"/deleteBookSources" -> BookSourceController.deleteSources(postData) "/deleteBookSources" -> BookSourceController.deleteSources(postData)
@@ -59,6 +67,7 @@ class HttpServer(port: Int) : NanoHTTPD(port) {
else -> null else -> null
} }
} }
}
Method.GET -> { Method.GET -> {
val parameters = session.parameters val parameters = session.parameters