From 553762fae8d47e40ef910e73d55c58bfbd8a057b Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 27 Sep 2026 22:55:41 +0600 Subject: [PATCH] Fix reading history being double counted Restore groups a backup's history entries by chapter before comparing them with the stored row, so a backup carrying one entry per copy of a duplicated chapter no longer adds its reading time more than once or moves the last read backwards. A removed history entry keeps its 0 instead of becoming NULL, a tracker's start date skips removed history, and the reader clears a session's start time before saving it, so overlapping saves can't count it twice. --- .../domain/track/interactor/AddTracks.kt | 5 +- .../tachiyomi/ui/reader/ReaderViewModel.kt | 2 +- .../data/backup/RestoreRepositoryImpl.kt | 46 ++++++++++--------- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt b/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt index 44dc760a0..e40e2ec6c 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt @@ -57,9 +57,8 @@ class AddTracks( if (track.startDate <= 0) { val firstReadChapterDate = getHistory.await(mangaId) - .sortedBy { it.readAt } - .firstOrNull() - ?.readAt + .mapNotNull { it.readAt?.takeIf { readAt -> readAt.time > 0 } } + .minOrNull() firstReadChapterDate?.let { val startDate = firstReadChapterDate.time.convertEpochMillisZone( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 2edd7c0f8..de3ffc51b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -629,9 +629,9 @@ class ReaderViewModel( val chapterId = readerChapter.chapter.id!! val endTime = Date() val sessionReadDuration = chapterReadStartTime?.let { endTime.time - it } ?: 0 + chapterReadStartTime = null upsertHistory.await(HistoryUpdate(chapterId, endTime, sessionReadDuration)) - chapterReadStartTime = null } } diff --git a/data/src/main/java/tachiyomi/data/backup/RestoreRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/backup/RestoreRepositoryImpl.kt index e1cbde202..ed13765fb 100644 --- a/data/src/main/java/tachiyomi/data/backup/RestoreRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/backup/RestoreRepositoryImpl.kt @@ -213,30 +213,32 @@ class RestoreRepositoryImpl( this.copy(id = 0L, mangaId = 0L, dateFetch = 0L, dateUpload = 0L) private suspend fun restoreHistory(manga: Manga, restoredHistory: List) { - val toUpdate = restoredHistory.mapNotNull { history -> - val dbHistory = database.historyQueries - .getHistoryByChapterUrlAndMangaId(history.chapterUrl, manga.id) - .awaitAsOneOrNull() - - if (dbHistory == null) { - val chapter = database.chaptersQueries - .getChapterByUrlAndMangaId(history.chapterUrl, manga.id) + val toUpdate = restoredHistory + .groupBy { it.chapterUrl } + .mapNotNull { (chapterUrl, copies) -> + val readAt = copies.maxOf { it.readAt?.time ?: 0L } + val readDuration = copies.sumOf { it.readDuration } + val dbHistory = database.historyQueries + .getHistoryByChapterUrlAndMangaId(chapterUrl, manga.id) .awaitAsOneOrNull() - // Chapter doesn't exist; skip - ?: return@mapNotNull null - // New history entry - return@mapNotNull Triple(chapter._id, history.readAt, history.readDuration) - } - // Update history entry - Triple( - dbHistory.chapter_id, - max(history.readAt?.time ?: 0L, dbHistory.last_read?.time ?: 0L) - .takeIf { it > 0L } - ?.let { Date(it) }, - max(history.readDuration, dbHistory.time_read) - dbHistory.time_read, - ) - } + if (dbHistory == null) { + val chapter = database.chaptersQueries + .getChapterByUrlAndMangaId(chapterUrl, manga.id) + .awaitAsOneOrNull() + // Chapter doesn't exist; skip + ?: return@mapNotNull null + // New history entry + return@mapNotNull Triple(chapter._id, Date(readAt), readDuration) + } + + // Update history entry + Triple( + dbHistory.chapter_id, + Date(max(readAt, dbHistory.last_read?.time ?: 0L)), + max(readDuration, dbHistory.time_read) - dbHistory.time_read, + ) + } toUpdate.forEach { (chapterId, readAt, readDuration) -> database.historyQueries.upsert(chapterId, readAt, readDuration)