From c5bd979645be5b54b4ff13dac3789a47563fefe9 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sun, 13 Oct 2024 09:39:28 +0300 Subject: [PATCH] Fix zip closing --- .../koitharu/kotatsu/core/zip/ZipOutput.kt | 14 ++- .../local/data/output/LocalMangaDirOutput.kt | 22 ++++- .../local/data/output/LocalMangaUtil.kt | 24 +---- .../local/data/output/LocalMangaZipOutput.kt | 98 +++++++++++-------- 4 files changed, 88 insertions(+), 70 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/zip/ZipOutput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/zip/ZipOutput.kt index f124671c3..1f5c34766 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/zip/ZipOutput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/zip/ZipOutput.kt @@ -104,8 +104,11 @@ class ZipOutput( } val zipEntry = ZipEntry(name) putNextEntry(zipEntry) - fis.copyTo(this) - closeEntry() + try { + fis.copyTo(this) + } finally { + closeEntry() + } } } return true @@ -118,8 +121,11 @@ class ZipOutput( } val zipEntry = ZipEntry(name) putNextEntry(zipEntry) - content.byteInputStream().copyTo(this) - closeEntry() + try { + content.byteInputStream().copyTo(this) + } finally { + closeEntry() + } return true } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaDirOutput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaDirOutput.kt index 32501d50d..680df9432 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaDirOutput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaDirOutput.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import okhttp3.internal.closeQuietly import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.util.ext.deleteAwait import org.koitharu.kotatsu.core.util.ext.takeIfReadable @@ -90,7 +91,7 @@ class LocalMangaDirOutput( override fun close() { for (output in chaptersOutput.values) { - output.close() + output.closeQuietly() } } @@ -119,10 +120,21 @@ class LocalMangaDirOutput( } private suspend fun ZipOutput.flushAndFinish() = runInterruptible(Dispatchers.IO) { - finish() - close() - val resFile = File(file.absolutePath.removeSuffix(SUFFIX_TMP)) - file.renameTo(resFile) + val e: Throwable? = try { + finish() + null + } catch (e: Throwable) { + e + } finally { + close() + } + if (e == null) { + val resFile = File(file.absolutePath.removeSuffix(SUFFIX_TMP)) + file.renameTo(resFile) + } else { + file.delete() + throw e + } } private fun chapterFileName(chapter: IndexedValue): String { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaUtil.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaUtil.kt index 08df5832a..011f4ff6f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaUtil.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaUtil.kt @@ -2,8 +2,6 @@ package org.koitharu.kotatsu.local.data.output import androidx.core.net.toFile import androidx.core.net.toUri -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runInterruptible import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.parsers.model.Manga @@ -16,26 +14,14 @@ class LocalMangaUtil( } suspend fun deleteChapters(ids: Set) { - newOutput().use { output -> - when (output) { - is LocalMangaZipOutput -> runInterruptible(Dispatchers.IO) { - LocalMangaZipOutput.filterChapters(output, ids) - } - - is LocalMangaDirOutput -> { - output.deleteChapters(ids) - output.finish() - } - } - } - } - - private suspend fun newOutput(): LocalMangaOutput = runInterruptible(Dispatchers.IO) { val file = manga.url.toUri().toFile() if (file.isDirectory) { - LocalMangaDirOutput(file, manga) + LocalMangaDirOutput(file, manga).use { output -> + output.deleteChapters(ids) + output.finish() + } } else { - LocalMangaZipOutput(file, manga) + LocalMangaZipOutput.filterChapters(file, manga, ids) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaZipOutput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaZipOutput.kt index d4370c3f6..eded64595 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaZipOutput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaZipOutput.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import okhttp3.internal.closeQuietly import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.util.ext.deleteAwait import org.koitharu.kotatsu.core.util.ext.readText @@ -52,27 +53,29 @@ class LocalMangaZipOutput( index.setCoverEntry(name) } - override suspend fun addPage(chapter: IndexedValue, file: File, pageNumber: Int, ext: String) = mutex.withLock { - val name = buildString { - append(FILENAME_PATTERN.format(chapter.value.branch.hashCode(), chapter.index + 1, pageNumber)) - if (ext.isNotEmpty() && ext.length <= 4) { - append('.') - append(ext) + override suspend fun addPage(chapter: IndexedValue, file: File, pageNumber: Int, ext: String) = + mutex.withLock { + val name = buildString { + append(FILENAME_PATTERN.format(chapter.value.branch.hashCode(), chapter.index + 1, pageNumber)) + if (ext.isNotEmpty() && ext.length <= 4) { + append('.') + append(ext) + } } + runInterruptible(Dispatchers.IO) { + output.put(name, file) + } + index.addChapter(chapter, null) } - runInterruptible(Dispatchers.IO) { - output.put(name, file) - } - index.addChapter(chapter, null) - } override suspend fun flushChapter(chapter: MangaChapter): Boolean = false override suspend fun finish() = mutex.withLock { runInterruptible(Dispatchers.IO) { - output.put(ENTRY_NAME_INDEX, index.toString()) - output.finish() - output.close() + output.use { output -> + output.put(ENTRY_NAME_INDEX, index.toString()) + output.finish() + } } rootFile.deleteAwait() output.file.renameTo(rootFile) @@ -115,42 +118,53 @@ class LocalMangaZipOutput( private const val FILENAME_PATTERN = "%08d_%03d%03d" - @WorkerThread - fun filterChapters(subject: LocalMangaZipOutput, idsToRemove: Set) { - ZipFile(subject.rootFile).use { zip -> - val index = MangaIndex(zip.readText(zip.getEntry(ENTRY_NAME_INDEX))) - idsToRemove.forEach { id -> index.removeChapter(id) } - val patterns = requireNotNull(index.getMangaInfo()?.chapters).map { - index.getChapterNamesPattern(it) - } - val coverEntryName = index.getCoverEntry() - for (entry in zip.entries()) { - when { - entry.name == ENTRY_NAME_INDEX -> { - subject.output.put(ENTRY_NAME_INDEX, index.toString()) + suspend fun filterChapters(file: File, manga: Manga, idsToRemove: Set) = + runInterruptible(Dispatchers.IO) { + val subject = LocalMangaZipOutput(file, manga) + try { + ZipFile(subject.rootFile).use { zip -> + val index = MangaIndex(zip.readText(zip.getEntry(ENTRY_NAME_INDEX))) + idsToRemove.forEach { id -> index.removeChapter(id) } + val patterns = requireNotNull(index.getMangaInfo()?.chapters).map { + index.getChapterNamesPattern(it) } + val coverEntryName = index.getCoverEntry() + for (entry in zip.entries()) { + when { + entry.name == ENTRY_NAME_INDEX -> { + subject.output.put(ENTRY_NAME_INDEX, index.toString()) + } - entry.isDirectory -> { - subject.output.addDirectory(entry.name) - } + entry.isDirectory -> { + subject.output.addDirectory(entry.name) + } - entry.name == coverEntryName -> { - subject.output.copyEntryFrom(zip, entry) - } + entry.name == coverEntryName -> { + subject.output.copyEntryFrom(zip, entry) + } - else -> { - val name = entry.name.substringBefore('.') - if (patterns.any { it.matches(name) }) { - subject.output.copyEntryFrom(zip, entry) + else -> { + val name = entry.name.substringBefore('.') + if (patterns.any { it.matches(name) }) { + subject.output.copyEntryFrom(zip, entry) + } + } } } + subject.output.finish() + subject.output.close() + subject.rootFile.delete() + subject.output.file.renameTo(subject.rootFile) } + } catch (e: Throwable) { + subject.closeQuietly() + try { + subject.output.file.delete() + } catch (e2: Throwable) { + e.addSuppressed(e2) + } + throw e } - subject.output.finish() - subject.output.close() - subject.rootFile.delete() - subject.output.file.renameTo(subject.rootFile) } - } } }