refactor: composite keys.

Use composite keys for better matching, there was mismatch before and better manga categories handling.

Added more logs as well.

Signed-off-by: KaiserBh <kaiserbh@proton.me>
This commit is contained in:
KaiserBh 2024-01-10 03:47:35 +11:00
parent 3c95d3aefc
commit f3c2c13f49
No known key found for this signature in database
GPG Key ID: 14D73B142042BBA9
2 changed files with 129 additions and 31 deletions

View File

@ -17,7 +17,7 @@ import eu.kanade.tachiyomi.data.sync.service.SyncYomiSyncService
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat import logcat.logcat
import tachiyomi.data.Chapters import tachiyomi.data.Chapters
import tachiyomi.data.DatabaseHandler import tachiyomi.data.DatabaseHandler
import tachiyomi.data.manga.MangaMapper.mapManga import tachiyomi.data.manga.MangaMapper.mapManga
@ -119,6 +119,13 @@ class SyncManager(
val remoteBackup = syncService?.doSync(syncData) val remoteBackup = syncService?.doSync(syncData)
// Check if it's first sync based on lastSyncTimestamp
if (syncPreferences.lastSyncTimestamp().get() == 0L && databaseManga.isNotEmpty()) {
// It's first sync no need to restore data. (just update remote data)
syncPreferences.lastSyncTimestamp().set(Date().time)
return
}
if (remoteBackup != null) { if (remoteBackup != null) {
val (filteredFavorites, nonFavorites) = filterFavoritesAndNonFavorites(remoteBackup) val (filteredFavorites, nonFavorites) = filterFavoritesAndNonFavorites(remoteBackup)
updateNonFavorites(nonFavorites) updateNonFavorites(nonFavorites)
@ -187,24 +194,66 @@ class SyncManager(
private suspend fun isMangaDifferent(localManga: Manga, remoteManga: BackupManga): Boolean { private suspend fun isMangaDifferent(localManga: Manga, remoteManga: BackupManga): Boolean {
val localChapters = handler.await { chaptersQueries.getChaptersByMangaId(localManga.id, 0).executeAsList() } val localChapters = handler.await { chaptersQueries.getChaptersByMangaId(localManga.id, 0).executeAsList() }
val localCategories = getCategories.await(localManga.id).map { it.order } val localCategories = getCategories.await(localManga.id).map { it.order }
val logTag = "isMangaDifferent"
return localManga.source != remoteManga.source || // Logging the start of comparison
localManga.url != remoteManga.url || logcat(LogPriority.DEBUG, logTag) {
localManga.title != remoteManga.title || "Comparing local manga (Title: ${localManga.title}) with remote manga (Title: ${remoteManga.title})"
localManga.status.toInt() != remoteManga.status || }
localManga.thumbnailUrl != remoteManga.thumbnailUrl ||
localManga.dateAdded != remoteManga.dateAdded || var isDifferent = false
localManga.chapterFlags.toInt() != remoteManga.chapterFlags ||
localManga.favorite != remoteManga.favorite || // Compare each field and log if they are different
localManga.viewerFlags.toInt() != remoteManga.viewer_flags || if (localManga.source != remoteManga.source) {
localManga.updateStrategy != remoteManga.updateStrategy || logDifference(logTag, "Source", localManga.source.toString(), remoteManga.source.toString())
areChaptersDifferent(localChapters, remoteManga.chapters) || isDifferent = true
localCategories != remoteManga.categories }
if (localManga.url != remoteManga.url) {
logDifference(logTag, "URL", localManga.url, remoteManga.url)
isDifferent = true
}
if (localManga.favorite != remoteManga.favorite) {
logDifference(logTag, "Favorite", localManga.favorite.toString(), remoteManga.favorite.toString())
isDifferent = true
}
if (areChaptersDifferent(localChapters, remoteManga.chapters)) {
logcat(LogPriority.DEBUG, logTag) {
"Chapters are different for manga: ${localManga.title}"
}
isDifferent = true
}
if (localCategories.toSet() != remoteManga.categories.toSet()) {
logcat(LogPriority.DEBUG, logTag) {
"Categories differ for manga: ${localManga.title}. " +
"Local categories: ${localCategories.joinToString()}, " +
"Remote categories: ${remoteManga.categories.joinToString()}"
}
isDifferent = true
}
// Log final result
logcat(LogPriority.DEBUG, logTag) {
"Manga difference check result for local manga (Title: ${localManga.title}): $isDifferent"
}
return isDifferent
}
private fun logDifference(tag: String, field: String, localValue: String, remoteValue: String) {
logcat(LogPriority.DEBUG, tag) {
"Difference found in $field. Local: $localValue, Remote: $remoteValue"
}
} }
private fun areChaptersDifferent(localChapters: List<Chapters>, remoteChapters: List<BackupChapter>): Boolean { private fun areChaptersDifferent(localChapters: List<Chapters>, remoteChapters: List<BackupChapter>): Boolean {
val logTag = "areChaptersDifferent"
// Early return if the sizes are different // Early return if the sizes are different
if (localChapters.size != remoteChapters.size) { if (localChapters.size != remoteChapters.size) {
logcat(LogPriority.DEBUG, logTag) {
"Chapter lists differ in size. Local: ${localChapters.size}, Remote: ${remoteChapters.size}"
}
return true return true
} }
@ -214,16 +263,60 @@ class SyncManager(
// Create a map for the local chapters for efficient comparison // Create a map for the local chapters for efficient comparison
val localChapterMap = localChapters.associateBy { it.url } val localChapterMap = localChapters.associateBy { it.url }
var isDifferent = false
// Check for any differences // Check for any differences
return convertedRemoteChapters.any { remoteChapter -> for (remoteChapter in convertedRemoteChapters) {
val localChapter = localChapterMap[remoteChapter.url] val localChapter = localChapterMap[remoteChapter.url]
localChapter == null || // No corresponding local chapter
localChapter.url != remoteChapter.url || if (localChapter == null) {
localChapter.read != remoteChapter.read || logcat(LogPriority.DEBUG, logTag) {
localChapter.bookmark != remoteChapter.bookmark || "Local chapter not found for URL: ${remoteChapter.url}"
localChapter.last_page_read != remoteChapter.lastPageRead ||
localChapter.chapter_number != remoteChapter.chapterNumber
} }
isDifferent = true
continue
}
if (localChapter.url != remoteChapter.url) {
logDifference(logTag, "URL", localChapter.url, remoteChapter.url)
isDifferent = true
}
if (localChapter.read != remoteChapter.read) {
logDifference(logTag, "Read Status", localChapter.read.toString(), remoteChapter.read.toString())
isDifferent = true
}
if (localChapter.bookmark != remoteChapter.bookmark) {
logDifference(
logTag,
"Bookmark Status",
localChapter.bookmark.toString(),
remoteChapter.bookmark.toString(),
)
isDifferent = true
}
if (localChapter.last_page_read != remoteChapter.lastPageRead) {
logDifference(
logTag,
"Last Page Read",
localChapter.last_page_read.toString(),
remoteChapter.lastPageRead.toString(),
)
isDifferent = true
}
// Break the loop if a difference is found
if (isDifferent) break
}
if (!isDifferent) {
logcat(LogPriority.DEBUG, logTag) {
"No differences found in chapters."
}
}
return isDifferent
} }
/** /**
@ -234,27 +327,31 @@ class SyncManager(
private suspend fun filterFavoritesAndNonFavorites(backup: Backup): Pair<List<BackupManga>, List<BackupManga>> { private suspend fun filterFavoritesAndNonFavorites(backup: Backup): Pair<List<BackupManga>, List<BackupManga>> {
val favorites = mutableListOf<BackupManga>() val favorites = mutableListOf<BackupManga>()
val nonFavorites = mutableListOf<BackupManga>() val nonFavorites = mutableListOf<BackupManga>()
val logTag = "filterFavoritesAndNonFavorites"
val elapsedTimeMillis = measureTimeMillis { val elapsedTimeMillis = measureTimeMillis {
val databaseMangaFavorites = getFavorites.await() val databaseMangaFavorites = getFavorites.await()
val localMangaMap = databaseMangaFavorites.associateBy { it.url } val localMangaMap = databaseMangaFavorites.associateBy {
Triple(it.source, it.url, it.title)
}
logcat(LogPriority.DEBUG) { "Starting to filter favorites and non-favorites from backup data." } logcat(LogPriority.DEBUG, logTag) { "Starting to filter favorites and non-favorites from backup data." }
backup.backupManga.forEach { remoteManga -> backup.backupManga.forEach { remoteManga ->
val localManga = localMangaMap[remoteManga.url] val compositeKey = Triple(remoteManga.source, remoteManga.url, remoteManga.title)
val localManga = localMangaMap[compositeKey]
when { when {
// Checks if the manga is in favorites and needs updating or adding // Checks if the manga is in favorites and needs updating or adding
remoteManga.favorite -> { remoteManga.favorite -> {
if (localManga == null || isMangaDifferent(localManga, remoteManga)) { if (localManga == null || isMangaDifferent(localManga, remoteManga)) {
logcat(LogPriority.DEBUG) { "Adding to favorites: ${remoteManga.title}" } logcat(LogPriority.DEBUG, logTag) { "Adding to favorites: ${remoteManga.title}" }
favorites.add(remoteManga) favorites.add(remoteManga)
} else { } else {
logcat(LogPriority.DEBUG) { "Already up-to-date favorite: ${remoteManga.title}" } logcat(LogPriority.DEBUG, logTag) { "Already up-to-date favorite: ${remoteManga.title}" }
} }
} }
// Handle non-favorites // Handle non-favorites
!remoteManga.favorite -> { !remoteManga.favorite -> {
logcat(LogPriority.DEBUG) { "Adding to non-favorites: ${remoteManga.title}" } logcat(LogPriority.DEBUG, logTag) { "Adding to non-favorites: ${remoteManga.title}" }
nonFavorites.add(remoteManga) nonFavorites.add(remoteManga)
} }
} }
@ -263,7 +360,7 @@ class SyncManager(
val minutes = elapsedTimeMillis / 60000 val minutes = elapsedTimeMillis / 60000
val seconds = (elapsedTimeMillis % 60000) / 1000 val seconds = (elapsedTimeMillis % 60000) / 1000
logcat(LogPriority.DEBUG) { logcat(LogPriority.DEBUG, logTag) {
"Filtering completed in ${minutes}m ${seconds}s. Favorites found: ${favorites.size}, " + "Filtering completed in ${minutes}m ${seconds}s. Favorites found: ${favorites.size}, " +
"Non-favorites found: ${nonFavorites.size}" "Non-favorites found: ${nonFavorites.size}"
} }
@ -309,7 +406,7 @@ class SyncManager(
if (updated) { if (updated) {
logcat(LogPriority.DEBUG) { logcat(LogPriority.DEBUG) {
"Chapter ${chapter.name} of ${manga.title} updated after last sync " + "Chapter ${chapter.name} of ${manga.title} updated after last sync " +
"(Chapter Last Modified: ${chapter.lastModifiedAt}, Last Sync: $lastSyncTimeStamp)." "(Chapter Last Modified: ${chapter.lastModifiedAt * 1000L}, Last Sync: $lastSyncTimeStamp)."
} }
} }
updated updated

View File

@ -100,7 +100,7 @@ abstract class SyncService(
val localMangaListSafe = localMangaList.orEmpty() val localMangaListSafe = localMangaList.orEmpty()
val remoteMangaListSafe = remoteMangaList.orEmpty() val remoteMangaListSafe = remoteMangaList.orEmpty()
logcat(logTag, LogPriority.DEBUG) { logcat(LogPriority.DEBUG, logTag) {
"Starting merge. Local list size: ${localMangaListSafe.size}, Remote list size: ${remoteMangaListSafe.size}" "Starting merge. Local list size: ${localMangaListSafe.size}, Remote list size: ${remoteMangaListSafe.size}"
} }
@ -144,8 +144,9 @@ abstract class SyncService(
} }
local != null && remote != null -> { local != null && remote != null -> {
logcat(LogPriority.DEBUG, logTag) { logcat(LogPriority.DEBUG, logTag) {
"Inspecting timestamps for ${local.title}. Local lastModifiedAt: ${local.lastModifiedAt}, " + "Inspecting timestamps for ${local.title}. " +
"Remote lastModifiedAt: ${remote.lastModifiedAt}" "Local lastModifiedAt: ${local.lastModifiedAt * 1000L}, " +
"Remote lastModifiedAt: ${remote.lastModifiedAt * 1000L}"
} }
// Convert seconds to milliseconds for accurate time comparison // Convert seconds to milliseconds for accurate time comparison
val localTime = Instant.ofEpochMilli(local.lastModifiedAt * 1000L) val localTime = Instant.ofEpochMilli(local.lastModifiedAt * 1000L)