Signed-off-by: KaiserBh <kaiserbh@proton.me>
This commit is contained in:
KaiserBh 2023-12-29 05:57:27 +11:00
parent 5dbeda6b65
commit 646ec0c947
No known key found for this signature in database
GPG Key ID: 14D73B142042BBA9

View File

@ -80,94 +80,91 @@ abstract class SyncService(
} }
/** /**
* Merges two lists of SyncManga objects, prioritizing the manga with the most recent lastModifiedAt value. * Merges two lists of BackupManga objects, selecting the most recent manga based on the lastModifiedAt value.
* If lastModifiedAt is null, the function defaults to Instant.MIN for comparison purposes. * If lastModifiedAt is null for a manga, it treats that manga as the oldest possible for comparison purposes.
* This function is designed to reconcile local and remote manga lists, ensuring the most up-to-date manga is retained.
* *
* @param localMangaList The list of local SyncManga objects. * @param localMangaList The list of local BackupManga objects or null.
* @param remoteMangaList The list of remote SyncManga objects. * @param remoteMangaList The list of remote BackupManga objects or null.
* @return The merged list of SyncManga objects. * @return A list of BackupManga objects, each representing the most recent version of the manga from either local or remote sources.
*/ */
private fun mergeMangaLists( private fun mergeMangaLists(
localMangaList: List<BackupManga>?, localMangaList: List<BackupManga>?,
remoteMangaList: List<BackupManga>?, remoteMangaList: List<BackupManga>?
): List<BackupManga> { ): List<BackupManga> {
if (localMangaList == null) return remoteMangaList ?: emptyList() // Convert null lists to empty to simplify logic
if (remoteMangaList == null) return localMangaList val localMangaListSafe = localMangaList.orEmpty()
val remoteMangaListSafe = remoteMangaList.orEmpty()
val localMangaMap = localMangaList.associateBy { Pair(it.source, it.url) } // Associate both local and remote manga by their unique keys (source and url)
val remoteMangaMap = remoteMangaList.associateBy { Pair(it.source, it.url) } val localMangaMap = localMangaListSafe.associateBy { Pair(it.source, it.url) }
val remoteMangaMap = remoteMangaListSafe.associateBy { Pair(it.source, it.url) }
val mergedMangaMap = mutableMapOf<Pair<Long, String>, BackupManga>() // Prepare to merge both sets of manga
return (localMangaMap.keys + remoteMangaMap.keys).mapNotNull { key ->
val local = localMangaMap[key]
val remote = remoteMangaMap[key]
localMangaMap.forEach { (key, localManga) -> when {
val remoteManga = remoteMangaMap[key] local != null && remote == null -> local
if (remoteManga != null) { local == null && remote != null -> remote
val localMangaLastModifiedAtInstant = Instant.ofEpochMilli(localManga.lastModifiedAt) local != null && remote != null -> {
val remoteMangaLastModifiedAtInstant = Instant.ofEpochMilli(remoteManga.lastModifiedAt) // Compare last modified times and merge chapters
val mergedChapters = mergeChapters(localManga.chapters, remoteManga.chapters) val localTime = Instant.ofEpochMilli(local.lastModifiedAt)
// Keep the more recent manga val remoteTime = Instant.ofEpochMilli(remote.lastModifiedAt)
if (localMangaLastModifiedAtInstant.isAfter(remoteMangaLastModifiedAtInstant)) { val mergedChapters = mergeChapters(local.chapters, remote.chapters)
mergedMangaMap[key] = localManga.copy(chapters = mergedChapters)
} else { if (localTime >= remoteTime) local.copy(chapters = mergedChapters)
mergedMangaMap[key] = remoteManga.copy(chapters = mergedChapters) else remote.copy(chapters = mergedChapters)
}
else -> null // This case occurs if both are null, which shouldn't happen but is handled for completeness.
} }
} else {
// If there is no corresponding remote manga, keep the local one
mergedMangaMap[key] = localManga
} }
} }
// Add any remote manga that doesn't exist locally
remoteMangaMap.forEach { (key, remoteManga) ->
if (!mergedMangaMap.containsKey(key)) {
mergedMangaMap[key] = remoteManga
}
}
return mergedMangaMap.values.toList()
}
/** /**
* Merges two lists of SyncChapter objects, prioritizing the chapter with the most recent lastModifiedAt value. * Merges two lists of BackupChapter objects, selecting the most recent chapter based on the lastModifiedAt value.
* If lastModifiedAt is null, the function defaults to Instant.MIN for comparison purposes. * If lastModifiedAt is null for a chapter, it treats that chapter as the oldest possible for comparison purposes.
* This function is designed to reconcile local and remote chapter lists, ensuring the most up-to-date chapter is retained.
* *
* @param localChapters The list of local SyncChapter objects. * @param localChapters The list of local BackupChapter objects.
* @param remoteChapters The list of remote SyncChapter objects. * @param remoteChapters The list of remote BackupChapter objects.
* @return The merged list of SyncChapter objects. * @return A list of BackupChapter objects, each representing the most recent version of the chapter from either local or remote sources.
*
* - This function is used in scenarios where local and remote chapter lists need to be synchronized.
* - It iterates over the union of the URLs from both local and remote chapters.
* - For each URL, it compares the corresponding local and remote chapters based on the lastModifiedAt value.
* - If only one source (local or remote) has the chapter for a URL, that chapter is used.
* - If both sources have the chapter, the one with the more recent lastModifiedAt value is chosen.
* - If lastModifiedAt is null or missing, the chapter is considered the oldest for safety, ensuring that any chapter with a valid timestamp is preferred.
* - The resulting list contains the most recent chapters from the combined set of local and remote chapters.
*/ */
private fun mergeChapters( private fun mergeChapters(
localChapters: List<BackupChapter>, localChapters: List<BackupChapter>,
remoteChapters: List<BackupChapter>, remoteChapters: List<BackupChapter>
): List<BackupChapter> { ): List<BackupChapter> {
// Associate chapters by URL for both local and remote
val localChapterMap = localChapters.associateBy { it.url } val localChapterMap = localChapters.associateBy { it.url }
val remoteChapterMap = remoteChapters.associateBy { it.url } val remoteChapterMap = remoteChapters.associateBy { it.url }
val mergedChapterMap = mutableMapOf<String, BackupChapter>()
localChapterMap.forEach { (url, localChapter) -> // Merge both chapter maps
return (localChapterMap.keys + remoteChapterMap.keys).mapNotNull { url ->
// Determine the most recent chapter by comparing lastModifiedAt, considering null as Instant.MIN
val localChapter = localChapterMap[url]
val remoteChapter = remoteChapterMap[url] val remoteChapter = remoteChapterMap[url]
if (remoteChapter != null) {
val localInstant = localChapter.lastModifiedAt.let { Instant.ofEpochMilli(it) }
val remoteInstant = remoteChapter.lastModifiedAt.let { Instant.ofEpochMilli(it) }
val mergedChapter = when {
if (localInstant > remoteInstant) { localChapter != null && remoteChapter == null -> localChapter
localChapter localChapter == null && remoteChapter != null -> remoteChapter
} else { localChapter != null && remoteChapter != null -> {
remoteChapter val localInstant = localChapter.lastModifiedAt.let { Instant.ofEpochMilli(it) } ?: Instant.MIN
val remoteInstant = remoteChapter.lastModifiedAt.let { Instant.ofEpochMilli(it) } ?: Instant.MIN
if (localInstant >= remoteInstant) localChapter else remoteChapter
} }
mergedChapterMap[url] = mergedChapter else -> null
} else {
mergedChapterMap[url] = localChapter
} }
} }
remoteChapterMap.forEach { (url, remoteChapter) ->
if (!mergedChapterMap.containsKey(url)) {
mergedChapterMap[url] = remoteChapter
}
}
return mergedChapterMap.values.toList()
} }
/** /**