From ca5498434409d4085c404f4ff5ed5e608f430a3b Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 26 Nov 2023 15:59:31 -0500 Subject: [PATCH] Use UniFile for local source file handling --- .../java/eu/kanade/tachiyomi/di/AppModule.kt | 4 +- .../kanade/tachiyomi/di/PreferenceModule.kt | 2 +- .../ui/reader/loader/DirectoryPageLoader.kt | 11 ++- .../ui/reader/loader/DownloadPageLoader.kt | 3 +- .../ui/reader/loader/EpubPageLoader.kt | 4 +- .../ui/reader/loader/RarPageLoader.kt | 7 +- .../ui/reader/loader/ZipPageLoader.kt | 9 +-- .../kanade/tachiyomi/util/storage/EpubFile.kt | 6 +- .../AndroidStorageFolderProvider.kt | 2 +- .../{provider => storage}/FolderProvider.kt | 2 +- .../core/storage/UniFileExtensions.kt | 3 + .../tachiyomi/core/util/system/ImageUtil.kt | 4 +- .../storage/service/StoragePreferences.kt | 2 +- .../tachiyomi/source/local/LocalSource.kt | 68 +++++++++++-------- .../source/local/image/LocalCoverManager.kt | 22 +++--- .../source/local/io/LocalSourceFileSystem.kt | 20 +++--- .../source/local/image/LocalCoverManager.kt | 6 +- .../tachiyomi/source/local/io/Archive.kt | 7 +- .../tachiyomi/source/local/io/Format.kt | 13 ++-- .../source/local/io/LocalSourceFileSystem.kt | 10 +-- 20 files changed, 110 insertions(+), 95 deletions(-) rename core/src/main/java/tachiyomi/core/{provider => storage}/AndroidStorageFolderProvider.kt (94%) rename core/src/main/java/tachiyomi/core/{provider => storage}/FolderProvider.kt (76%) diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt index 7a527329f6..2744644f89 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt @@ -25,7 +25,7 @@ import kotlinx.serialization.json.Json import nl.adaptivity.xmlutil.XmlDeclMode import nl.adaptivity.xmlutil.core.XmlVersion import nl.adaptivity.xmlutil.serialization.XML -import tachiyomi.core.provider.AndroidStorageFolderProvider +import tachiyomi.core.storage.AndroidStorageFolderProvider import tachiyomi.data.AndroidDatabaseHandler import tachiyomi.data.Database import tachiyomi.data.DatabaseHandler @@ -125,7 +125,7 @@ class AppModule(val app: Application) : InjektModule { addSingletonFactory { ImageSaver(app) } addSingletonFactory { AndroidStorageFolderProvider(app) } - addSingletonFactory { LocalSourceFileSystem(get()) } + addSingletonFactory { LocalSourceFileSystem(app, get()) } addSingletonFactory { LocalCoverManager(app, get()) } // Asynchronously init expensive components for a faster cold start diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt index 257ff5dd43..51136226b9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt @@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.util.system.isDevFlavor import tachiyomi.core.preference.AndroidPreferenceStore import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.provider.AndroidStorageFolderProvider +import tachiyomi.core.storage.AndroidStorageFolderProvider import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.library.service.LibraryPreferences diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt index 837986b282..2a11f74e36 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt @@ -1,25 +1,24 @@ package eu.kanade.tachiyomi.ui.reader.loader +import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder import tachiyomi.core.util.system.ImageUtil -import java.io.File -import java.io.FileInputStream /** * Loader used to load a chapter from a directory given on [file]. */ -internal class DirectoryPageLoader(val file: File) : PageLoader() { +internal class DirectoryPageLoader(val file: UniFile) : PageLoader() { override var isLocal: Boolean = true override suspend fun getPages(): List { return file.listFiles() - ?.filter { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } } - ?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } + ?.filter { !it.isDirectory && ImageUtil.isImage(it.name) { it.openInputStream() } } + ?.sortedWith { f1, f2 -> f1.name.orEmpty().compareToCaseInsensitiveNaturalOrder(f2.name.orEmpty()) } ?.mapIndexed { i, file -> - val streamFn = { FileInputStream(file) } + val streamFn = { file.openInputStream() } ReaderPage(i).apply { stream = streamFn status = Page.State.READY diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt index 64b6a73f58..4fb5adb6c4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt @@ -12,7 +12,6 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import tachiyomi.domain.manga.model.Manga import uy.kohesive.injekt.injectLazy -import java.io.File /** * Loader used to load a chapter from the downloaded chapters. @@ -47,7 +46,7 @@ internal class DownloadPageLoader( } private suspend fun getPagesFromArchive(chapterPath: UniFile): List { - val loader = ZipPageLoader(File(chapterPath.filePath!!)).also { zipPageLoader = it } + val loader = ZipPageLoader(chapterPath).also { zipPageLoader = it } return loader.getPages() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt index 324af51bf3..cd00e37561 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt @@ -1,14 +1,14 @@ package eu.kanade.tachiyomi.ui.reader.loader +import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.storage.EpubFile -import java.io.File /** * Loader used to load a chapter from a .epub file. */ -internal class EpubPageLoader(file: File) : PageLoader() { +internal class EpubPageLoader(file: UniFile) : PageLoader() { private val epub = EpubFile(file) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt index 056319d4e6..319179117d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt @@ -2,11 +2,12 @@ package eu.kanade.tachiyomi.ui.reader.loader import com.github.junrar.Archive import com.github.junrar.rarfile.FileHeader +import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder +import tachiyomi.core.storage.toFile import tachiyomi.core.util.system.ImageUtil -import java.io.File import java.io.InputStream import java.io.PipedInputStream import java.io.PipedOutputStream @@ -14,9 +15,9 @@ import java.io.PipedOutputStream /** * Loader used to load a chapter from a .rar or .cbr file. */ -internal class RarPageLoader(file: File) : PageLoader() { +internal class RarPageLoader(file: UniFile) : PageLoader() { - private val rar = Archive(file) + private val rar = Archive(file.toFile()) override var isLocal: Boolean = true diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt index e04fe78e6a..b63b325578 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt @@ -1,23 +1,24 @@ package eu.kanade.tachiyomi.ui.reader.loader import android.os.Build +import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder +import tachiyomi.core.storage.toFile import tachiyomi.core.util.system.ImageUtil -import java.io.File import java.nio.charset.StandardCharsets import java.util.zip.ZipFile /** * Loader used to load a chapter from a .zip or .cbz file. */ -internal class ZipPageLoader(file: File) : PageLoader() { +internal class ZipPageLoader(file: UniFile) : PageLoader() { private val zip = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - ZipFile(file, StandardCharsets.ISO_8859_1) + ZipFile(file.toFile(), StandardCharsets.ISO_8859_1) } else { - ZipFile(file) + ZipFile(file.toFile()) } override var isLocal: Boolean = true diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt b/core/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt index a00ee69e75..7650f65b5e 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt +++ b/core/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt @@ -1,7 +1,9 @@ package eu.kanade.tachiyomi.util.storage +import com.hippo.unifile.UniFile import org.jsoup.Jsoup import org.jsoup.nodes.Document +import tachiyomi.core.storage.toFile import java.io.Closeable import java.io.File import java.io.InputStream @@ -11,12 +13,12 @@ import java.util.zip.ZipFile /** * Wrapper over ZipFile to load files in epub format. */ -class EpubFile(file: File) : Closeable { +class EpubFile(file: UniFile) : Closeable { /** * Zip file of this epub. */ - private val zip = ZipFile(file) + private val zip = ZipFile(file.toFile()) /** * Path separator used by this epub. diff --git a/core/src/main/java/tachiyomi/core/provider/AndroidStorageFolderProvider.kt b/core/src/main/java/tachiyomi/core/storage/AndroidStorageFolderProvider.kt similarity index 94% rename from core/src/main/java/tachiyomi/core/provider/AndroidStorageFolderProvider.kt rename to core/src/main/java/tachiyomi/core/storage/AndroidStorageFolderProvider.kt index fc2859868b..a5d48a49d4 100644 --- a/core/src/main/java/tachiyomi/core/provider/AndroidStorageFolderProvider.kt +++ b/core/src/main/java/tachiyomi/core/storage/AndroidStorageFolderProvider.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.provider +package tachiyomi.core.storage import android.content.Context import android.os.Environment diff --git a/core/src/main/java/tachiyomi/core/provider/FolderProvider.kt b/core/src/main/java/tachiyomi/core/storage/FolderProvider.kt similarity index 76% rename from core/src/main/java/tachiyomi/core/provider/FolderProvider.kt rename to core/src/main/java/tachiyomi/core/storage/FolderProvider.kt index b4e124cee0..decd1c3784 100644 --- a/core/src/main/java/tachiyomi/core/provider/FolderProvider.kt +++ b/core/src/main/java/tachiyomi/core/storage/FolderProvider.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.provider +package tachiyomi.core.storage import java.io.File diff --git a/core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt b/core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt index 9a869c8835..5343dfa3f8 100644 --- a/core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt +++ b/core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt @@ -1,9 +1,12 @@ package tachiyomi.core.storage import com.hippo.unifile.UniFile +import java.io.File val UniFile.extension: String? get() = name?.substringAfterLast('.') val UniFile.nameWithoutExtension: String? get() = name?.substringBeforeLast('.') + +fun UniFile.toFile(): File? = filePath?.let { File(it) } diff --git a/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt b/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt index b6cbc45e6b..f72a963b7d 100644 --- a/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt +++ b/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt @@ -37,7 +37,9 @@ import kotlin.math.min object ImageUtil { - fun isImage(name: String, openStream: (() -> InputStream)? = null): Boolean { + fun isImage(name: String?, openStream: (() -> InputStream)? = null): Boolean { + if (name == null) return false + val contentType = try { URLConnection.guessContentTypeFromName(name) } catch (e: Exception) { diff --git a/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt b/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt index a426caaa4f..e930ebaa42 100644 --- a/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt @@ -1,7 +1,7 @@ package tachiyomi.domain.storage.service import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.provider.FolderProvider +import tachiyomi.core.storage.FolderProvider class StoragePreferences( private val folderProvider: FolderProvider, diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index 8e2dc215c2..1c91597d08 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -22,6 +22,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.extension +import tachiyomi.core.storage.nameWithoutExtension +import tachiyomi.core.storage.toFile import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.system.ImageUtil import tachiyomi.core.util.system.logcat @@ -37,7 +40,6 @@ import tachiyomi.source.local.metadata.fillChapterMetadata import tachiyomi.source.local.metadata.fillMangaMetadata import uy.kohesive.injekt.injectLazy import java.io.File -import java.io.FileInputStream import java.io.InputStream import java.nio.charset.StandardCharsets import java.util.zip.ZipFile @@ -83,11 +85,11 @@ actual class LocalSource( } var mangaDirs = baseDirFiles // Filter out files that are hidden and is not a folder - .filter { it.isDirectory && !it.name.startsWith('.') } + .filter { it.isDirectory && !it.name.orEmpty().startsWith('.') } .distinctBy { it.name } .filter { // Filter by query or last modified if (lastModifiedLimit == 0L) { - it.name.contains(query, ignoreCase = true) + it.name.orEmpty().contains(query, ignoreCase = true) } else { it.lastModified() >= lastModifiedLimit } @@ -97,16 +99,16 @@ actual class LocalSource( when (filter) { is OrderBy.Popular -> { mangaDirs = if (filter.state!!.ascending) { - mangaDirs.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }) + mangaDirs.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name.orEmpty() }) } else { - mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name }) + mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name.orEmpty() }) } } is OrderBy.Latest -> { mangaDirs = if (filter.state!!.ascending) { - mangaDirs.sortedBy(File::lastModified) + mangaDirs.sortedBy(UniFile::lastModified) } else { - mangaDirs.sortedByDescending(File::lastModified) + mangaDirs.sortedByDescending(UniFile::lastModified) } } @@ -119,13 +121,13 @@ actual class LocalSource( // Transform mangaDirs to list of SManga val mangas = mangaDirs.map { mangaDir -> SManga.create().apply { - title = mangaDir.name - url = mangaDir.name + title = mangaDir.name.orEmpty() + url = mangaDir.name.orEmpty() // Try to find the cover - coverManager.find(mangaDir.name) - ?.takeIf(File::exists) - ?.let { thumbnail_url = it.absolutePath } + coverManager.find(mangaDir.name.orEmpty()) + ?.takeIf(UniFile::exists) + ?.let { thumbnail_url = it.uri.toString() } } } @@ -155,7 +157,7 @@ actual class LocalSource( // Manga details related override suspend fun getMangaDetails(manga: SManga): SManga = withIOContext { coverManager.find(manga.url)?.let { - manga.thumbnail_url = it.absolutePath + manga.thumbnail_url = it.uri.toString() } // Augment manga details based on metadata files @@ -174,13 +176,13 @@ actual class LocalSource( // Top level ComicInfo.xml comicInfoFile != null -> { noXmlFile?.delete() - setMangaDetailsFromComicInfoFile(comicInfoFile.inputStream(), manga) + setMangaDetailsFromComicInfoFile(comicInfoFile.openInputStream(), manga) } // Old custom JSON format // TODO: remove support for this entirely after a while legacyJsonDetailsFile != null -> { - json.decodeFromStream(legacyJsonDetailsFile.inputStream()).run { + json.decodeFromStream(legacyJsonDetailsFile.openInputStream()).run { title?.let { manga.title = it } author?.let { manga.author = it } artist?.let { manga.artist = it } @@ -190,7 +192,7 @@ actual class LocalSource( } // Replace with ComicInfo.xml file val comicInfo = manga.getComicInfo() - UniFile.fromFile(mangaDir) + mangaDir ?.createFile(COMIC_INFO_FILE) ?.openOutputStream() ?.use { @@ -206,7 +208,7 @@ actual class LocalSource( .filter(Archive::isSupported) .toList() - val folderPath = mangaDir?.absolutePath + val folderPath = mangaDir?.filePath val copiedFile = copyComicInfoFileFromArchive(chapterArchives, folderPath) if (copiedFile != null) { @@ -224,11 +226,11 @@ actual class LocalSource( return@withIOContext manga } - private fun copyComicInfoFileFromArchive(chapterArchives: List, folderPath: String?): File? { + private fun copyComicInfoFileFromArchive(chapterArchives: List, folderPath: String?): File? { for (chapter in chapterArchives) { when (Format.valueOf(chapter)) { is Format.Zip -> { - ZipFile(chapter).use { zip: ZipFile -> + ZipFile(chapter.toFile()).use { zip: ZipFile -> zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile -> zip.getInputStream(comicInfoFile).buffered().use { stream -> return copyComicInfoFile(stream, folderPath) @@ -237,7 +239,7 @@ actual class LocalSource( } } is Format.Rar -> { - JunrarArchive(chapter).use { rar -> + JunrarArchive(chapter.toFile()).use { rar -> rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile -> rar.getInputStream(comicInfoFile).buffered().use { stream -> return copyComicInfoFile(stream, folderPath) @@ -276,9 +278,9 @@ actual class LocalSource( SChapter.create().apply { url = "${manga.url}/${chapterFile.name}" name = if (chapterFile.isDirectory) { - chapterFile.name + chapterFile.name.orEmpty() } else { - chapterFile.nameWithoutExtension + chapterFile.nameWithoutExtension.orEmpty() } date_upload = chapterFile.lastModified() chapter_number = ChapterRecognition @@ -308,8 +310,8 @@ actual class LocalSource( fun getFormat(chapter: SChapter): Format { try { - return File(fileSystem.getBaseDirectory(), chapter.url) - .takeIf { it.exists() } + return fileSystem.getBaseDirectory() + ?.findFile(chapter.url) ?.let(Format.Companion::valueOf) ?: throw Exception(context.stringResource(MR.strings.chapter_not_found)) } catch (e: Format.UnknownFormatException) { @@ -319,18 +321,24 @@ actual class LocalSource( } } - private fun updateCover(chapter: SChapter, manga: SManga): File? { + private fun updateCover(chapter: SChapter, manga: SManga): UniFile? { return try { when (val format = getFormat(chapter)) { is Format.Directory -> { val entry = format.file.listFiles() - ?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } - ?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } } + ?.sortedWith { f1, f2 -> + f1.name.orEmpty().compareToCaseInsensitiveNaturalOrder( + f2.name.orEmpty(), + ) + } + ?.find { + !it.isDirectory && ImageUtil.isImage(it.name) { it.openInputStream() } + } - entry?.let { coverManager.update(manga, it.inputStream()) } + entry?.let { coverManager.update(manga, it.openInputStream()) } } is Format.Zip -> { - ZipFile(format.file).use { zip -> + ZipFile(format.file.toFile()).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) } } @@ -339,7 +347,7 @@ actual class LocalSource( } } is Format.Rar -> { - JunrarArchive(format.file).use { archive -> + JunrarArchive(format.file.toFile()).use { archive -> val entry = archive.fileHeaders .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) } .find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } } diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt index 7683756e38..b19968fb22 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt @@ -4,9 +4,9 @@ import android.content.Context import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.util.storage.DiskUtil +import tachiyomi.core.storage.nameWithoutExtension import tachiyomi.core.util.system.ImageUtil import tachiyomi.source.local.io.LocalSourceFileSystem -import java.io.File import java.io.InputStream private const val DEFAULT_COVER_NAME = "cover.jpg" @@ -16,43 +16,37 @@ actual class LocalCoverManager( private val fileSystem: LocalSourceFileSystem, ) { - actual fun find(mangaUrl: String): File? { + actual fun find(mangaUrl: String): UniFile? { return fileSystem.getFilesInMangaDirectory(mangaUrl) // Get all file whose names start with "cover" .filter { it.isFile && it.nameWithoutExtension.equals("cover", ignoreCase = true) } // Get the first actual image .firstOrNull { - ImageUtil.isImage(it.name) { it.inputStream() } + ImageUtil.isImage(it.name) { it.openInputStream() } } } actual fun update( manga: SManga, inputStream: InputStream, - ): File? { + ): UniFile? { val directory = fileSystem.getMangaDirectory(manga.url) if (directory == null) { inputStream.close() return null } - var targetFile = find(manga.url) - if (targetFile == null) { - targetFile = File(directory.absolutePath, DEFAULT_COVER_NAME) - targetFile.createNewFile() - } + val targetFile = find(manga.url) ?: directory.createFile(DEFAULT_COVER_NAME) - // It might not exist at this point - targetFile.parentFile?.mkdirs() inputStream.use { input -> - targetFile.outputStream().use { output -> + targetFile.openOutputStream().use { output -> input.copyTo(output) } } - DiskUtil.createNoMediaFile(UniFile.fromFile(directory), context) + DiskUtil.createNoMediaFile(directory, context) - manga.thumbnail_url = targetFile.absolutePath + manga.thumbnail_url = targetFile.uri.toString() return targetFile } } diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt index 9e53bc5e28..a5ce8a513a 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt @@ -1,27 +1,31 @@ package tachiyomi.source.local.io -import tachiyomi.core.provider.FolderProvider -import java.io.File +import android.content.Context +import androidx.core.net.toUri +import com.hippo.unifile.UniFile +import tachiyomi.core.storage.FolderProvider actual class LocalSourceFileSystem( + private val context: Context, private val folderProvider: FolderProvider, ) { - actual fun getBaseDirectory(): File { - return File(folderProvider.directory(), "local") + actual fun getBaseDirectory(): UniFile? { + return UniFile.fromUri(context, folderProvider.path().toUri()) + ?.createDirectory("local") } - actual fun getFilesInBaseDirectory(): List { - return getBaseDirectory().listFiles().orEmpty().toList() + actual fun getFilesInBaseDirectory(): List { + return getBaseDirectory()?.listFiles().orEmpty().toList() } - actual fun getMangaDirectory(name: String): File? { + actual fun getMangaDirectory(name: String): UniFile? { return getFilesInBaseDirectory() // Get the first mangaDir or null .firstOrNull { it.isDirectory && it.name == name } } - actual fun getFilesInMangaDirectory(name: String): List { + actual fun getFilesInMangaDirectory(name: String): List { return getFilesInBaseDirectory() // Filter out ones that are not related to the manga and is not a directory .filter { it.isDirectory && it.name == name } diff --git a/source-local/src/commonMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt b/source-local/src/commonMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt index fd31299c2b..037d9f1dcb 100644 --- a/source-local/src/commonMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt +++ b/source-local/src/commonMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt @@ -1,12 +1,12 @@ package tachiyomi.source.local.image +import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.source.model.SManga -import java.io.File import java.io.InputStream expect class LocalCoverManager { - fun find(mangaUrl: String): File? + fun find(mangaUrl: String): UniFile? - fun update(manga: SManga, inputStream: InputStream): File? + fun update(manga: SManga, inputStream: InputStream): UniFile? } diff --git a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt index b28ee60b5d..a8f5a07406 100644 --- a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt +++ b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt @@ -1,12 +1,13 @@ package tachiyomi.source.local.io -import java.io.File +import com.hippo.unifile.UniFile +import tachiyomi.core.storage.extension object Archive { private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "epub") - fun isSupported(file: File): Boolean = with(file) { - return extension.lowercase() in SUPPORTED_ARCHIVE_TYPES + fun isSupported(file: UniFile): Boolean { + return file.extension in SUPPORTED_ARCHIVE_TYPES } } diff --git a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt index 53406b5de8..0f29ae8aba 100644 --- a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt +++ b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt @@ -1,18 +1,19 @@ package tachiyomi.source.local.io -import java.io.File +import com.hippo.unifile.UniFile +import tachiyomi.core.storage.extension sealed interface Format { - data class Directory(val file: File) : Format - data class Zip(val file: File) : Format - data class Rar(val file: File) : Format - data class Epub(val file: File) : Format + data class Directory(val file: UniFile) : Format + data class Zip(val file: UniFile) : Format + data class Rar(val file: UniFile) : Format + data class Epub(val file: UniFile) : Format class UnknownFormatException : Exception() companion object { - fun valueOf(file: File) = with(file) { + fun valueOf(file: UniFile) = with(file) { when { isDirectory -> Directory(this) extension.equals("zip", true) || extension.equals("cbz", true) -> Zip(this) diff --git a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt index f7e46c4159..5aa74d8517 100644 --- a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt +++ b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt @@ -1,14 +1,14 @@ package tachiyomi.source.local.io -import java.io.File +import com.hippo.unifile.UniFile expect class LocalSourceFileSystem { - fun getBaseDirectory(): File + fun getBaseDirectory(): UniFile? - fun getFilesInBaseDirectory(): List + fun getFilesInBaseDirectory(): List - fun getMangaDirectory(name: String): File? + fun getMangaDirectory(name: String): UniFile? - fun getFilesInMangaDirectory(name: String): List + fun getFilesInMangaDirectory(name: String): List }