From 844454053d34d091538df1058fb9645d6e4c8d08 Mon Sep 17 00:00:00 2001 From: Fidel Selva Date: Sat, 16 Apr 2022 08:54:03 -0500 Subject: [PATCH] handle solid RAR archives (#339) * Upgrade junrar version to 7.5.0 and set unrar.extractor.thread-keep-alive-seconds to 30 (default is 5) * #338 Read whole archive in case RAR file is solid (it is, it can't be decompressed at an arbitrary location). --- server/build.gradle.kts | 5 +- .../source/local/loader/RarPageLoader.kt | 51 ++++++++++--------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/server/build.gradle.kts b/server/build.gradle.kts index f29fb618..e82af58a 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -54,7 +54,7 @@ dependencies { // Disk & File implementation("net.lingala.zip4j:zip4j:2.9.1") - implementation("com.github.junrar:junrar:7.4.0") + implementation("com.github.junrar:junrar:7.5.0") // CloudflareInterceptor implementation("net.sourceforge.htmlunit:htmlunit:2.56.0") @@ -75,6 +75,9 @@ dependencies { } application { + applicationDefaultJvmArgs = listOf( + "-Djunrar.extractor.thread-keep-alive-seconds=30" + ) mainClass.set(MainClass) } diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/loader/RarPageLoader.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/loader/RarPageLoader.kt index 411ace0c..e7893ab2 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/loader/RarPageLoader.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/loader/RarPageLoader.kt @@ -5,11 +5,11 @@ import com.github.junrar.rarfile.FileHeader import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil -import java.io.File +import java.io.ByteArrayOutputStream +import java.io.ByteArrayInputStream import java.io.InputStream -import java.io.PipedInputStream -import java.io.PipedOutputStream -import java.util.concurrent.Executors +import java.io.File + /** * Loader used to load a chapter from a .rar or .cbr file. @@ -22,20 +22,40 @@ class RarPageLoader(file: File) : PageLoader { private val archive = Archive(file) /** - * Pool for copying compressed files to an input stream. + * The fully uncompressed files, to be used in case archive is solid. */ - private val pool = Executors.newFixedThreadPool(1) + private var archiveMap = mutableMapOf() /** * Returns an observable containing the pages found on this rar archive ordered with a natural * comparator. */ override fun getPages(): List { + if (archive.mainHeader.isSolid) { + // Solid means that we need to read all the file sequentially + for (header in archive.fileHeaders) { + val baos = ByteArrayOutputStream() + archive.extractFile(header, baos) + archiveMap[header] = ByteArrayInputStream(baos.toByteArray()) + } + // After reading the full archive, proceed to filter and transform + return archive.fileHeaders + .filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { archiveMap.getValue(it) } } + .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) } + .mapIndexed { i, header -> + val streamFn = { archiveMap.getValue(header) } + + ReaderPage(i).apply { + stream = streamFn + status = Page.READY + } + } + } return archive.fileHeaders .filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } } .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) } .mapIndexed { i, header -> - val streamFn = { getStream(header) } + val streamFn = { archive.getInputStream(header) } ReaderPage(i).apply { stream = streamFn @@ -43,21 +63,4 @@ class RarPageLoader(file: File) : PageLoader { } } } - - /** - * Returns an input stream for the given [header]. - */ - private fun getStream(header: FileHeader): InputStream { - val pipeIn = PipedInputStream() - val pipeOut = PipedOutputStream(pipeIn) - pool.execute { - try { - pipeOut.use { - archive.extractFile(header, it) - } - } catch (e: Exception) { - } - } - return pipeIn - } }