From a5d64be197533d03bd9db5487df4771ef0446363 Mon Sep 17 00:00:00 2001 From: Zeedif Date: Sun, 24 Aug 2025 10:35:38 -0600 Subject: [PATCH] fix(kosync): use correct partial hash for binary checksum (#1610) When generating a hash on-the-fly for the binary checksum method, the code was performing a full MD5 hash of the entire file stream. This was incorrect as the KOReader binary method expects a specific partial hash (reading small chunks at different offsets). This change ensures that when an in-memory CBZ is created, it is first written to a temporary file. Then, the correct `KoreaderHelper.hashContents()` function is used on that file to generate the partial hash, matching KOReader's logic. The temporary file is deleted immediately after. --- .../manga/impl/sync/KoreaderSyncService.kt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt index 80658869..dd8d1a58 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt @@ -18,10 +18,12 @@ import suwayomi.tachidesk.graphql.types.KoSyncStatusPayload import suwayomi.tachidesk.graphql.types.KoreaderSyncChecksumMethod import suwayomi.tachidesk.graphql.types.KoreaderSyncStrategy import suwayomi.tachidesk.manga.impl.ChapterDownloadHelper +import suwayomi.tachidesk.manga.impl.util.KoreaderHelper import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable import suwayomi.tachidesk.server.serverConfig import uy.kohesive.injekt.injectLazy +import java.io.File import java.util.UUID import kotlin.math.abs @@ -119,11 +121,19 @@ object KoreaderSyncService { KoreaderSyncChecksumMethod.BINARY -> { logger.info { "[KOSYNC HASH] No hash for chapterId=$chapterId. Generating from downloaded content." } try { - // This generates a deterministic CBZ stream from either a folder or an existing CBZ file. - // If it fails, it means the chapter is not available for hashing. + // Always create a CBZ in memory if it doesn't exist val (stream, _) = ChapterDownloadHelper.getArchiveStreamWithSize(mangaId, chapterId) - stream.use { - Hash.md5(it.readBytes()) + // Write the stream to a temp file for partial hashing + val tempFile = File.createTempFile("kosync-hash-", ".cbz") + try { + tempFile.outputStream().use { fos -> + stream.use { it.copyTo(fos) } + } + // Use the same hashing method as for downloads + KoreaderHelper.hashContents(tempFile) + } finally { + // Always delete the temp file + tempFile.delete() } } catch (e: Exception) { logger.warn(e) { "[KOSYNC HASH] Failed to generate archive stream for chapterId=$chapterId." }