Przeglądaj źródła

[修复] bookUrl 变更导致本地书裂变/误合并问题

Kudomaga 8 miesięcy temu
rodzic
commit
3ca7c9f7e3

+ 60 - 9
app/src/main/java/io/legado/app/help/book/BookExtensions.kt

@@ -121,6 +121,29 @@ fun Book.contains(word: String?): Boolean {
             || intro?.contains(word) == true
 }
 
+/**
+ * 仅在目标bookUrl未被其他书占用,或判定为同一本书时,允许迁移主键。
+ */
+fun Book.canSafelyRebindTo(newBookUrl: String): Boolean {
+    if (newBookUrl == bookUrl) return true
+    val targetBook = appDb.bookDao.getBook(newBookUrl) ?: return true
+
+    val sameOriginName = originName.isNotBlank() && originName == targetBook.originName
+    val sameNameAuthor = name.isNotBlank() && author.isNotBlank()
+            && name == targetBook.name && author == targetBook.author
+    val sameOrigin = origin.isNotBlank() && origin == targetBook.origin
+    val canMerge = sameOriginName && (sameNameAuthor || sameOrigin)
+    if (!canMerge) {
+        AppLog.put(
+            "书籍重定位冲突,已跳过迁移\n" +
+                    "old=$bookUrl\nnew=$newBookUrl\n" +
+                    "oldName=$name oldAuthor=$author oldOriginName=$originName\n" +
+                    "targetName=${targetBook.name} targetAuthor=${targetBook.author} targetOriginName=${targetBook.originName}"
+        )
+    }
+    return canMerge
+}
+
 private val localUriCache by lazy {
     ConcurrentHashMap<String, Uri>()
 }
@@ -150,17 +173,31 @@ fun Book.getLocalUri(): Uri {
 
     // 查找书籍保存目录
     if (!defaultBookDir.isNullOrBlank()) {
-        val treeUri = Uri.parse(defaultBookDir)
+        val treeUri = defaultBookDir.toUri()
         val treeFileDoc = FileDoc.fromUri(treeUri, true)
+
         if (!treeFileDoc.exists()) {
             appCtx.toastOnUi("书籍保存目录失效,请重新设置!")
         } else {
             val fileDoc = treeFileDoc.find(originName, 5, 100)
             if (fileDoc != null) {
-                localUriCache[bookUrl] = fileDoc.uri
-                //更新bookUrl 重启不用再找一遍
-                bookUrl = fileDoc.toString()
-                save()
+                val newBookUrl = fileDoc.toString()
+                val oldBook = copy()
+                if (!oldBook.canSafelyRebindTo(newBookUrl)) {
+                    return fileDoc.uri
+                }
+                appDb.runInTransaction {
+
+                    if (oldBook.bookUrl == newBookUrl) {
+                        save()
+                    } else {
+                        val newBook = oldBook.copy(bookUrl = newBookUrl)
+                        appDb.bookDao.replace(oldBook, newBook)
+                        BookHelp.updateCacheFolder(oldBook, newBook)
+                        this.bookUrl = newBookUrl
+                    }
+                }
+                localUriCache[newBookUrl] = fileDoc.uri
                 return fileDoc.uri
             }
         }
@@ -176,9 +213,23 @@ fun Book.getLocalUri(): Uri {
         val treeFileDoc = FileDoc.fromUri(treeUri, true)
         val fileDoc = treeFileDoc.find(originName, 5, 100)
         if (fileDoc != null) {
-            localUriCache[bookUrl] = fileDoc.uri
-            bookUrl = fileDoc.toString()
-            save()
+            val newBookUrl = fileDoc.toString()
+            val oldBook = copy()
+            if (!oldBook.canSafelyRebindTo(newBookUrl)) {
+                return fileDoc.uri
+            }
+
+            appDb.runInTransaction {
+                if (oldBook.bookUrl == newBookUrl) {
+                    save()
+                } else {
+                    val newBook = oldBook.copy(bookUrl = newBookUrl)
+                    appDb.bookDao.replace(oldBook, newBook)
+                    BookHelp.updateCacheFolder(oldBook, newBook)
+                    this.bookUrl = newBookUrl
+                }
+            }
+            localUriCache[newBookUrl] = fileDoc.uri
             return fileDoc.uri
         }
     }
@@ -438,4 +489,4 @@ fun tryParesExportFileName(jsStr: String): Boolean {
         RhinoScriptEngine.eval(jsStr, bindings)
         true
     }.getOrDefault(false)
-}
+}

+ 30 - 5
app/src/main/java/io/legado/app/model/localBook/LocalBook.kt

@@ -23,6 +23,8 @@ import io.legado.app.help.book.BookHelp
 import io.legado.app.help.book.ContentProcessor
 import io.legado.app.help.book.addType
 import io.legado.app.help.book.archiveName
+import io.legado.app.help.book.cacheLocalUri
+import io.legado.app.help.book.canSafelyRebindTo
 import io.legado.app.help.book.getArchiveUri
 import io.legado.app.help.book.getLocalUri
 import io.legado.app.help.book.getRemoteUrl
@@ -37,6 +39,7 @@ import io.legado.app.help.config.AppConfig
 import io.legado.app.lib.webdav.WebDav
 import io.legado.app.lib.webdav.WebDavException
 import io.legado.app.model.analyzeRule.AnalyzeUrl
+import io.legado.app.model.analyzeRule.CustomUrl
 import io.legado.app.utils.ArchiveUtils
 import io.legado.app.utils.FileDoc
 import io.legado.app.utils.FileUtils
@@ -50,6 +53,7 @@ import io.legado.app.utils.isAbsUrl
 import io.legado.app.utils.isContentScheme
 import io.legado.app.utils.isDataUrl
 import io.legado.app.utils.printOnDebug
+import kotlinx.coroutines.currentCoroutineContext
 import kotlinx.coroutines.runBlocking
 import org.apache.commons.text.StringEscapeUtils
 import splitties.init.appCtx
@@ -60,7 +64,6 @@ import java.io.FileNotFoundException
 import java.io.FileOutputStream
 import java.io.InputStream
 import java.util.regex.Pattern
-import kotlin.coroutines.coroutineContext
 
 /**
  * 书籍文件导入 目录正文解析
@@ -403,7 +406,7 @@ object LocalBook {
         val inputStream = when {
             str.isAbsUrl() -> AnalyzeUrl(
                 str, source = source, callTimeout = 0,
-                coroutineContext = coroutineContext
+                coroutineContext = currentCoroutineContext()
             ).getInputStreamAwait()
 
             str.isDataUrl() -> ByteArrayInputStream(
@@ -458,7 +461,7 @@ object LocalBook {
     fun isOnBookShelf(
         fileName: String
     ): Boolean {
-        return appDb.bookDao.hasFile(fileName) == true
+        return appDb.bookDao.hasFile(fileName)
     }
 
     //文件类书源 合并在线书籍信息 在线 > 本地
@@ -501,9 +504,31 @@ object LocalBook {
                     localBook.bookUrl = newBook.bookUrl
                 } else {
                     // txt epub pdf umd
+                    val oldBook = localBook.copy()
                     val fileUri = saveBookFile(it, localBook.originName)
-                    localBook.bookUrl = FileDoc.fromUri(fileUri, false).toString()
-                    localBook.save()
+                    val newBookUrl = FileDoc.fromUri(fileUri, false).toString()
+                    if (!oldBook.canSafelyRebindTo(newBookUrl)) {
+                        localBook.cacheLocalUri(fileUri)
+                        return true
+                    }
+
+                    appDb.runInTransaction {
+                        if (oldBook.bookUrl == newBookUrl) {
+                            localBook.origin =
+                                BookType.webDavTag + CustomUrl(webDavUrl).toString()
+
+                            localBook.save()
+                        } else {
+                            val newBook = oldBook.copy(
+                                bookUrl = newBookUrl,
+                                origin = BookType.webDavTag + CustomUrl(webDavUrl).toString()
+                            )
+                            appDb.bookDao.replace(oldBook, newBook)
+                            BookHelp.updateCacheFolder(oldBook, newBook)
+                            localBook.bookUrl = newBookUrl
+                            localBook.origin = newBook.origin
+                        }
+                    }
                 }
             }
             return true