|
@@ -1,61 +1,57 @@
|
|
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 eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
|
|
|
-import tachiyomi.core.util.system.ImageUtil
|
|
|
|
|
|
+import uy.kohesive.injekt.injectLazy
|
|
import java.io.File
|
|
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
|
|
-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(file: File) : PageLoader() {
|
|
|
|
|
|
- private val rar = Archive(file)
|
|
|
|
|
|
+ private val context: Application by injectLazy()
|
|
|
|
+ private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also {
|
|
|
|
+ it.deleteRecursively()
|
|
|
|
+ it.mkdirs()
|
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
|
- * Pool for copying compressed files to an input stream.
|
|
|
|
- */
|
|
|
|
- private val pool = Executors.newFixedThreadPool(1)
|
|
|
|
|
|
+ init {
|
|
|
|
+ Archive(file).use { rar ->
|
|
|
|
+ rar.fileHeaders.asSequence()
|
|
|
|
+ .filterNot { it.isDirectory }
|
|
|
|
+ .forEach { header ->
|
|
|
|
+ val pageFile = File(tmpDir, header.fileName).also { it.createNewFile() }
|
|
|
|
+ getStream(rar, header).use {
|
|
|
|
+ it.copyTo(pageFile.outputStream())
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
override var isLocal: Boolean = true
|
|
override var isLocal: Boolean = true
|
|
|
|
|
|
override suspend fun getPages(): List<ReaderPage> {
|
|
override suspend fun getPages(): List<ReaderPage> {
|
|
- 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(header) }
|
|
|
|
- status = Page.State.READY
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- .toList()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- override suspend fun loadPage(page: ReaderPage) {
|
|
|
|
- check(!isRecycled)
|
|
|
|
|
|
+ return DirectoryPageLoader(tmpDir).getPages()
|
|
}
|
|
}
|
|
|
|
|
|
override fun recycle() {
|
|
override fun recycle() {
|
|
super.recycle()
|
|
super.recycle()
|
|
- rar.close()
|
|
|
|
- pool.shutdown()
|
|
|
|
|
|
+ tmpDir.deleteRecursively()
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Returns an input stream for the given [header].
|
|
* Returns an input stream for the given [header].
|
|
*/
|
|
*/
|
|
- private fun getStream(header: FileHeader): InputStream {
|
|
|
|
|
|
+ private fun getStream(rar: Archive, header: FileHeader): InputStream {
|
|
val pipeIn = PipedInputStream()
|
|
val pipeIn = PipedInputStream()
|
|
val pipeOut = PipedOutputStream(pipeIn)
|
|
val pipeOut = PipedOutputStream(pipeIn)
|
|
- pool.execute {
|
|
|
|
|
|
+ synchronized(this) {
|
|
try {
|
|
try {
|
|
pipeOut.use {
|
|
pipeOut.use {
|
|
rar.extractFile(header, it)
|
|
rar.extractFile(header, it)
|