Remove tmp chapter files after exiting reader

(cherry picked from commit 4e221397ceaec334307546920b3e1168e56f5433)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt
#	source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt
This commit is contained in:
arkon 2024-01-04 18:02:40 -05:00 committed by Jobobby04
parent f2922e5f17
commit c8ca321be2
7 changed files with 72 additions and 47 deletions

View File

@ -32,6 +32,7 @@ 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.storage.AndroidStorageFolderProvider import tachiyomi.core.storage.AndroidStorageFolderProvider
import tachiyomi.core.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
@ -139,6 +140,8 @@ class AppModule(val app: Application) : InjektModule {
ProtoBuf ProtoBuf
} }
addSingletonFactory { UniFileTempFileManager(app) }
addSingletonFactory { ChapterCache(app, get(), get()) } addSingletonFactory { ChapterCache(app, get(), get()) }
addSingletonFactory { CoverCache(app) } addSingletonFactory { CoverCache(app) }

View File

@ -71,6 +71,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.preference.toggle import tachiyomi.core.preference.toggle
import tachiyomi.core.storage.UniFileTempFileManager
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.launchNonCancellable import tachiyomi.core.util.lang.launchNonCancellable
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
@ -108,6 +109,7 @@ 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(),
@ -380,6 +382,7 @@ class ReaderViewModel @JvmOverloads constructor(
context = context, context = context,
downloadManager = downloadManager, downloadManager = downloadManager,
downloadProvider = downloadProvider, downloadProvider = downloadProvider,
tempFileManager = tempFileManager,
manga = manga, manga = manga,
source = source, /* SY --> */ source = source, /* SY --> */
sourceManager = sourceManager, sourceManager = sourceManager,
@ -1274,6 +1277,7 @@ class ReaderViewModel @JvmOverloads constructor(
private fun deletePendingChapters() { private fun deletePendingChapters() {
viewModelScope.launchNonCancellable { viewModelScope.launchNonCancellable {
downloadManager.deletePendingChapters() downloadManager.deletePendingChapters()
tempFileManager.deleteTempFiles()
} }
} }

View File

@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import tachiyomi.core.i18n.stringResource 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.lang.withIOContext
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
@ -28,6 +28,7 @@ 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,
// SY --> // SY -->
@ -125,13 +126,13 @@ class ChapterLoader(
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(format.file.toTempFile(context)) is Format.Zip -> ZipPageLoader(tempFileManager.createTempFile(format.file))
is Format.Rar -> try { is Format.Rar -> try {
RarPageLoader(format.file.toTempFile(context)) RarPageLoader(tempFileManager.createTempFile(format.file))
} 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(format.file.toTempFile(context)) is Format.Epub -> EpubPageLoader(tempFileManager.createTempFile(format.file))
} }
} }
else -> error(context.stringResource(MR.strings.loader_not_implemented_error)) else -> error(context.stringResource(MR.strings.loader_not_implemented_error))
@ -142,13 +143,13 @@ class ChapterLoader(
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(format.file.toTempFile(context)) is Format.Zip -> ZipPageLoader(tempFileManager.createTempFile(format.file))
is Format.Rar -> try { is Format.Rar -> try {
RarPageLoader(format.file.toTempFile(context)) RarPageLoader(tempFileManager.createTempFile(format.file))
} 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(format.file.toTempFile(context)) is Format.Epub -> EpubPageLoader(tempFileManager.createTempFile(format.file))
} }
} }
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.storage.toTempFile import tachiyomi.core.storage.UniFileTempFileManager
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -23,6 +23,7 @@ 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()
@ -46,8 +47,8 @@ internal class DownloadPageLoader(
zipPageLoader?.recycle() zipPageLoader?.recycle()
} }
private suspend fun getPagesFromArchive(chapterPath: UniFile): List<ReaderPage> { private suspend fun getPagesFromArchive(file: UniFile): List<ReaderPage> {
val loader = ZipPageLoader(chapterPath.toTempFile(context)).also { zipPageLoader = it } val loader = ZipPageLoader(tempFileManager.createTempFile(file)).also { zipPageLoader = it }
return loader.getPages() return loader.getPages()
} }

View File

@ -1,11 +1,6 @@
package tachiyomi.core.storage package tachiyomi.core.storage
import android.content.Context
import android.os.Build
import android.os.FileUtils
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import java.io.BufferedOutputStream
import java.io.File
val UniFile.extension: String? val UniFile.extension: String?
get() = name?.substringAfterLast('.') get() = name?.substringAfterLast('.')
@ -15,27 +10,3 @@ val UniFile.nameWithoutExtension: String?
val UniFile.displayablePath: String val UniFile.displayablePath: String
get() = filePath ?: uri.toString() 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
}

View File

@ -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()
}
}

View File

@ -28,9 +28,9 @@ 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.storage.UniFileTempFileManager
import tachiyomi.core.storage.extension import tachiyomi.core.storage.extension
import tachiyomi.core.storage.nameWithoutExtension import tachiyomi.core.storage.nameWithoutExtension
import tachiyomi.core.storage.toTempFile
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.ImageUtil import tachiyomi.core.util.system.ImageUtil
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
@ -62,6 +62,7 @@ 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))
@ -198,7 +199,7 @@ actual class LocalSource(
} }
// SY --> // SY -->
comicInfoArchiveFile != null -> { comicInfoArchiveFile != null -> {
val comicInfoArchive = ZipFile(comicInfoArchiveFile.toTempFile(context)) val comicInfoArchive = ZipFile(tempFileManager.createTempFile(comicInfoArchiveFile))
noXmlFile?.delete() noXmlFile?.delete()
if (CbzCrypto.checkCbzPassword(comicInfoArchive, CbzCrypto.getDecryptedPasswordCbz())) { if (CbzCrypto.checkCbzPassword(comicInfoArchive, CbzCrypto.getDecryptedPasswordCbz())) {
@ -268,7 +269,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(chapter.toTempFile(context)).use { zip: ZipFile -> ZipFile(tempFileManager.createTempFile(chapter)).use { zip: ZipFile ->
// SY --> // SY -->
if (zip.isEncrypted && !CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz()) if (zip.isEncrypted && !CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())
) { ) {
@ -287,7 +288,7 @@ actual class LocalSource(
} }
} }
is Format.Rar -> { 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.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)
@ -353,7 +354,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(format.file.toTempFile(context)).use { epub -> EpubFile(tempFileManager.createTempFile(format.file)).use { epub ->
epub.fillMetadata(manga, this) epub.fillMetadata(manga, this)
} }
} }
@ -412,7 +413,7 @@ actual class LocalSource(
entry?.let { coverManager.update(manga, it.openInputStream()) } entry?.let { coverManager.update(manga, it.openInputStream()) }
} }
is Format.Zip -> { is Format.Zip -> {
ZipFile(format.file.toTempFile(context)).use { zip -> ZipFile(tempFileManager.createTempFile(format.file)).use { zip ->
// SY --> // SY -->
var encrypted = false var encrypted = false
if (zip.isEncrypted) { if (zip.isEncrypted) {
@ -427,7 +428,7 @@ actual class LocalSource(
} }
} }
is Format.Rar -> { is Format.Rar -> {
JunrarArchive(format.file.toTempFile(context)).use { archive -> JunrarArchive(tempFileManager.createTempFile(format.file)).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) } }
@ -436,7 +437,7 @@ actual class LocalSource(
} }
} }
is Format.Epub -> { is Format.Epub -> {
EpubFile(format.file.toTempFile(context)).use { epub -> EpubFile(tempFileManager.createTempFile(format.file)).use { epub ->
val entry = epub.getImagesFromPages() val entry = epub.getImagesFromPages()
.firstOrNull() .firstOrNull()
?.let { epub.getEntry(it) } ?.let { epub.getEntry(it) }