Just extract everything in zips to a directory
Issues: - Apache implementation relies on methods unavailable on lower Android API levels - Using input stream implementation doesn't seem to read some files properly, but using ZipFile implementation still requires reading the entire thing into memory (cherry picked from commit 6f59c6c6bb12f1ef123abd418001c7a86d43ef7a) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt
This commit is contained in:
parent
ce4f2e83b0
commit
98e94ef7ae
@ -210,7 +210,6 @@ dependencies {
|
||||
// Disk
|
||||
implementation(libs.disklrucache)
|
||||
implementation(libs.unifile)
|
||||
implementation(libs.compress)
|
||||
implementation(libs.junrar)
|
||||
// SY -->
|
||||
implementation(libs.zip4j)
|
||||
|
1
app/proguard-rules.pro
vendored
1
app/proguard-rules.pro
vendored
@ -265,7 +265,6 @@
|
||||
-keep,allowoptimization class kotlin.coroutines.Continuation
|
||||
-keep,allowoptimization class * extends uy.kohesive.injekt.api.TypeReference
|
||||
-keep,allowoptimization public class io.requery.android.database.sqlite.SQLiteConnection { *; }
|
||||
-keep,allowoptimization class org.apache.commons.compress.archivers.zip.**
|
||||
|
||||
# Suggested rules
|
||||
-dontwarn com.oracle.svm.core.annotate.AutomaticFeature
|
||||
|
@ -1,10 +1,11 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.loader
|
||||
|
||||
import android.app.Application
|
||||
import com.github.junrar.Archive
|
||||
import com.github.junrar.rarfile.FileHeader
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||
import tachiyomi.core.util.system.ImageUtil
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.PipedInputStream
|
||||
@ -15,36 +16,30 @@ import java.io.PipedOutputStream
|
||||
*/
|
||||
internal class RarPageLoader(file: File) : PageLoader() {
|
||||
|
||||
private val context: Application by injectLazy()
|
||||
private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also {
|
||||
it.deleteRecursively()
|
||||
it.mkdirs()
|
||||
}
|
||||
|
||||
init {
|
||||
Archive(file).use { rar ->
|
||||
rar.fileHeaders.asSequence()
|
||||
.filterNot { it.isDirectory }
|
||||
.forEach { header ->
|
||||
val pageOutputStream = File(tmpDir, header.fileName.substringAfterLast("/"))
|
||||
.also { it.createNewFile() }
|
||||
.outputStream()
|
||||
getStream(rar, header).use {
|
||||
it.copyTo(pageOutputStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private val rar = Archive(file)
|
||||
|
||||
override var isLocal: Boolean = true
|
||||
|
||||
override suspend fun getPages(): List<ReaderPage> {
|
||||
return DirectoryPageLoader(tmpDir).getPages()
|
||||
return rar.fileHeaders.asSequence()
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { rar.getInputStream(it) } }
|
||||
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||
.mapIndexed { i, header ->
|
||||
ReaderPage(i).apply {
|
||||
stream = { getStream(rar, header) }
|
||||
status = Page.State.READY
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
|
||||
override suspend fun loadPage(page: ReaderPage) {
|
||||
check(!isRecycled)
|
||||
}
|
||||
|
||||
override fun recycle() {
|
||||
super.recycle()
|
||||
tmpDir.deleteRecursively()
|
||||
rar.close()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,68 +5,48 @@ import android.os.Build
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||
import eu.kanade.tachiyomi.util.storage.CbzCrypto
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile
|
||||
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel
|
||||
import net.lingala.zip4j.ZipFile
|
||||
import tachiyomi.core.util.system.ImageUtil
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.nio.charset.StandardCharsets
|
||||
import net.lingala.zip4j.ZipFile as Zip4JFile
|
||||
|
||||
/**
|
||||
* Loader used to load a chapter from a .zip or .cbz file.
|
||||
*/
|
||||
internal class ZipPageLoader(file: File) : PageLoader() {
|
||||
|
||||
// SY -->
|
||||
private val context: Application by injectLazy()
|
||||
private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also {
|
||||
it.deleteRecursively()
|
||||
it.mkdirs()
|
||||
}
|
||||
|
||||
// SY -->
|
||||
init {
|
||||
val zip = Zip4JFile(file)
|
||||
ZipFile(file).use { zip ->
|
||||
if (zip.isEncrypted) {
|
||||
if (!CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())) {
|
||||
this.recycle()
|
||||
throw IllegalStateException(context.getString(R.string.wrong_cbz_archive_password))
|
||||
}
|
||||
unzipEncrypted(zip)
|
||||
unzip(zip, CbzCrypto.getDecryptedPasswordCbz())
|
||||
} else {
|
||||
unzip(file)
|
||||
}
|
||||
}
|
||||
private fun unzip(file: File) {
|
||||
// SY <--
|
||||
ByteArrayOutputStream().use { byteArrayOutputStream ->
|
||||
FileInputStream(file).use { it.copyTo(byteArrayOutputStream) }
|
||||
|
||||
ZipFile(SeekableInMemoryByteChannel(byteArrayOutputStream.toByteArray())).use { zip ->
|
||||
zip.entries.asSequence()
|
||||
.filterNot { it.isDirectory }
|
||||
.forEach { entry ->
|
||||
File(tmpDir, entry.name.substringAfterLast("/"))
|
||||
.also { it.createNewFile() }
|
||||
.outputStream().use { pageOutputStream ->
|
||||
zip.getInputStream(entry).copyTo(pageOutputStream)
|
||||
pageOutputStream.flush()
|
||||
unzip(zip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SY -->
|
||||
private fun unzipEncrypted(zip: Zip4JFile) {
|
||||
private fun unzip(zip: ZipFile, password: CharArray? = null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
zip.charset = StandardCharsets.ISO_8859_1
|
||||
}
|
||||
zip.setPassword(CbzCrypto.getDecryptedPasswordCbz())
|
||||
|
||||
if (password != null) {
|
||||
zip.setPassword(password)
|
||||
}
|
||||
|
||||
zip.fileHeaders.asSequence()
|
||||
.filterNot { it.isDirectory }
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { zip.getInputStream(it) } }
|
||||
.forEach { entry ->
|
||||
zip.extractFile(entry, tmpDir.absolutePath)
|
||||
}
|
||||
@ -78,9 +58,4 @@ internal class ZipPageLoader(file: File) : PageLoader() {
|
||||
override suspend fun getPages(): List<ReaderPage> {
|
||||
return DirectoryPageLoader(tmpDir).getPages()
|
||||
}
|
||||
|
||||
override fun recycle() {
|
||||
super.recycle()
|
||||
tmpDir.deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ jsoup = "org.jsoup:jsoup:1.16.1"
|
||||
|
||||
disklrucache = "com.jakewharton:disklrucache:2.0.2"
|
||||
unifile = "com.github.tachiyomiorg:unifile:17bec43"
|
||||
compress = "org.apache.commons:commons-compress:1.23.0"
|
||||
junrar = "com.github.junrar:junrar:7.5.4"
|
||||
zip4j = "net.lingala.zip4j:zip4j:2.11.5"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user