diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/BackupZipInput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/BackupZipInput.kt index 342f1640d..2fb7cd110 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/BackupZipInput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/backup/BackupZipInput.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.runInterruptible +import okhttp3.internal.closeQuietly import okio.Closeable import org.json.JSONArray import org.koitharu.kotatsu.core.exceptions.BadBackupFormatException @@ -38,7 +39,7 @@ class BackupZipInput private constructor(val file: File) : Closeable { fun cleanupAsync() { processLifecycleScope.launch(Dispatchers.IO, CoroutineStart.ATOMIC) { runCatching { - close() + closeQuietly() file.delete() } } @@ -46,14 +47,22 @@ class BackupZipInput private constructor(val file: File) : Closeable { companion object { - fun from(file: File): BackupZipInput = try { - val res = BackupZipInput(file) - if (res.zipFile.getEntry("index") == null) { - throw BadBackupFormatException(null) + fun from(file: File): BackupZipInput { + var res: BackupZipInput? = null + return try { + res = BackupZipInput(file) + if (res.zipFile.getEntry("index") == null) { + throw BadBackupFormatException(null) + } + res + } catch (exception: Exception) { + res?.closeQuietly() + throw if (exception is ZipException) { + BadBackupFormatException(exception) + } else { + exception + } } - res - } catch (e: ZipException) { - throw BadBackupFormatException(e) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt index f6bab4ce2..8618d1ec2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt @@ -10,10 +10,13 @@ import android.provider.OpenableColumns import androidx.core.database.getStringOrNull import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible +import okhttp3.internal.closeQuietly +import org.jetbrains.annotations.Blocking import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.fs.FileSequence import java.io.File import java.io.FileFilter +import java.io.InputStream import java.nio.file.attribute.BasicFileAttributes import java.util.zip.ZipEntry import java.util.zip.ZipFile @@ -32,10 +35,19 @@ fun File.takeIfWriteable() = takeIf { it.exists() && it.canWrite() } fun File.isNotEmpty() = length() != 0L +@Blocking fun ZipFile.readText(entry: ZipEntry) = getInputStream(entry).bufferedReader().use { it.readText() } +@Blocking +fun ZipFile.getInputStreamOrClose(entry: ZipEntry): InputStream = try { + getInputStream(entry) +} catch (e: Throwable) { + closeQuietly() + throw e +} + fun File.getStorageName(context: Context): String = runCatching { val manager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Uri.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Uri.kt index 0981bfa79..152aebd14 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Uri.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Uri.kt @@ -41,7 +41,7 @@ fun Uri.source(): Source = when (scheme) { URI_SCHEME_ZIP -> { val zip = ZipFile(schemeSpecificPart) val entry = zip.getEntry(fragment) - zip.getInputStream(entry).source().withExtraCloseable(zip) + zip.getInputStreamOrClose(entry).source().withExtraCloseable(zip) } else -> unsupportedUri(this) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageFetcher.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageFetcher.kt index e954b630e..344a54004 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageFetcher.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageFetcher.kt @@ -22,6 +22,7 @@ import okio.source import org.koitharu.kotatsu.core.network.MangaHttpClient import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.core.util.ext.getInputStreamOrClose import org.koitharu.kotatsu.local.data.PagesCache import org.koitharu.kotatsu.local.data.isFileUri import org.koitharu.kotatsu.local.data.isZipUri @@ -68,7 +69,7 @@ class MangaPageFetcher( val entry = zip.getEntry(uri.fragment) SourceResult( source = ImageSource( - source = zip.getInputStream(entry).source().withExtraCloseable(zip).buffer(), + source = zip.getInputStreamOrClose(entry).source().withExtraCloseable(zip).buffer(), context = context, metadata = MangaPageMetadata(page), ), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/CbzFetcher.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/CbzFetcher.kt index ecc5791b1..0ce579bd3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/CbzFetcher.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/CbzFetcher.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible import okio.buffer import okio.source +import org.koitharu.kotatsu.core.util.ext.getInputStreamOrClose import org.koitharu.kotatsu.local.data.util.withExtraCloseable import java.util.zip.ZipFile @@ -24,7 +25,7 @@ class CbzFetcher( val zip = ZipFile(uri.schemeSpecificPart) val entry = zip.getEntry(uri.fragment) val ext = MimeTypeMap.getFileExtensionFromUrl(entry.name) - val bufferedSource = zip.getInputStream(entry).source().withExtraCloseable(zip).buffer() + val bufferedSource = zip.getInputStreamOrClose(entry).source().withExtraCloseable(zip).buffer() SourceResult( source = ImageSource( source = bufferedSource, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt index 36f839979..ec33cd83c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt @@ -112,32 +112,33 @@ class LocalMangaZipInput(root: File) : LocalMangaInput(root) { return runInterruptible(Dispatchers.IO) { val uri = Uri.parse(chapter.url) val file = uri.toFile() - val zip = ZipFile(file) - val index = zip.getEntry(LocalMangaOutput.ENTRY_NAME_INDEX)?.let(zip::readText)?.let(::MangaIndex) - var entries = zip.entries().asSequence() - entries = if (index != null) { - val pattern = index.getChapterNamesPattern(chapter) - entries.filter { x -> !x.isDirectory && x.name.substringBefore('.').matches(pattern) } - } else { - val parent = uri.fragment.orEmpty() - entries.filter { x -> - !x.isDirectory && x.name.substringBeforeLast( - File.separatorChar, - "", - ) == parent + ZipFile(file).use { zip -> + val index = zip.getEntry(LocalMangaOutput.ENTRY_NAME_INDEX)?.let(zip::readText)?.let(::MangaIndex) + var entries = zip.entries().asSequence() + entries = if (index != null) { + val pattern = index.getChapterNamesPattern(chapter) + entries.filter { x -> !x.isDirectory && x.name.substringBefore('.').matches(pattern) } + } else { + val parent = uri.fragment.orEmpty() + entries.filter { x -> + !x.isDirectory && x.name.substringBeforeLast( + File.separatorChar, + "", + ) == parent + } } + entries + .toListSorted(compareBy(AlphanumComparator()) { x -> x.name }) + .map { x -> + val entryUri = zipUri(file, x.name) + MangaPage( + id = entryUri.longHashCode(), + url = entryUri, + preview = null, + source = LocalMangaSource, + ) + } } - entries - .toListSorted(compareBy(AlphanumComparator()) { x -> x.name }) - .map { x -> - val entryUri = zipUri(file, x.name) - MangaPage( - id = entryUri.longHashCode(), - url = entryUri, - preview = null, - source = LocalMangaSource, - ) - } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/DetectReaderModeUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/DetectReaderModeUseCase.kt index 21c8cea06..9870d4d9c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/DetectReaderModeUseCase.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/DetectReaderModeUseCase.kt @@ -67,10 +67,11 @@ class DetectReaderModeUseCase @Inject constructor( val size = when { uri.isZipUri() -> runInterruptible(Dispatchers.IO) { - val zip = ZipFile(uri.schemeSpecificPart) - val entry = zip.getEntry(uri.fragment) - zip.getInputStream(entry).use { - getBitmapSize(it) + ZipFile(uri.schemeSpecificPart).use { zip -> + val entry = zip.getEntry(uri.fragment) + zip.getInputStream(entry).use { + getBitmapSize(it) + } } }