Remove tmp chapter files after exiting reader
Cette révision appartient à :
Parent
8a7d6a328a
révision
4e221397ce
7 fichiers modifiés avec 76 ajouts et 45 suppressions
|
@ -27,6 +27,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
|
||||||
|
@ -111,6 +112,8 @@ 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) }
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,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
|
||||||
|
@ -85,6 +86,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(),
|
||||||
|
@ -269,7 +271,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, manga, source)
|
loader = ChapterLoader(context, downloadManager, downloadProvider, tempFileManager, manga, source)
|
||||||
|
|
||||||
loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id })
|
loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id })
|
||||||
Result.success(true)
|
Result.success(true)
|
||||||
|
@ -904,6 +906,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||||
private fun deletePendingChapters() {
|
private fun deletePendingChapters() {
|
||||||
viewModelScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
downloadManager.deletePendingChapters()
|
downloadManager.deletePendingChapters()
|
||||||
|
tempFileManager.deleteTempFiles()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.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
|
||||||
|
@ -24,6 +24,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,
|
||||||
) {
|
) {
|
||||||
|
@ -85,17 +86,24 @@ class ChapterLoader(
|
||||||
skipCache = true,
|
skipCache = true,
|
||||||
)
|
)
|
||||||
return when {
|
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 ->
|
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)
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
44
core/src/main/java/tachiyomi/core/storage/UniFileTempFileManager.kt
Fichier normal
44
core/src/main/java/tachiyomi/core/storage/UniFileTempFileManager.kt
Fichier normal
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,9 +24,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
|
||||||
|
@ -56,6 +56,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))
|
||||||
|
@ -213,7 +214,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 ->
|
||||||
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)
|
||||||
|
@ -222,7 +223,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)
|
||||||
|
@ -272,7 +273,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -331,7 +332,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 ->
|
||||||
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) } }
|
||||||
|
@ -340,7 +341,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) } }
|
||||||
|
@ -349,7 +350,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) }
|
||||||
|
|
Référencer dans un nouveau ticket