Make comic book archive caching optional instead of rolling it back completely (#883)
* Make comic book archive caching optional and fix cbz archives not opening for some users * corrected placement of // SY <--
This commit is contained in:
@@ -545,6 +545,11 @@ object SettingsReaderScreen : SearchableSettings {
|
||||
3 to stringResource(R.string.center_margin_double_and_wide_page),
|
||||
),
|
||||
),
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
pref = readerPreferences.cacheArchiveMangaOnDisk(),
|
||||
title = stringResource(R.string.cache_archived_manga_to_disk),
|
||||
subtitle = stringResource(R.string.cache_archived_manga_to_disk_subtitle),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -522,7 +522,15 @@ class Downloader(
|
||||
// If the original page was previously split, then skip
|
||||
if (imageFile.name.orEmpty().startsWith("${filenamePrefix}__")) return
|
||||
|
||||
ImageUtil.splitTallImage(tmpDir, imageFile, filenamePrefix)
|
||||
ImageUtil.splitTallImage(
|
||||
tmpDir,
|
||||
imageFile,
|
||||
filenamePrefix,
|
||||
// SY -->
|
||||
zip4jFile = null,
|
||||
zip4jEntry = null,
|
||||
// SY <--
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e) { "Failed to split downloaded image" }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
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 eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||
import tachiyomi.core.util.system.ImageUtil
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.PipedInputStream
|
||||
@@ -18,9 +21,40 @@ internal class RarPageLoader(file: File) : PageLoader() {
|
||||
|
||||
private val rar = Archive(file)
|
||||
|
||||
// SY -->
|
||||
private val context: Application by injectLazy()
|
||||
private val readerPreferences: ReaderPreferences by injectLazy()
|
||||
private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also {
|
||||
it.deleteRecursively()
|
||||
}
|
||||
|
||||
init {
|
||||
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
||||
tmpDir.mkdirs()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
|
||||
override var isLocal: Boolean = true
|
||||
|
||||
override suspend fun getPages(): List<ReaderPage> {
|
||||
// SY -->
|
||||
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
||||
return DirectoryPageLoader(tmpDir).getPages()
|
||||
}
|
||||
// SY <--
|
||||
return rar.fileHeaders.asSequence()
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { rar.getInputStream(it) } }
|
||||
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||
@@ -40,6 +74,9 @@ internal class RarPageLoader(file: File) : PageLoader() {
|
||||
override fun recycle() {
|
||||
super.recycle()
|
||||
rar.close()
|
||||
// SY -->
|
||||
tmpDir.deleteRecursively()
|
||||
// SY <--
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,13 +3,17 @@ package eu.kanade.tachiyomi.ui.reader.loader
|
||||
import android.app.Application
|
||||
import android.os.Build
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||
import eu.kanade.tachiyomi.util.storage.CbzCrypto
|
||||
import net.lingala.zip4j.ZipFile
|
||||
import tachiyomi.core.util.system.ImageUtil
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.zip.ZipFile
|
||||
import net.lingala.zip4j.ZipFile as Zip4jFile
|
||||
|
||||
/**
|
||||
* Loader used to load a chapter from a .zip or .cbz file.
|
||||
@@ -18,44 +22,99 @@ internal class ZipPageLoader(file: File) : PageLoader() {
|
||||
|
||||
// SY -->
|
||||
private val context: Application by injectLazy()
|
||||
private val readerPreferences: ReaderPreferences by injectLazy()
|
||||
private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also {
|
||||
it.deleteRecursively()
|
||||
it.mkdirs()
|
||||
}
|
||||
private val zip4j: Zip4jFile = Zip4jFile(file)
|
||||
private val zip: ZipFile? =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
if (!zip4j.isEncrypted) ZipFile(file, StandardCharsets.ISO_8859_1) else null
|
||||
} else {
|
||||
if (!zip4j.isEncrypted) ZipFile(file) else null
|
||||
}
|
||||
|
||||
init {
|
||||
ZipFile(file).use { zip ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
zip4j.charset = StandardCharsets.ISO_8859_1
|
||||
}
|
||||
|
||||
Zip4jFile(file).use { zip ->
|
||||
if (zip.isEncrypted) {
|
||||
if (!CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())) {
|
||||
this.recycle()
|
||||
throw IllegalStateException(context.getString(R.string.wrong_cbz_archive_password))
|
||||
}
|
||||
unzip(zip, CbzCrypto.getDecryptedPasswordCbz())
|
||||
zip4j.setPassword(CbzCrypto.getDecryptedPasswordCbz())
|
||||
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
||||
unzip()
|
||||
}
|
||||
} else {
|
||||
unzip(zip)
|
||||
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
||||
unzip()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun unzip(zip: ZipFile, password: CharArray? = null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
zip.charset = StandardCharsets.ISO_8859_1
|
||||
}
|
||||
|
||||
if (password != null) {
|
||||
zip.setPassword(password)
|
||||
}
|
||||
|
||||
zip.fileHeaders.asSequence()
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { zip.getInputStream(it) } }
|
||||
.forEach { entry ->
|
||||
zip.extractFile(entry, tmpDir.absolutePath)
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
override fun recycle() {
|
||||
super.recycle()
|
||||
zip?.close()
|
||||
// SY -->
|
||||
zip4j.close()
|
||||
tmpDir.deleteRecursively()
|
||||
}
|
||||
private fun unzip() {
|
||||
tmpDir.mkdirs()
|
||||
zip4j.fileHeaders.asSequence()
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { zip4j.getInputStream(it) } }
|
||||
.forEach { entry ->
|
||||
zip4j.extractFile(entry, tmpDir.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
override var isLocal: Boolean = true
|
||||
|
||||
override suspend fun getPages(): List<ReaderPage> {
|
||||
return DirectoryPageLoader(tmpDir).getPages()
|
||||
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
||||
return DirectoryPageLoader(tmpDir).getPages()
|
||||
}
|
||||
|
||||
if (zip == null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
zip4j.charset = StandardCharsets.ISO_8859_1
|
||||
}
|
||||
|
||||
return zip4j.fileHeaders.asSequence()
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { zip4j.getInputStream(it) } }
|
||||
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||
.mapIndexed { i, entry ->
|
||||
ReaderPage(i).apply {
|
||||
stream = { zip4j.getInputStream(entry) }
|
||||
status = Page.State.READY
|
||||
zip4jFile = zip4j
|
||||
zip4jEntry = entry
|
||||
}
|
||||
}.toList()
|
||||
} else {
|
||||
// SY <--
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No additional action required to load the page
|
||||
*/
|
||||
override suspend fun loadPage(page: ReaderPage) {
|
||||
check(!isRecycled)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.model
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import net.lingala.zip4j.ZipFile
|
||||
import net.lingala.zip4j.model.FileHeader
|
||||
import java.io.InputStream
|
||||
|
||||
open class ReaderPage(
|
||||
@@ -8,6 +10,9 @@ open class ReaderPage(
|
||||
url: String = "",
|
||||
imageUrl: String? = null,
|
||||
// SY -->
|
||||
/** zip4j inputStreams do not support mark() and release(), so they must be passed to ImageUtil */
|
||||
var zip4jFile: ZipFile? = null,
|
||||
var zip4jEntry: FileHeader? = null,
|
||||
/** Value to check if this page is used to as if it was too wide */
|
||||
var shiftedPage: Boolean = false,
|
||||
/** Value to check if a page is can be doubled up, but can't because the next page is too wide */
|
||||
|
||||
@@ -166,6 +166,8 @@ class ReaderPreferences(
|
||||
fun invertDoublePages() = preferenceStore.getBoolean("invert_double_pages", false)
|
||||
|
||||
fun centerMarginType() = preferenceStore.getInt("center_margin_type", PagerConfig.CenterMarginType.NONE)
|
||||
|
||||
fun cacheArchiveMangaOnDisk() = preferenceStore.getBoolean("cache_archive_manga_on_disk", false)
|
||||
// SY <--
|
||||
|
||||
enum class TappingInvertMode(val shouldInvertHorizontal: Boolean = false, val shouldInvertVertical: Boolean = false) {
|
||||
|
||||
@@ -229,7 +229,13 @@ class PagerPageHolder(
|
||||
return splitInHalf(imageStream)
|
||||
}
|
||||
|
||||
val isDoublePage = ImageUtil.isWideImage(imageStream)
|
||||
val isDoublePage = ImageUtil.isWideImage(
|
||||
imageStream,
|
||||
// SY -->
|
||||
page.zip4jFile,
|
||||
page.zip4jEntry,
|
||||
// SY <--
|
||||
)
|
||||
if (!isDoublePage) {
|
||||
return imageStream
|
||||
}
|
||||
@@ -240,7 +246,13 @@ class PagerPageHolder(
|
||||
}
|
||||
|
||||
private fun rotateDualPage(imageStream: BufferedInputStream): InputStream {
|
||||
val isDoublePage = ImageUtil.isWideImage(imageStream)
|
||||
val isDoublePage = ImageUtil.isWideImage(
|
||||
imageStream,
|
||||
// SY -->
|
||||
page.zip4jFile,
|
||||
page.zip4jEntry,
|
||||
// SY <--
|
||||
)
|
||||
return if (isDoublePage) {
|
||||
val rotation = if (viewer.config.dualPageRotateToFitInvert) -90f else 90f
|
||||
ImageUtil.rotateImage(imageStream, rotation)
|
||||
@@ -254,7 +266,13 @@ class PagerPageHolder(
|
||||
if (imageStream2 == null) {
|
||||
return if (imageStream is BufferedInputStream &&
|
||||
!ImageUtil.isAnimatedAndSupported(imageStream) &&
|
||||
ImageUtil.isWideImage(imageStream) &&
|
||||
ImageUtil.isWideImage(
|
||||
imageStream,
|
||||
// SY -->
|
||||
page.zip4jFile,
|
||||
page.zip4jEntry,
|
||||
// SY <--
|
||||
) &&
|
||||
viewer.config.centerMarginType and PagerConfig.CenterMarginType.WIDE_PAGE_CENTER_MARGIN > 0 &&
|
||||
!viewer.config.imageCropBorders
|
||||
) {
|
||||
|
||||
+21
-3
@@ -213,7 +213,13 @@ class WebtoonPageHolder(
|
||||
|
||||
private fun process(imageStream: BufferedInputStream): InputStream {
|
||||
if (viewer.config.dualPageSplit) {
|
||||
val isDoublePage = ImageUtil.isWideImage(imageStream)
|
||||
val isDoublePage = ImageUtil.isWideImage(
|
||||
imageStream,
|
||||
// SY -->
|
||||
page?.zip4jFile,
|
||||
page?.zip4jEntry,
|
||||
// SY <--
|
||||
)
|
||||
if (isDoublePage) {
|
||||
val upperSide = if (viewer.config.dualPageInvert) ImageUtil.Side.LEFT else ImageUtil.Side.RIGHT
|
||||
return ImageUtil.splitAndMerge(imageStream, upperSide)
|
||||
@@ -224,7 +230,13 @@ class WebtoonPageHolder(
|
||||
if (page is StencilPage) {
|
||||
return imageStream
|
||||
}
|
||||
val isStripSplitNeeded = ImageUtil.isStripSplitNeeded(imageStream)
|
||||
val isStripSplitNeeded = ImageUtil.isStripSplitNeeded(
|
||||
imageStream,
|
||||
// SY -->
|
||||
page?.zip4jFile,
|
||||
page?.zip4jEntry,
|
||||
// SY <--
|
||||
)
|
||||
if (isStripSplitNeeded) {
|
||||
return onStripSplit(imageStream)
|
||||
}
|
||||
@@ -237,7 +249,13 @@ class WebtoonPageHolder(
|
||||
// If we have reached this point [page] and its stream shouldn't be null
|
||||
val page = page!!
|
||||
val stream = page.stream!!
|
||||
val splitData = ImageUtil.getSplitDataForStream(imageStream).toMutableList()
|
||||
val splitData = ImageUtil.getSplitDataForStream(
|
||||
imageStream,
|
||||
// SY -->
|
||||
page.zip4jFile,
|
||||
page.zip4jEntry,
|
||||
// SY <--
|
||||
).toMutableList()
|
||||
val currentSplitData = splitData.removeFirst()
|
||||
val newPages = splitData.map {
|
||||
StencilPage(page) { ImageUtil.splitStrip(it, stream) }
|
||||
|
||||
Reference in New Issue
Block a user