Revert attempts to read archives to cache first
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
This commit is contained in:
parent
d36cf5ce15
commit
6f59c6c6bb
5 changed files with 44 additions and 60 deletions
|
@ -210,7 +210,6 @@ dependencies {
|
||||||
// Disk
|
// Disk
|
||||||
implementation(libs.disklrucache)
|
implementation(libs.disklrucache)
|
||||||
implementation(libs.unifile)
|
implementation(libs.unifile)
|
||||||
implementation(libs.compress)
|
|
||||||
implementation(libs.junrar)
|
implementation(libs.junrar)
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
|
|
3
app/proguard-rules.pro
vendored
3
app/proguard-rules.pro
vendored
|
@ -71,9 +71,6 @@
|
||||||
# XmlUtil
|
# XmlUtil
|
||||||
-keep public enum nl.adaptivity.xmlutil.EventType { *; }
|
-keep public enum nl.adaptivity.xmlutil.EventType { *; }
|
||||||
|
|
||||||
# org.apache.commons:commons-compress
|
|
||||||
-keep,allowoptimization class org.apache.commons.compress.archivers.zip.**
|
|
||||||
|
|
||||||
# Firebase
|
# Firebase
|
||||||
-keep class com.google.firebase.installations.** { *; }
|
-keep class com.google.firebase.installations.** { *; }
|
||||||
-keep interface com.google.firebase.installations.** { *; }
|
-keep interface com.google.firebase.installations.** { *; }
|
|
@ -1,10 +1,11 @@
|
||||||
package eu.kanade.tachiyomi.ui.reader.loader
|
package eu.kanade.tachiyomi.ui.reader.loader
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import com.github.junrar.Archive
|
import com.github.junrar.Archive
|
||||||
import com.github.junrar.rarfile.FileHeader
|
import com.github.junrar.rarfile.FileHeader
|
||||||
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
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.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.PipedInputStream
|
import java.io.PipedInputStream
|
||||||
|
@ -15,36 +16,30 @@ import java.io.PipedOutputStream
|
||||||
*/
|
*/
|
||||||
internal class RarPageLoader(file: File) : PageLoader() {
|
internal class RarPageLoader(file: File) : PageLoader() {
|
||||||
|
|
||||||
private val context: Application by injectLazy()
|
private val rar = Archive(file)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var isLocal: Boolean = true
|
override var isLocal: Boolean = true
|
||||||
|
|
||||||
override suspend fun getPages(): List<ReaderPage> {
|
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() {
|
override fun recycle() {
|
||||||
super.recycle()
|
super.recycle()
|
||||||
tmpDir.deleteRecursively()
|
rar.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,52 +1,46 @@
|
||||||
package eu.kanade.tachiyomi.ui.reader.loader
|
package eu.kanade.tachiyomi.ui.reader.loader
|
||||||
|
|
||||||
import android.app.Application
|
import android.os.Build
|
||||||
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||||
import org.apache.commons.compress.archivers.zip.ZipFile
|
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||||
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel
|
import tachiyomi.core.util.system.ImageUtil
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loader used to load a chapter from a .zip or .cbz file.
|
* Loader used to load a chapter from a .zip or .cbz file.
|
||||||
*/
|
*/
|
||||||
internal class ZipPageLoader(file: File) : PageLoader() {
|
internal class ZipPageLoader(file: File) : PageLoader() {
|
||||||
|
|
||||||
private val context: Application by injectLazy()
|
private val zip = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also {
|
ZipFile(file, StandardCharsets.ISO_8859_1)
|
||||||
it.deleteRecursively()
|
} else {
|
||||||
it.mkdirs()
|
ZipFile(file)
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override var isLocal: Boolean = true
|
override var isLocal: Boolean = true
|
||||||
|
|
||||||
override suspend fun getPages(): List<ReaderPage> {
|
override suspend fun getPages(): List<ReaderPage> {
|
||||||
return DirectoryPageLoader(tmpDir).getPages()
|
return zip.entries().asSequence()
|
||||||
|
.filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
||||||
|
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||||
|
.mapIndexed { i, entry ->
|
||||||
|
ReaderPage(i).apply {
|
||||||
|
stream = { zip.getInputStream(entry) }
|
||||||
|
status = Page.State.READY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun loadPage(page: ReaderPage) {
|
||||||
|
check(!isRecycled)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun recycle() {
|
override fun recycle() {
|
||||||
super.recycle()
|
super.recycle()
|
||||||
tmpDir.deleteRecursively()
|
zip.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ jsoup = "org.jsoup:jsoup:1.16.1"
|
||||||
|
|
||||||
disklrucache = "com.jakewharton:disklrucache:2.0.2"
|
disklrucache = "com.jakewharton:disklrucache:2.0.2"
|
||||||
unifile = "com.github.tachiyomiorg:unifile:17bec43"
|
unifile = "com.github.tachiyomiorg:unifile:17bec43"
|
||||||
compress = "org.apache.commons:commons-compress:1.23.0"
|
|
||||||
junrar = "com.github.junrar:junrar:7.5.4"
|
junrar = "com.github.junrar:junrar:7.5.4"
|
||||||
|
|
||||||
sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" }
|
sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" }
|
||||||
|
|
Reference in a new issue