Don't copy archives to temp files when opening (#326)

This commit is contained in:
FooIbar 2024-02-04 01:33:18 +08:00 committed by GitHub
parent 170daf9fb2
commit 0da7ad6f1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 48 additions and 92 deletions

View File

@ -208,7 +208,7 @@ dependencies {
// Disk // Disk
implementation(libs.disklrucache) implementation(libs.disklrucache)
implementation(libs.unifile) implementation(libs.unifile)
implementation(libs.junrar) implementation(libs.bundles.archive)
// Preferences // Preferences
implementation(libs.preferencektx) implementation(libs.preferencektx)

View File

@ -72,6 +72,9 @@
# XmlUtil # XmlUtil
-keep public enum nl.adaptivity.xmlutil.EventType { *; } -keep public enum nl.adaptivity.xmlutil.EventType { *; }
# Apache Commons Compress
-keep class * extends org.apache.commons.compress.archivers.zip.ZipExtraField { <init>(); }
# 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.** { *; }

View File

@ -27,7 +27,6 @@ import nl.adaptivity.xmlutil.XmlDeclMode
import nl.adaptivity.xmlutil.core.XmlVersion import nl.adaptivity.xmlutil.core.XmlVersion
import nl.adaptivity.xmlutil.serialization.XML import nl.adaptivity.xmlutil.serialization.XML
import tachiyomi.core.common.storage.AndroidStorageFolderProvider import tachiyomi.core.common.storage.AndroidStorageFolderProvider
import tachiyomi.core.common.storage.UniFileTempFileManager
import tachiyomi.data.AndroidDatabaseHandler import tachiyomi.data.AndroidDatabaseHandler
import tachiyomi.data.Database import tachiyomi.data.Database
import tachiyomi.data.DatabaseHandler import tachiyomi.data.DatabaseHandler
@ -112,8 +111,6 @@ class AppModule(val app: Application) : InjektModule {
ProtoBuf ProtoBuf
} }
addSingletonFactory { UniFileTempFileManager(app) }
addSingletonFactory { ChapterCache(app, get()) } addSingletonFactory { ChapterCache(app, get()) }
addSingletonFactory { CoverCache(app) } addSingletonFactory { CoverCache(app) }

View File

@ -55,7 +55,6 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.common.preference.toggle import tachiyomi.core.common.preference.toggle
import tachiyomi.core.common.storage.UniFileTempFileManager
import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.launchNonCancellable
import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withIOContext
@ -86,7 +85,6 @@ class ReaderViewModel @JvmOverloads constructor(
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(),
private val downloadProvider: DownloadProvider = Injekt.get(), private val downloadProvider: DownloadProvider = Injekt.get(),
private val tempFileManager: UniFileTempFileManager = Injekt.get(),
private val imageSaver: ImageSaver = Injekt.get(), private val imageSaver: ImageSaver = Injekt.get(),
preferences: BasePreferences = Injekt.get(), preferences: BasePreferences = Injekt.get(),
val readerPreferences: ReaderPreferences = Injekt.get(), val readerPreferences: ReaderPreferences = Injekt.get(),
@ -271,7 +269,7 @@ class ReaderViewModel @JvmOverloads constructor(
val context = Injekt.get<Application>() val context = Injekt.get<Application>()
val source = sourceManager.getOrStub(manga.source) val source = sourceManager.getOrStub(manga.source)
loader = ChapterLoader(context, downloadManager, downloadProvider, tempFileManager, manga, source) loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source)
loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id }) loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id })
Result.success(true) Result.success(true)
@ -907,7 +905,6 @@ class ReaderViewModel @JvmOverloads constructor(
private fun deletePendingChapters() { private fun deletePendingChapters() {
viewModelScope.launchNonCancellable { viewModelScope.launchNonCancellable {
downloadManager.deletePendingChapters() downloadManager.deletePendingChapters()
tempFileManager.deleteTempFiles()
} }
} }

View File

@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.storage.UniFileTempFileManager import tachiyomi.core.common.storage.openReadOnlyChannel
import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
@ -24,7 +24,6 @@ class ChapterLoader(
private val context: Context, private val context: Context,
private val downloadManager: DownloadManager, private val downloadManager: DownloadManager,
private val downloadProvider: DownloadProvider, private val downloadProvider: DownloadProvider,
private val tempFileManager: UniFileTempFileManager,
private val manga: Manga, private val manga: Manga,
private val source: Source, private val source: Source,
) { ) {
@ -92,18 +91,17 @@ class ChapterLoader(
source, source,
downloadManager, downloadManager,
downloadProvider, downloadProvider,
tempFileManager,
) )
source is LocalSource -> source.getFormat(chapter.chapter).let { format -> source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
when (format) { when (format) {
is Format.Directory -> DirectoryPageLoader(format.file) is Format.Directory -> DirectoryPageLoader(format.file)
is Format.Zip -> ZipPageLoader(tempFileManager.createTempFile(format.file)) is Format.Zip -> ZipPageLoader(format.file.openReadOnlyChannel(context))
is Format.Rar -> try { is Format.Rar -> try {
RarPageLoader(tempFileManager.createTempFile(format.file)) RarPageLoader(format.file.openInputStream())
} catch (e: UnsupportedRarV5Exception) { } catch (e: UnsupportedRarV5Exception) {
error(context.stringResource(MR.strings.loader_rar5_error)) error(context.stringResource(MR.strings.loader_rar5_error))
} }
is Format.Epub -> EpubPageLoader(tempFileManager.createTempFile(format.file)) is Format.Epub -> EpubPageLoader(format.file.openReadOnlyChannel(context))
} }
} }
source is HttpSource -> HttpPageLoader(chapter, source) source is HttpSource -> HttpPageLoader(chapter, source)

View File

@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import tachiyomi.core.common.storage.UniFileTempFileManager import tachiyomi.core.common.storage.openReadOnlyChannel
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -23,7 +23,6 @@ internal class DownloadPageLoader(
private val source: Source, private val source: Source,
private val downloadManager: DownloadManager, private val downloadManager: DownloadManager,
private val downloadProvider: DownloadProvider, private val downloadProvider: DownloadProvider,
private val tempFileManager: UniFileTempFileManager,
) : PageLoader() { ) : PageLoader() {
private val context: Application by injectLazy() private val context: Application by injectLazy()
@ -48,7 +47,7 @@ internal class DownloadPageLoader(
} }
private suspend fun getPagesFromArchive(file: UniFile): List<ReaderPage> { private suspend fun getPagesFromArchive(file: UniFile): List<ReaderPage> {
val loader = ZipPageLoader(tempFileManager.createTempFile(file)).also { zipPageLoader = it } val loader = ZipPageLoader(file.openReadOnlyChannel(context)).also { zipPageLoader = it }
return loader.getPages() return loader.getPages()
} }

View File

@ -3,14 +3,14 @@ package eu.kanade.tachiyomi.ui.reader.loader
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.util.storage.EpubFile import eu.kanade.tachiyomi.util.storage.EpubFile
import java.io.File import java.nio.channels.SeekableByteChannel
/** /**
* Loader used to load a chapter from a .epub file. * Loader used to load a chapter from a .epub file.
*/ */
internal class EpubPageLoader(file: File) : PageLoader() { internal class EpubPageLoader(channel: SeekableByteChannel) : PageLoader() {
private val epub = EpubFile(file) private val epub = EpubFile(channel)
override var isLocal: Boolean = true override var isLocal: Boolean = true

View File

@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.ImageUtil
import java.io.File
import java.io.InputStream import java.io.InputStream
import java.io.PipedInputStream import java.io.PipedInputStream
import java.io.PipedOutputStream import java.io.PipedOutputStream
@ -15,9 +14,9 @@ import java.util.concurrent.Executors
/** /**
* Loader used to load a chapter from a .rar or .cbr file. * Loader used to load a chapter from a .rar or .cbr file.
*/ */
internal class RarPageLoader(file: File) : PageLoader() { internal class RarPageLoader(inputStream: InputStream) : PageLoader() {
private val rar = Archive(file) private val rar = Archive(inputStream)
override var isLocal: Boolean = true override var isLocal: Boolean = true

View File

@ -3,22 +3,21 @@ package eu.kanade.tachiyomi.ui.reader.loader
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
import org.apache.commons.compress.archivers.zip.ZipFile
import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.ImageUtil
import java.io.File import java.nio.channels.SeekableByteChannel
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(channel: SeekableByteChannel) : PageLoader() {
private val zip = ZipFile(file, StandardCharsets.ISO_8859_1) private val zip = ZipFile(channel)
override var isLocal: Boolean = true override var isLocal: Boolean = true
override suspend fun getPages(): List<ReaderPage> { override suspend fun getPages(): List<ReaderPage> {
return zip.entries().asSequence() return zip.entries.asSequence()
.filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } .filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
.mapIndexed { i, entry -> .mapIndexed { i, entry ->

View File

@ -32,6 +32,7 @@ dependencies {
implementation(libs.image.decoder) implementation(libs.image.decoder)
implementation(libs.unifile) implementation(libs.unifile)
implementation(libs.bundles.archive)
api(kotlinx.coroutines.core) api(kotlinx.coroutines.core)
api(kotlinx.serialization.json) api(kotlinx.serialization.json)

View File

@ -1,22 +1,23 @@
package eu.kanade.tachiyomi.util.storage package eu.kanade.tachiyomi.util.storage
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipFile
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import java.io.Closeable import java.io.Closeable
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.util.zip.ZipEntry import java.nio.channels.SeekableByteChannel
import java.util.zip.ZipFile
/** /**
* Wrapper over ZipFile to load files in epub format. * Wrapper over ZipFile to load files in epub format.
*/ */
class EpubFile(file: File) : Closeable { class EpubFile(channel: SeekableByteChannel) : Closeable {
/** /**
* Zip file of this epub. * Zip file of this epub.
*/ */
private val zip = ZipFile(file) private val zip = ZipFile(channel)
/** /**
* Path separator used by this epub. * Path separator used by this epub.
@ -33,14 +34,14 @@ class EpubFile(file: File) : Closeable {
/** /**
* Returns an input stream for reading the contents of the specified zip file entry. * Returns an input stream for reading the contents of the specified zip file entry.
*/ */
fun getInputStream(entry: ZipEntry): InputStream { fun getInputStream(entry: ZipArchiveEntry): InputStream {
return zip.getInputStream(entry) return zip.getInputStream(entry)
} }
/** /**
* Returns the zip file entry for the specified name, or null if not found. * Returns the zip file entry for the specified name, or null if not found.
*/ */
fun getEntry(name: String): ZipEntry? { fun getEntry(name: String): ZipArchiveEntry? {
return zip.getEntry(name) return zip.getEntry(name)
} }

View File

@ -1,6 +1,9 @@
package tachiyomi.core.common.storage package tachiyomi.core.common.storage
import android.content.Context
import android.os.ParcelFileDescriptor
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import java.nio.channels.FileChannel
val UniFile.extension: String? val UniFile.extension: String?
get() = name?.substringAfterLast('.') get() = name?.substringAfterLast('.')
@ -10,3 +13,7 @@ val UniFile.nameWithoutExtension: String?
val UniFile.displayablePath: String val UniFile.displayablePath: String
get() = filePath ?: uri.toString() get() = filePath ?: uri.toString()
fun UniFile.openReadOnlyChannel(context: Context): FileChannel {
return ParcelFileDescriptor.AutoCloseInputStream(context.contentResolver.openFileDescriptor(uri, "r")).channel
}

View File

@ -1,46 +0,0 @@
package tachiyomi.core.common.storage
import android.content.Context
import android.os.Build
import android.os.FileUtils
import com.hippo.unifile.UniFile
import java.io.BufferedOutputStream
import java.io.File
class UniFileTempFileManager(
private val context: Context,
) {
private val dir = File(context.externalCacheDir, "tmp")
fun createTempFile(file: UniFile): File {
dir.mkdirs()
val inputStream = context.contentResolver.openInputStream(file.uri)!!
val tempFile = File.createTempFile(
file.nameWithoutExtension.orEmpty().padEnd(3), // Prefix must be 3+ chars
null,
dir,
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
FileUtils.copy(inputStream, tempFile.outputStream())
} else {
BufferedOutputStream(tempFile.outputStream()).use { tmpOut ->
inputStream.use { input ->
val buffer = ByteArray(8192)
var count: Int
while (input.read(buffer).also { count = it } > 0) {
tmpOut.write(buffer, 0, count)
}
}
}
}
return tempFile
}
fun deleteTempFiles() {
dir.deleteRecursively()
}
}

View File

@ -32,6 +32,7 @@ jsoup = "org.jsoup:jsoup:1.17.2"
disklrucache = "com.jakewharton:disklrucache:2.0.2" disklrucache = "com.jakewharton:disklrucache:2.0.2"
unifile = "com.github.tachiyomiorg:unifile:7c257e1c64" unifile = "com.github.tachiyomiorg:unifile:7c257e1c64"
common-compress = "org.apache.commons:commons-compress:1.25.0"
junrar = "com.github.junrar:junrar:7.5.5" junrar = "com.github.junrar:junrar:7.5.5"
sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" } sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" }
@ -100,6 +101,7 @@ detekt-rules-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatt
detekt-rules-compose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" } detekt-rules-compose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" }
[bundles] [bundles]
archive = ["common-compress", "junrar"]
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"] okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"]
js-engine = ["quickjs-android"] js-engine = ["quickjs-android"]
sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"] sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"]

View File

@ -12,7 +12,7 @@ kotlin {
api(projects.i18n) api(projects.i18n)
implementation(libs.unifile) implementation(libs.unifile)
implementation(libs.junrar) implementation(libs.bundles.archive)
} }
} }
val androidMain by getting { val androidMain by getting {

View File

@ -18,15 +18,16 @@ import kotlinx.serialization.json.decodeFromStream
import logcat.LogPriority import logcat.LogPriority
import nl.adaptivity.xmlutil.AndroidXmlReader import nl.adaptivity.xmlutil.AndroidXmlReader
import nl.adaptivity.xmlutil.serialization.XML import nl.adaptivity.xmlutil.serialization.XML
import org.apache.commons.compress.archivers.zip.ZipFile
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.metadata.comicinfo.ComicInfo
import tachiyomi.core.metadata.comicinfo.copyFromComicInfo import tachiyomi.core.metadata.comicinfo.copyFromComicInfo
import tachiyomi.core.metadata.comicinfo.getComicInfo import tachiyomi.core.metadata.comicinfo.getComicInfo
import tachiyomi.core.metadata.tachiyomi.MangaDetails import tachiyomi.core.metadata.tachiyomi.MangaDetails
import tachiyomi.core.common.storage.UniFileTempFileManager
import tachiyomi.core.common.storage.extension import tachiyomi.core.common.storage.extension
import tachiyomi.core.common.storage.nameWithoutExtension import tachiyomi.core.common.storage.nameWithoutExtension
import tachiyomi.core.common.storage.openReadOnlyChannel
import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
@ -43,7 +44,6 @@ import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.util.zip.ZipFile
import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.days
import com.github.junrar.Archive as JunrarArchive import com.github.junrar.Archive as JunrarArchive
import tachiyomi.domain.source.model.Source as DomainSource import tachiyomi.domain.source.model.Source as DomainSource
@ -56,7 +56,6 @@ actual class LocalSource(
private val json: Json by injectLazy() private val json: Json by injectLazy()
private val xml: XML by injectLazy() private val xml: XML by injectLazy()
private val tempFileManager: UniFileTempFileManager by injectLazy()
private val POPULAR_FILTERS = FilterList(OrderBy.Popular(context)) private val POPULAR_FILTERS = FilterList(OrderBy.Popular(context))
private val LATEST_FILTERS = FilterList(OrderBy.Latest(context)) private val LATEST_FILTERS = FilterList(OrderBy.Latest(context))
@ -214,7 +213,7 @@ actual class LocalSource(
for (chapter in chapterArchives) { for (chapter in chapterArchives) {
when (Format.valueOf(chapter)) { when (Format.valueOf(chapter)) {
is Format.Zip -> { is Format.Zip -> {
ZipFile(tempFileManager.createTempFile(chapter)).use { zip: ZipFile -> ZipFile(chapter.openReadOnlyChannel(context)).use { zip: ZipFile ->
zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile -> zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile ->
zip.getInputStream(comicInfoFile).buffered().use { stream -> zip.getInputStream(comicInfoFile).buffered().use { stream ->
return copyComicInfoFile(stream, folderPath) return copyComicInfoFile(stream, folderPath)
@ -223,7 +222,7 @@ actual class LocalSource(
} }
} }
is Format.Rar -> { is Format.Rar -> {
JunrarArchive(tempFileManager.createTempFile(chapter)).use { rar -> JunrarArchive(chapter.openInputStream()).use { rar ->
rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile -> rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile ->
rar.getInputStream(comicInfoFile).buffered().use { stream -> rar.getInputStream(comicInfoFile).buffered().use { stream ->
return copyComicInfoFile(stream, folderPath) return copyComicInfoFile(stream, folderPath)
@ -273,7 +272,7 @@ actual class LocalSource(
val format = Format.valueOf(chapterFile) val format = Format.valueOf(chapterFile)
if (format is Format.Epub) { if (format is Format.Epub) {
EpubFile(tempFileManager.createTempFile(format.file)).use { epub -> EpubFile(format.file.openReadOnlyChannel(context)).use { epub ->
epub.fillMetadata(manga, this) epub.fillMetadata(manga, this)
} }
} }
@ -332,8 +331,8 @@ actual class LocalSource(
entry?.let { coverManager.update(manga, it.openInputStream()) } entry?.let { coverManager.update(manga, it.openInputStream()) }
} }
is Format.Zip -> { is Format.Zip -> {
ZipFile(tempFileManager.createTempFile(format.file)).use { zip -> ZipFile(format.file.openReadOnlyChannel(context)).use { zip ->
val entry = zip.entries().toList() val entry = zip.entries.toList()
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } .find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
@ -341,7 +340,7 @@ actual class LocalSource(
} }
} }
is Format.Rar -> { is Format.Rar -> {
JunrarArchive(tempFileManager.createTempFile(format.file)).use { archive -> JunrarArchive(format.file.openInputStream()).use { archive ->
val entry = archive.fileHeaders val entry = archive.fileHeaders
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) } .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
.find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } } .find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
@ -350,7 +349,7 @@ actual class LocalSource(
} }
} }
is Format.Epub -> { is Format.Epub -> {
EpubFile(tempFileManager.createTempFile(format.file)).use { epub -> EpubFile(format.file.openReadOnlyChannel(context)).use { epub ->
val entry = epub.getImagesFromPages() val entry = epub.getImagesFromPages()
.firstOrNull() .firstOrNull()
?.let { epub.getEntry(it) } ?.let { epub.getEntry(it) }