Browse Source

Remove tmp chapter files after exiting reader

arkon 1 year ago
parent
commit
4e221397ce

+ 3 - 0
app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt

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

+ 4 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt

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

+ 13 - 5
app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt

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

+ 4 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt

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

+ 0 - 29
core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt

@@ -1,11 +1,6 @@
 package tachiyomi.core.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
 
 val UniFile.extension: String?
     get() = name?.substringAfterLast('.')
@@ -15,27 +10,3 @@ val UniFile.nameWithoutExtension: String?
 
 val UniFile.displayablePath: String
     get() = filePath ?: uri.toString()
-
-fun UniFile.toTempFile(context: Context): File {
-    val inputStream = context.contentResolver.openInputStream(uri)!!
-    val tempFile = File.createTempFile(
-        nameWithoutExtension.orEmpty().padEnd(3), // Prefix must be 3+ chars
-        null,
-    )
-
-    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
-}

+ 44 - 0
core/src/main/java/tachiyomi/core/storage/UniFileTempFileManager.kt

@@ -0,0 +1,44 @@
+package tachiyomi.core.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").also { it.mkdir() }
+
+    fun createTempFile(file: UniFile): File {
+        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()
+    }
+}

+ 8 - 7
source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt

@@ -24,9 +24,9 @@ import tachiyomi.core.metadata.comicinfo.ComicInfo
 import tachiyomi.core.metadata.comicinfo.copyFromComicInfo
 import tachiyomi.core.metadata.comicinfo.getComicInfo
 import tachiyomi.core.metadata.tachiyomi.MangaDetails
+import tachiyomi.core.storage.UniFileTempFileManager
 import tachiyomi.core.storage.extension
 import tachiyomi.core.storage.nameWithoutExtension
-import tachiyomi.core.storage.toTempFile
 import tachiyomi.core.util.lang.withIOContext
 import tachiyomi.core.util.system.ImageUtil
 import tachiyomi.core.util.system.logcat
@@ -56,6 +56,7 @@ actual class LocalSource(
 
     private val json: Json by injectLazy()
     private val xml: XML by injectLazy()
+    private val tempFileManager: UniFileTempFileManager by injectLazy()
 
     private val POPULAR_FILTERS = FilterList(OrderBy.Popular(context))
     private val LATEST_FILTERS = FilterList(OrderBy.Latest(context))
@@ -213,7 +214,7 @@ actual class LocalSource(
         for (chapter in chapterArchives) {
             when (Format.valueOf(chapter)) {
                 is Format.Zip -> {
-                    ZipFile(chapter.toTempFile(context)).use { zip: ZipFile ->
+                    ZipFile(tempFileManager.createTempFile(chapter)).use { zip: ZipFile ->
                         zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile ->
                             zip.getInputStream(comicInfoFile).buffered().use { stream ->
                                 return copyComicInfoFile(stream, folderPath)
@@ -222,7 +223,7 @@ actual class LocalSource(
                     }
                 }
                 is Format.Rar -> {
-                    JunrarArchive(chapter.toTempFile(context)).use { rar ->
+                    JunrarArchive(tempFileManager.createTempFile(chapter)).use { rar ->
                         rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile ->
                             rar.getInputStream(comicInfoFile).buffered().use { stream ->
                                 return copyComicInfoFile(stream, folderPath)
@@ -272,7 +273,7 @@ actual class LocalSource(
 
                     val format = Format.valueOf(chapterFile)
                     if (format is Format.Epub) {
-                        EpubFile(format.file.toTempFile(context)).use { epub ->
+                        EpubFile(tempFileManager.createTempFile(format.file)).use { epub ->
                             epub.fillMetadata(manga, this)
                         }
                     }
@@ -331,7 +332,7 @@ actual class LocalSource(
                     entry?.let { coverManager.update(manga, it.openInputStream()) }
                 }
                 is Format.Zip -> {
-                    ZipFile(format.file.toTempFile(context)).use { zip ->
+                    ZipFile(tempFileManager.createTempFile(format.file)).use { zip ->
                         val entry = zip.entries().toList()
                             .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
                             .find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
@@ -340,7 +341,7 @@ actual class LocalSource(
                     }
                 }
                 is Format.Rar -> {
-                    JunrarArchive(format.file.toTempFile(context)).use { archive ->
+                    JunrarArchive(tempFileManager.createTempFile(format.file)).use { archive ->
                         val entry = archive.fileHeaders
                             .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
                             .find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
@@ -349,7 +350,7 @@ actual class LocalSource(
                     }
                 }
                 is Format.Epub -> {
-                    EpubFile(format.file.toTempFile(context)).use { epub ->
+                    EpubFile(tempFileManager.createTempFile(format.file)).use { epub ->
                         val entry = epub.getImagesFromPages()
                             .firstOrNull()
                             ?.let { epub.getEntry(it) }