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).
This commit is contained in:
Fidel Selva
2022-04-16 08:54:03 -05:00
committed by GitHub
parent db5c5ed534
commit 844454053d
2 changed files with 31 additions and 25 deletions

View File

@@ -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)
}

View File

@@ -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<FileHeader, InputStream>()
/**
* Returns an observable containing the pages found on this rar archive ordered with a natural
* comparator.
*/
override fun getPages(): List<ReaderPage> {
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
}
}