diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ab38d1d9..02b6da77d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co - Fixed app bars remaining visible after changing pages by tapping in the paged reader after using the chapter navigator slider ([@AntsyLich](https://github.com/AntsyLich)) ([#3567](https://github.com/mihonapp/mihon/pull/3567)) - Fixed MangaBaka User Agent string ([@MajorTanya](https://github.com/MajorTanya)) ([#3578](https://github.com/mihonapp/mihon/pull/3578)) - Fixed extension installation with shizuku installer ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken)) ([#3630](https://github.com/mihonapp/mihon/pull/3630)) +- Fixed backup restore dropping library entries when the backup contains duplicate chapters ([@na-ji](https://github.com/na-ji)) ([#3667](https://github.com/mihonapp/mihon/pull/3667)) ## [v0.20.1] - 2026-07-09 ### Added diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt index b38086513..ce21be407 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt @@ -148,13 +148,30 @@ class BackupRestorer( mangaRestorer.sortByNew(backupMangas) .chunked(100) .forEach { chunk -> - database.transaction { + val restoredAsBatch = try { + database.transaction { + chunk.forEach { + ensureActive() + mangaRestorer.restore(it, backupCategories) + } + } + true + } catch (e: Exception) { + ensureActive() + logcat(LogPriority.WARN, e) { "Batch restore failed, retrying entry by entry" } + false + } + + if (restoredAsBatch) { + restoreProgress.addAndFetch(chunk.size) + } else { chunk.forEach { ensureActive() try { mangaRestorer.restore(it, backupCategories) } catch (e: Exception) { + ensureActive() val sourceName = sourceMapping[it.source] ?: it.source.toString() errors.add(Date() to "${it.title} [$sourceName]: ${e.message}") } @@ -162,6 +179,7 @@ class BackupRestorer( restoreProgress.incrementAndFetch() } } + notifier.showRestoreProgress(chunk.last().title, restoreProgress.load(), restoreAmount, isSync) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt index 907d5959f..2e3af2a17 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt @@ -284,7 +284,7 @@ class MangaRestorer( restoreCategories(manga, categories, backupCategories) restoreChapters(manga, chapters) restoreTracking(manga, tracks) - restoreHistory(history) + restoreHistory(manga, history) restoreExcludedScanlators(manga, excludedScanlators) updateManga.awaitUpdateFetchInterval(manga, now, currentFetchWindow) return manga @@ -324,16 +324,16 @@ class MangaRestorer( } } - private suspend fun restoreHistory(backupHistory: List) { + private suspend fun restoreHistory(manga: Manga, backupHistory: List) { val toUpdate = backupHistory.mapNotNull { history -> val dbHistory = database.historyQueries - .getHistoryByChapterUrl(history.url) + .getHistoryByChapterUrlAndMangaId(history.url, manga.id) .awaitAsOneOrNull() val item = history.getHistoryImpl() if (dbHistory == null) { val chapter = database.chaptersQueries - .getChapterByUrl(history.url) + .getChapterByUrlAndMangaId(history.url, manga.id) .awaitAsOneOrNull() return@mapNotNull if (chapter == null) { // Chapter doesn't exist; skip diff --git a/data/src/main/sqldelight/tachiyomi/data/chapters.sq b/data/src/main/sqldelight/tachiyomi/data/chapters.sq index a1e6f3ab7..b6280be75 100644 --- a/data/src/main/sqldelight/tachiyomi/data/chapters.sq +++ b/data/src/main/sqldelight/tachiyomi/data/chapters.sq @@ -88,7 +88,8 @@ getChapterByUrlAndMangaId: SELECT * FROM chapters WHERE url = :chapterUrl -AND manga_id = :mangaId; +AND manga_id = :mangaId +LIMIT 1; removeChaptersWithIds: DELETE FROM chapters diff --git a/data/src/main/sqldelight/tachiyomi/data/history.sq b/data/src/main/sqldelight/tachiyomi/data/history.sq index e9c30907c..6cecd5784 100644 --- a/data/src/main/sqldelight/tachiyomi/data/history.sq +++ b/data/src/main/sqldelight/tachiyomi/data/history.sq @@ -23,7 +23,7 @@ JOIN chapters C ON H.chapter_id = C._id WHERE C.manga_id = :mangaId AND C._id = H.chapter_id; -getHistoryByChapterUrl: +getHistoryByChapterUrlAndMangaId: SELECT H._id, H.chapter_id, @@ -32,7 +32,8 @@ H.time_read FROM history H JOIN chapters C ON H.chapter_id = C._id -WHERE C.url = :chapterUrl AND C._id = H.chapter_id; +WHERE C.url = :chapterUrl AND C.manga_id = :mangaId AND C._id = H.chapter_id +LIMIT 1; resetHistoryById: UPDATE history