diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt index 9ea6e8b2..99e62654 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt @@ -20,6 +20,7 @@ import suwayomi.tachidesk.server.ApplicationDirs import uy.kohesive.injekt.injectLazy import java.io.File import java.io.InputStream +import java.util.zip.Deflater private val applicationDirs: ApplicationDirs by injectLazy() @@ -61,9 +62,12 @@ class ArchiveProvider( } ZipArchiveOutputStream(outputFile.outputStream()).use { zipOut -> + zipOut.setMethod(ZipArchiveOutputStream.DEFLATED) + zipOut.setLevel(Deflater.DEFAULT_COMPRESSION) if (chapterCacheFolder.isDirectory) { chapterCacheFolder.listFiles()?.sortedBy { it.name }?.forEach { val entry = ZipArchiveEntry(it.name) + entry.time = 0L try { zipOut.putArchiveEntry(entry) it.inputStream().use { inputStream -> diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt index f07f52f9..9d457f01 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt @@ -1,5 +1,7 @@ package suwayomi.tachidesk.manga.impl.download.fileProvider.impl +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update import suwayomi.tachidesk.manga.impl.download.fileProvider.ChaptersFilesProvider @@ -16,8 +18,7 @@ import java.io.ByteArrayOutputStream import java.io.File import java.io.FileInputStream import java.io.InputStream -import java.util.zip.ZipEntry -import java.util.zip.ZipOutputStream +import java.util.zip.Deflater private val applicationDirs: ApplicationDirs by injectLazy() @@ -83,17 +84,21 @@ class FolderProvider( } val byteArrayOutputStream = ByteArrayOutputStream() - ZipOutputStream(BufferedOutputStream(byteArrayOutputStream)).use { zipOutputStream -> + ZipArchiveOutputStream(BufferedOutputStream(byteArrayOutputStream)).use { zipOutputStream -> + zipOutputStream.setMethod(ZipArchiveOutputStream.DEFLATED) + zipOutputStream.setLevel(Deflater.DEFAULT_COMPRESSION) + chapterDir .listFiles() ?.filter { it.isFile } ?.sortedBy { it.name } ?.forEach { imageFile -> FileInputStream(imageFile).use { fileInputStream -> - val zipEntry = ZipEntry(imageFile.name) - zipOutputStream.putNextEntry(zipEntry) + val zipEntry = ZipArchiveEntry(imageFile.name) + zipEntry.time = 0L + zipOutputStream.putArchiveEntry(zipEntry) fileInputStream.copyTo(zipOutputStream) - zipOutputStream.closeEntry() + zipOutputStream.closeArchiveEntry() } } } 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 e9b59913..80658869 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 @@ -17,13 +17,11 @@ import org.jetbrains.exposed.sql.update import suwayomi.tachidesk.graphql.types.KoSyncStatusPayload import suwayomi.tachidesk.graphql.types.KoreaderSyncChecksumMethod import suwayomi.tachidesk.graphql.types.KoreaderSyncStrategy -import suwayomi.tachidesk.manga.impl.util.KoreaderHelper -import suwayomi.tachidesk.manga.impl.util.getChapterCbzPath +import suwayomi.tachidesk.manga.impl.ChapterDownloadHelper 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 @@ -102,35 +100,35 @@ object KoreaderSyncService { private fun getOrGenerateChapterHash(chapterId: Int): String? { return transaction { - val existingHash = + val chapterRow = ChapterTable - .select(ChapterTable.koreaderHash) + .select(ChapterTable.koreaderHash, ChapterTable.manga, ChapterTable.isDownloaded) .where { ChapterTable.id eq chapterId } - .firstOrNull() - ?.get(ChapterTable.koreaderHash) + .firstOrNull() ?: return@transaction null + val existingHash = chapterRow[ChapterTable.koreaderHash] if (!existingHash.isNullOrBlank()) { return@transaction existingHash } + val mangaId = chapterRow[ChapterTable.manga].value val checksumMethod = serverConfig.koreaderSyncChecksumMethod.value + val newHash = when (checksumMethod) { KoreaderSyncChecksumMethod.BINARY -> { - logger.info { "[KOSYNC HASH] No hash for chapterId=$chapterId. Generating from CBZ content." } - val mangaId = - ChapterTable - .select(ChapterTable.manga) - .where { ChapterTable.id eq chapterId } - .firstOrNull() - ?.get(ChapterTable.manga) - ?.value ?: return@transaction null - val cbzFile = File(getChapterCbzPath(mangaId, chapterId)) - if (!cbzFile.exists()) { - logger.info { "[KOSYNC HASH] Could not generate hash for chapterId=$chapterId. CBZ not found." } - return@transaction null + 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. + val (stream, _) = ChapterDownloadHelper.getArchiveStreamWithSize(mangaId, chapterId) + stream.use { + Hash.md5(it.readBytes()) + } + } catch (e: Exception) { + logger.warn(e) { "[KOSYNC HASH] Failed to generate archive stream for chapterId=$chapterId." } + null } - KoreaderHelper.hashContents(cbzFile) } KoreaderSyncChecksumMethod.FILENAME -> { logger.info { "[KOSYNC HASH] No hash for chapterId=$chapterId. Generating from filename." }