Use UniFile for local source file handling
(cherry picked from commit ca5498434409d4085c404f4ff5ed5e608f430a3b) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt # core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt # source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt # source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt # source-local/src/commonMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt
This commit is contained in:
parent
bda2ef3eee
commit
927c94041e
@ -30,7 +30,7 @@ import net.zetetic.database.sqlcipher.SupportOpenHelperFactory
|
|||||||
import nl.adaptivity.xmlutil.XmlDeclMode
|
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.provider.AndroidStorageFolderProvider
|
import tachiyomi.core.storage.AndroidStorageFolderProvider
|
||||||
import tachiyomi.data.AndroidDatabaseHandler
|
import tachiyomi.data.AndroidDatabaseHandler
|
||||||
import tachiyomi.data.Database
|
import tachiyomi.data.Database
|
||||||
import tachiyomi.data.DatabaseHandler
|
import tachiyomi.data.DatabaseHandler
|
||||||
@ -153,7 +153,7 @@ class AppModule(val app: Application) : InjektModule {
|
|||||||
addSingletonFactory { ImageSaver(app) }
|
addSingletonFactory { ImageSaver(app) }
|
||||||
|
|
||||||
addSingletonFactory { AndroidStorageFolderProvider(app) }
|
addSingletonFactory { AndroidStorageFolderProvider(app) }
|
||||||
addSingletonFactory { LocalSourceFileSystem(get<AndroidStorageFolderProvider>()) }
|
addSingletonFactory { LocalSourceFileSystem(app, get<AndroidStorageFolderProvider>()) }
|
||||||
addSingletonFactory { LocalCoverManager(app, get()) }
|
addSingletonFactory { LocalCoverManager(app, get()) }
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
|
@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
|||||||
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
||||||
import tachiyomi.core.preference.AndroidPreferenceStore
|
import tachiyomi.core.preference.AndroidPreferenceStore
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
import tachiyomi.core.preference.PreferenceStore
|
||||||
import tachiyomi.core.provider.AndroidStorageFolderProvider
|
import tachiyomi.core.storage.AndroidStorageFolderProvider
|
||||||
import tachiyomi.domain.backup.service.BackupPreferences
|
import tachiyomi.domain.backup.service.BackupPreferences
|
||||||
import tachiyomi.domain.download.service.DownloadPreferences
|
import tachiyomi.domain.download.service.DownloadPreferences
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
package eu.kanade.tachiyomi.ui.reader.loader
|
package eu.kanade.tachiyomi.ui.reader.loader
|
||||||
|
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
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 eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||||
import tachiyomi.core.util.system.ImageUtil
|
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].
|
* 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 var isLocal: Boolean = true
|
||||||
|
|
||||||
override suspend fun getPages(): List<ReaderPage> {
|
override suspend fun getPages(): List<ReaderPage> {
|
||||||
return file.listFiles()
|
return file.listFiles()
|
||||||
?.filter { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
?.filter { !it.isDirectory && ImageUtil.isImage(it.name) { it.openInputStream() } }
|
||||||
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
?.sortedWith { f1, f2 -> f1.name.orEmpty().compareToCaseInsensitiveNaturalOrder(f2.name.orEmpty()) }
|
||||||
?.mapIndexed { i, file ->
|
?.mapIndexed { i, file ->
|
||||||
val streamFn = { FileInputStream(file) }
|
val streamFn = { file.openInputStream() }
|
||||||
ReaderPage(i).apply {
|
ReaderPage(i).apply {
|
||||||
stream = streamFn
|
stream = streamFn
|
||||||
status = Page.State.READY
|
status = Page.State.READY
|
||||||
|
@ -12,7 +12,6 @@ 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.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loader used to load a chapter from the downloaded chapters.
|
* Loader used to load a chapter from the downloaded chapters.
|
||||||
@ -47,7 +46,7 @@ internal class DownloadPageLoader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getPagesFromArchive(chapterPath: UniFile): List<ReaderPage> {
|
private suspend fun getPagesFromArchive(chapterPath: UniFile): List<ReaderPage> {
|
||||||
val loader = ZipPageLoader(File(chapterPath.filePath!!)).also { zipPageLoader = it }
|
val loader = ZipPageLoader(chapterPath).also { zipPageLoader = it }
|
||||||
return loader.getPages()
|
return loader.getPages()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package eu.kanade.tachiyomi.ui.reader.loader
|
package eu.kanade.tachiyomi.ui.reader.loader
|
||||||
|
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
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.storage.EpubFile
|
import eu.kanade.tachiyomi.util.storage.EpubFile
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loader used to load a chapter from a .epub 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)
|
private val epub = EpubFile(file)
|
||||||
|
|
||||||
|
@ -3,10 +3,12 @@ package eu.kanade.tachiyomi.ui.reader.loader
|
|||||||
import android.app.Application
|
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 com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
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.ui.reader.setting.ReaderPreferences
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||||
|
import tachiyomi.core.storage.toFile
|
||||||
import tachiyomi.core.util.system.ImageUtil
|
import tachiyomi.core.util.system.ImageUtil
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -17,9 +19,9 @@ import java.io.PipedOutputStream
|
|||||||
/**
|
/**
|
||||||
* 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: UniFile) : PageLoader() {
|
||||||
|
|
||||||
private val rar = Archive(file)
|
private val rar = Archive(file.toFile())
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
private val context: Application by injectLazy()
|
private val context: Application by injectLazy()
|
||||||
@ -31,7 +33,7 @@ internal class RarPageLoader(file: File) : PageLoader() {
|
|||||||
init {
|
init {
|
||||||
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
||||||
tmpDir.mkdirs()
|
tmpDir.mkdirs()
|
||||||
Archive(file).use { rar ->
|
Archive(file.toFile()).use { rar ->
|
||||||
rar.fileHeaders.asSequence()
|
rar.fileHeaders.asSequence()
|
||||||
.filterNot { it.isDirectory }
|
.filterNot { it.isDirectory }
|
||||||
.forEach { header ->
|
.forEach { header ->
|
||||||
@ -52,7 +54,7 @@ internal class RarPageLoader(file: File) : PageLoader() {
|
|||||||
override suspend fun getPages(): List<ReaderPage> {
|
override suspend fun getPages(): List<ReaderPage> {
|
||||||
// SY -->
|
// SY -->
|
||||||
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
||||||
return DirectoryPageLoader(tmpDir).getPages()
|
return DirectoryPageLoader(UniFile.fromFile(tmpDir)!!).getPages()
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
return rar.fileHeaders.asSequence()
|
return rar.fileHeaders.asSequence()
|
||||||
|
@ -2,12 +2,14 @@ package eu.kanade.tachiyomi.ui.reader.loader
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
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.ui.reader.setting.ReaderPreferences
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||||
import eu.kanade.tachiyomi.util.storage.CbzCrypto
|
import eu.kanade.tachiyomi.util.storage.CbzCrypto
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.i18n.stringResource
|
||||||
|
import tachiyomi.core.storage.toFile
|
||||||
import tachiyomi.core.util.system.ImageUtil
|
import tachiyomi.core.util.system.ImageUtil
|
||||||
import tachiyomi.i18n.sy.SYMR
|
import tachiyomi.i18n.sy.SYMR
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
@ -19,7 +21,7 @@ import net.lingala.zip4j.ZipFile as Zip4jFile
|
|||||||
/**
|
/**
|
||||||
* Loader used to load a chapter from a .zip or .cbz file.
|
* Loader used to load a chapter from a .zip or .cbz file.
|
||||||
*/
|
*/
|
||||||
internal class ZipPageLoader(file: File) : PageLoader() {
|
internal class ZipPageLoader(file: UniFile) : PageLoader() {
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
private val context: Application by injectLazy()
|
private val context: Application by injectLazy()
|
||||||
@ -27,12 +29,12 @@ internal class ZipPageLoader(file: File) : PageLoader() {
|
|||||||
private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also {
|
private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also {
|
||||||
it.deleteRecursively()
|
it.deleteRecursively()
|
||||||
}
|
}
|
||||||
private val zip4j: Zip4jFile = Zip4jFile(file)
|
private val zip4j: Zip4jFile = Zip4jFile(file.toFile())
|
||||||
private val zip: ZipFile? =
|
private val zip: ZipFile? =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
if (!zip4j.isEncrypted) ZipFile(file, StandardCharsets.ISO_8859_1) else null
|
if (!zip4j.isEncrypted) ZipFile(file.toFile(), StandardCharsets.ISO_8859_1) else null
|
||||||
} else {
|
} else {
|
||||||
if (!zip4j.isEncrypted) ZipFile(file) else null
|
if (!zip4j.isEncrypted) ZipFile(file.toFile()) else null
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -40,7 +42,7 @@ internal class ZipPageLoader(file: File) : PageLoader() {
|
|||||||
zip4j.charset = StandardCharsets.ISO_8859_1
|
zip4j.charset = StandardCharsets.ISO_8859_1
|
||||||
}
|
}
|
||||||
|
|
||||||
Zip4jFile(file).use { zip ->
|
Zip4jFile(file.toFile()).use { zip ->
|
||||||
if (zip.isEncrypted) {
|
if (zip.isEncrypted) {
|
||||||
if (!CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())) {
|
if (!CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())) {
|
||||||
this.recycle()
|
this.recycle()
|
||||||
@ -79,7 +81,7 @@ internal class ZipPageLoader(file: File) : PageLoader() {
|
|||||||
|
|
||||||
override suspend fun getPages(): List<ReaderPage> {
|
override suspend fun getPages(): List<ReaderPage> {
|
||||||
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
if (readerPreferences.cacheArchiveMangaOnDisk().get()) {
|
||||||
return DirectoryPageLoader(tmpDir).getPages()
|
return DirectoryPageLoader(UniFile.fromFile(tmpDir)!!).getPages()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zip == null) {
|
if (zip == null) {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package eu.kanade.tachiyomi.util.storage
|
package eu.kanade.tachiyomi.util.storage
|
||||||
|
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
|
import tachiyomi.core.storage.toFile
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@ -11,12 +13,12 @@ import java.util.zip.ZipFile
|
|||||||
/**
|
/**
|
||||||
* Wrapper over ZipFile to load files in epub format.
|
* Wrapper over ZipFile to load files in epub format.
|
||||||
*/
|
*/
|
||||||
class EpubFile(file: File) : Closeable {
|
class EpubFile(file: UniFile) : Closeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zip file of this epub.
|
* Zip file of this epub.
|
||||||
*/
|
*/
|
||||||
private val zip = ZipFile(file)
|
private val zip = ZipFile(file.toFile())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path separator used by this epub.
|
* Path separator used by this epub.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package tachiyomi.core.provider
|
package tachiyomi.core.storage
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Environment
|
import android.os.Environment
|
@ -1,4 +1,4 @@
|
|||||||
package tachiyomi.core.provider
|
package tachiyomi.core.storage
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
@ -1,9 +1,12 @@
|
|||||||
package tachiyomi.core.storage
|
package tachiyomi.core.storage
|
||||||
|
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
val UniFile.extension: String?
|
val UniFile.extension: String?
|
||||||
get() = name?.substringAfterLast('.')
|
get() = name?.substringAfterLast('.')
|
||||||
|
|
||||||
val UniFile.nameWithoutExtension: String?
|
val UniFile.nameWithoutExtension: String?
|
||||||
get() = name?.substringBeforeLast('.')
|
get() = name?.substringBeforeLast('.')
|
||||||
|
|
||||||
|
fun UniFile.toFile(): File? = filePath?.let { File(it) }
|
||||||
|
@ -43,7 +43,8 @@ import kotlin.math.min
|
|||||||
|
|
||||||
object ImageUtil {
|
object ImageUtil {
|
||||||
|
|
||||||
fun isImage(name: String, openStream: (() -> InputStream)? = null): Boolean {
|
fun isImage(name: String?, openStream: (() -> InputStream)? = null): Boolean {
|
||||||
|
if (name == null) return false
|
||||||
// SY -->
|
// SY -->
|
||||||
if (File(name).extension.equals("cbi", ignoreCase = true)) return true
|
if (File(name).extension.equals("cbi", ignoreCase = true)) return true
|
||||||
// SY <--
|
// SY <--
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package tachiyomi.domain.storage.service
|
package tachiyomi.domain.storage.service
|
||||||
|
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
import tachiyomi.core.preference.PreferenceStore
|
||||||
import tachiyomi.core.provider.FolderProvider
|
import tachiyomi.core.storage.FolderProvider
|
||||||
|
|
||||||
class StoragePreferences(
|
class StoragePreferences(
|
||||||
private val folderProvider: FolderProvider,
|
private val folderProvider: FolderProvider,
|
||||||
|
@ -26,6 +26,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.extension
|
||||||
|
import tachiyomi.core.storage.nameWithoutExtension
|
||||||
|
import tachiyomi.core.storage.toFile
|
||||||
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
|
||||||
@ -41,7 +44,6 @@ import tachiyomi.source.local.metadata.fillChapterMetadata
|
|||||||
import tachiyomi.source.local.metadata.fillMangaMetadata
|
import tachiyomi.source.local.metadata.fillMangaMetadata
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
||||||
@ -96,14 +98,14 @@ actual class LocalSource(
|
|||||||
.filter {
|
.filter {
|
||||||
it.isDirectory &&
|
it.isDirectory &&
|
||||||
/* SY --> */ (
|
/* SY --> */ (
|
||||||
!it.name.startsWith('.') ||
|
!it.name.orEmpty().startsWith('.') ||
|
||||||
allowLocalSourceHiddenFolders
|
allowLocalSourceHiddenFolders
|
||||||
) /* SY <-- */
|
) /* SY <-- */
|
||||||
}
|
}
|
||||||
.distinctBy { it.name }
|
.distinctBy { it.name }
|
||||||
.filter { // Filter by query or last modified
|
.filter { // Filter by query or last modified
|
||||||
if (lastModifiedLimit == 0L) {
|
if (lastModifiedLimit == 0L) {
|
||||||
it.name.contains(query, ignoreCase = true)
|
it.name.orEmpty().contains(query, ignoreCase = true)
|
||||||
} else {
|
} else {
|
||||||
it.lastModified() >= lastModifiedLimit
|
it.lastModified() >= lastModifiedLimit
|
||||||
}
|
}
|
||||||
@ -113,16 +115,16 @@ actual class LocalSource(
|
|||||||
when (filter) {
|
when (filter) {
|
||||||
is OrderBy.Popular -> {
|
is OrderBy.Popular -> {
|
||||||
mangaDirs = if (filter.state!!.ascending) {
|
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 {
|
} else {
|
||||||
mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name })
|
mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name.orEmpty() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is OrderBy.Latest -> {
|
is OrderBy.Latest -> {
|
||||||
mangaDirs = if (filter.state!!.ascending) {
|
mangaDirs = if (filter.state!!.ascending) {
|
||||||
mangaDirs.sortedBy(File::lastModified)
|
mangaDirs.sortedBy(UniFile::lastModified)
|
||||||
} else {
|
} else {
|
||||||
mangaDirs.sortedByDescending(File::lastModified)
|
mangaDirs.sortedByDescending(UniFile::lastModified)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,13 +137,13 @@ actual class LocalSource(
|
|||||||
// Transform mangaDirs to list of SManga
|
// Transform mangaDirs to list of SManga
|
||||||
val mangas = mangaDirs.map { mangaDir ->
|
val mangas = mangaDirs.map { mangaDir ->
|
||||||
SManga.create().apply {
|
SManga.create().apply {
|
||||||
title = mangaDir.name
|
title = mangaDir.name.orEmpty()
|
||||||
url = mangaDir.name
|
url = mangaDir.name.orEmpty()
|
||||||
|
|
||||||
// Try to find the cover
|
// Try to find the cover
|
||||||
coverManager.find(mangaDir.name)
|
coverManager.find(mangaDir.name.orEmpty())
|
||||||
?.takeIf(File::exists)
|
?.takeIf(UniFile::exists)
|
||||||
?.let { thumbnail_url = it.absolutePath }
|
?.let { thumbnail_url = it.uri.toString() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +172,7 @@ actual class LocalSource(
|
|||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
fun updateMangaInfo(manga: SManga) {
|
fun updateMangaInfo(manga: SManga) {
|
||||||
val directory = fileSystem.getFilesInBaseDirectories().map { File(it, manga.url) }.find {
|
val directory = fileSystem.getFilesInBaseDirectory().map { File(it.toFile(), manga.url) }.find {
|
||||||
it.exists()
|
it.exists()
|
||||||
} ?: return
|
} ?: return
|
||||||
val existingFileName = directory.listFiles()?.find { it.extension == "json" }?.name
|
val existingFileName = directory.listFiles()?.find { it.extension == "json" }?.name
|
||||||
@ -188,7 +190,7 @@ actual class LocalSource(
|
|||||||
// Manga details related
|
// Manga details related
|
||||||
override suspend fun getMangaDetails(manga: SManga): SManga = withIOContext {
|
override suspend fun getMangaDetails(manga: SManga): SManga = withIOContext {
|
||||||
coverManager.find(manga.url)?.let {
|
coverManager.find(manga.url)?.let {
|
||||||
manga.thumbnail_url = it.absolutePath
|
manga.thumbnail_url = it.uri.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Augment manga details based on metadata files
|
// Augment manga details based on metadata files
|
||||||
@ -211,11 +213,11 @@ actual class LocalSource(
|
|||||||
// Top level ComicInfo.xml
|
// Top level ComicInfo.xml
|
||||||
comicInfoFile != null -> {
|
comicInfoFile != null -> {
|
||||||
noXmlFile?.delete()
|
noXmlFile?.delete()
|
||||||
setMangaDetailsFromComicInfoFile(comicInfoFile.inputStream(), manga)
|
setMangaDetailsFromComicInfoFile(comicInfoFile.openInputStream(), manga)
|
||||||
}
|
}
|
||||||
// SY -->
|
// SY -->
|
||||||
comicInfoArchiveFile != null -> {
|
comicInfoArchiveFile != null -> {
|
||||||
val comicInfoArchive = ZipFile(comicInfoArchiveFile)
|
val comicInfoArchive = ZipFile(comicInfoArchiveFile.toFile())
|
||||||
noXmlFile?.delete()
|
noXmlFile?.delete()
|
||||||
|
|
||||||
if (CbzCrypto.checkCbzPassword(comicInfoArchive, CbzCrypto.getDecryptedPasswordCbz())) {
|
if (CbzCrypto.checkCbzPassword(comicInfoArchive, CbzCrypto.getDecryptedPasswordCbz())) {
|
||||||
@ -229,7 +231,7 @@ actual class LocalSource(
|
|||||||
// Old custom JSON format
|
// Old custom JSON format
|
||||||
// TODO: remove support for this entirely after a while
|
// TODO: remove support for this entirely after a while
|
||||||
legacyJsonDetailsFile != null -> {
|
legacyJsonDetailsFile != null -> {
|
||||||
json.decodeFromStream<MangaDetails>(legacyJsonDetailsFile.inputStream()).run {
|
json.decodeFromStream<MangaDetails>(legacyJsonDetailsFile.openInputStream()).run {
|
||||||
title?.let { manga.title = it }
|
title?.let { manga.title = it }
|
||||||
author?.let { manga.author = it }
|
author?.let { manga.author = it }
|
||||||
artist?.let { manga.artist = it }
|
artist?.let { manga.artist = it }
|
||||||
@ -239,7 +241,7 @@ actual class LocalSource(
|
|||||||
}
|
}
|
||||||
// Replace with ComicInfo.xml file
|
// Replace with ComicInfo.xml file
|
||||||
val comicInfo = manga.getComicInfo()
|
val comicInfo = manga.getComicInfo()
|
||||||
UniFile.fromFile(mangaDir)
|
mangaDir
|
||||||
?.createFile(COMIC_INFO_FILE)
|
?.createFile(COMIC_INFO_FILE)
|
||||||
?.openOutputStream()
|
?.openOutputStream()
|
||||||
?.use {
|
?.use {
|
||||||
@ -255,7 +257,7 @@ actual class LocalSource(
|
|||||||
.filter(Archive::isSupported)
|
.filter(Archive::isSupported)
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
val folderPath = mangaDir?.absolutePath
|
val folderPath = mangaDir?.filePath
|
||||||
|
|
||||||
val copiedFile = copyComicInfoFileFromArchive(chapterArchives, folderPath)
|
val copiedFile = copyComicInfoFileFromArchive(chapterArchives, folderPath)
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -281,11 +283,11 @@ actual class LocalSource(
|
|||||||
return@withIOContext manga
|
return@withIOContext manga
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyComicInfoFileFromArchive(chapterArchives: List<File>, folderPath: String?): File? {
|
private fun copyComicInfoFileFromArchive(chapterArchives: List<UniFile>, folderPath: String?): File? {
|
||||||
for (chapter in chapterArchives) {
|
for (chapter in chapterArchives) {
|
||||||
when (Format.valueOf(chapter)) {
|
when (Format.valueOf(chapter)) {
|
||||||
is Format.Zip -> {
|
is Format.Zip -> {
|
||||||
ZipFile(chapter).use { zip: ZipFile ->
|
ZipFile(chapter.toFile()).use { zip: ZipFile ->
|
||||||
// SY -->
|
// SY -->
|
||||||
if (zip.isEncrypted && !CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())
|
if (zip.isEncrypted && !CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())
|
||||||
) {
|
) {
|
||||||
@ -304,7 +306,7 @@ actual class LocalSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Format.Rar -> {
|
is Format.Rar -> {
|
||||||
JunrarArchive(chapter).use { rar ->
|
JunrarArchive(chapter.toFile()).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)
|
||||||
@ -359,9 +361,9 @@ actual class LocalSource(
|
|||||||
SChapter.create().apply {
|
SChapter.create().apply {
|
||||||
url = "${manga.url}/${chapterFile.name}"
|
url = "${manga.url}/${chapterFile.name}"
|
||||||
name = if (chapterFile.isDirectory) {
|
name = if (chapterFile.isDirectory) {
|
||||||
chapterFile.name
|
chapterFile.name.orEmpty()
|
||||||
} else {
|
} else {
|
||||||
chapterFile.nameWithoutExtension
|
chapterFile.nameWithoutExtension.orEmpty()
|
||||||
}
|
}
|
||||||
date_upload = chapterFile.lastModified()
|
date_upload = chapterFile.lastModified()
|
||||||
chapter_number = ChapterRecognition
|
chapter_number = ChapterRecognition
|
||||||
@ -391,8 +393,8 @@ actual class LocalSource(
|
|||||||
|
|
||||||
fun getFormat(chapter: SChapter): Format {
|
fun getFormat(chapter: SChapter): Format {
|
||||||
try {
|
try {
|
||||||
return File(fileSystem.getBaseDirectory(), chapter.url)
|
return fileSystem.getBaseDirectory()
|
||||||
.takeIf { it.exists() }
|
?.findFile(chapter.url)
|
||||||
?.let(Format.Companion::valueOf)
|
?.let(Format.Companion::valueOf)
|
||||||
?: throw Exception(context.stringResource(MR.strings.chapter_not_found))
|
?: throw Exception(context.stringResource(MR.strings.chapter_not_found))
|
||||||
} catch (e: Format.UnknownFormatException) {
|
} catch (e: Format.UnknownFormatException) {
|
||||||
@ -402,18 +404,24 @@ actual class LocalSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCover(chapter: SChapter, manga: SManga): File? {
|
private fun updateCover(chapter: SChapter, manga: SManga): UniFile? {
|
||||||
return try {
|
return try {
|
||||||
when (val format = getFormat(chapter)) {
|
when (val format = getFormat(chapter)) {
|
||||||
is Format.Directory -> {
|
is Format.Directory -> {
|
||||||
val entry = format.file.listFiles()
|
val entry = format.file.listFiles()
|
||||||
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
?.sortedWith { f1, f2 ->
|
||||||
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
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 -> {
|
is Format.Zip -> {
|
||||||
ZipFile(format.file).use { zip ->
|
ZipFile(format.file.toFile()).use { zip ->
|
||||||
// SY -->
|
// SY -->
|
||||||
var encrypted = false
|
var encrypted = false
|
||||||
if (zip.isEncrypted) {
|
if (zip.isEncrypted) {
|
||||||
@ -428,7 +436,7 @@ actual class LocalSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Format.Rar -> {
|
is Format.Rar -> {
|
||||||
JunrarArchive(format.file).use { archive ->
|
JunrarArchive(format.file.toFile()).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) } }
|
||||||
|
@ -7,6 +7,8 @@ import eu.kanade.tachiyomi.util.storage.CbzCrypto
|
|||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
import net.lingala.zip4j.ZipFile
|
import net.lingala.zip4j.ZipFile
|
||||||
import net.lingala.zip4j.model.ZipParameters
|
import net.lingala.zip4j.model.ZipParameters
|
||||||
|
import tachiyomi.core.storage.nameWithoutExtension
|
||||||
|
import tachiyomi.core.storage.toFile
|
||||||
import tachiyomi.core.util.system.ImageUtil
|
import tachiyomi.core.util.system.ImageUtil
|
||||||
import tachiyomi.source.local.io.LocalSourceFileSystem
|
import tachiyomi.source.local.io.LocalSourceFileSystem
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -20,13 +22,13 @@ actual class LocalCoverManager(
|
|||||||
private val fileSystem: LocalSourceFileSystem,
|
private val fileSystem: LocalSourceFileSystem,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
actual fun find(mangaUrl: String): File? {
|
actual fun find(mangaUrl: String): UniFile? {
|
||||||
return fileSystem.getFilesInMangaDirectory(mangaUrl)
|
return fileSystem.getFilesInMangaDirectory(mangaUrl)
|
||||||
// Get all file whose names start with "cover"
|
// Get all file whose names start with "cover"
|
||||||
.filter { it.isFile && it.nameWithoutExtension.equals("cover", ignoreCase = true) }
|
.filter { it.isFile && it.nameWithoutExtension.equals("cover", ignoreCase = true) }
|
||||||
// Get the first actual image
|
// Get the first actual image
|
||||||
.firstOrNull {
|
.firstOrNull {
|
||||||
ImageUtil.isImage(it.name) { it.inputStream() } || it.name == COVER_ARCHIVE_NAME
|
ImageUtil.isImage(it.name) { it.openInputStream() } || it.name == COVER_ARCHIVE_NAME
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +38,7 @@ actual class LocalCoverManager(
|
|||||||
// SY -->
|
// SY -->
|
||||||
encrypted: Boolean,
|
encrypted: Boolean,
|
||||||
// SY <--
|
// SY <--
|
||||||
): File? {
|
): UniFile? {
|
||||||
val directory = fileSystem.getMangaDirectory(manga.url)
|
val directory = fileSystem.getMangaDirectory(manga.url)
|
||||||
if (directory == null) {
|
if (directory == null) {
|
||||||
inputStream.close()
|
inputStream.close()
|
||||||
@ -46,38 +48,38 @@ actual class LocalCoverManager(
|
|||||||
var targetFile = find(manga.url)
|
var targetFile = find(manga.url)
|
||||||
if (targetFile == null) {
|
if (targetFile == null) {
|
||||||
// SY -->
|
// SY -->
|
||||||
if (encrypted) {
|
targetFile = if (encrypted) {
|
||||||
targetFile = File(directory.absolutePath, COVER_ARCHIVE_NAME)
|
directory.createFile(COVER_ARCHIVE_NAME)
|
||||||
} else {
|
} else {
|
||||||
targetFile = File(directory.absolutePath, DEFAULT_COVER_NAME)
|
directory.createFile(DEFAULT_COVER_NAME)
|
||||||
targetFile.createNewFile()
|
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targetFile!!
|
||||||
|
|
||||||
// It might not exist at this point
|
// It might not exist at this point
|
||||||
targetFile.parentFile?.mkdirs()
|
|
||||||
inputStream.use { input ->
|
inputStream.use { input ->
|
||||||
// SY -->
|
// SY -->
|
||||||
if (encrypted) {
|
if (encrypted) {
|
||||||
val zip4j = ZipFile(targetFile)
|
val zip4j = ZipFile(targetFile.toFile())
|
||||||
val zipParameters = ZipParameters()
|
val zipParameters = ZipParameters()
|
||||||
zip4j.setPassword(CbzCrypto.getDecryptedPasswordCbz())
|
zip4j.setPassword(CbzCrypto.getDecryptedPasswordCbz())
|
||||||
CbzCrypto.setZipParametersEncrypted(zipParameters)
|
CbzCrypto.setZipParametersEncrypted(zipParameters)
|
||||||
zipParameters.fileNameInZip = DEFAULT_COVER_NAME
|
zipParameters.fileNameInZip = DEFAULT_COVER_NAME
|
||||||
zip4j.addStream(input, zipParameters)
|
zip4j.addStream(input, zipParameters)
|
||||||
|
|
||||||
DiskUtil.createNoMediaFile(UniFile.fromFile(directory), context)
|
DiskUtil.createNoMediaFile(directory, context)
|
||||||
|
|
||||||
manga.thumbnail_url = zip4j.file.absolutePath
|
manga.thumbnail_url = targetFile.uri.toString()
|
||||||
return zip4j.file
|
return targetFile
|
||||||
} else {
|
} else {
|
||||||
// SY <--
|
// SY <--
|
||||||
targetFile.outputStream().use { output ->
|
targetFile.openOutputStream().use { output ->
|
||||||
input.copyTo(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
|
return targetFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,31 @@
|
|||||||
package tachiyomi.source.local.io
|
package tachiyomi.source.local.io
|
||||||
|
|
||||||
import tachiyomi.core.provider.FolderProvider
|
import android.content.Context
|
||||||
import java.io.File
|
import androidx.core.net.toUri
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
|
import tachiyomi.core.storage.FolderProvider
|
||||||
|
|
||||||
actual class LocalSourceFileSystem(
|
actual class LocalSourceFileSystem(
|
||||||
|
private val context: Context,
|
||||||
private val folderProvider: FolderProvider,
|
private val folderProvider: FolderProvider,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
actual fun getBaseDirectory(): File {
|
actual fun getBaseDirectory(): UniFile? {
|
||||||
return File(folderProvider.directory(), "local")
|
return UniFile.fromUri(context, folderProvider.path().toUri())
|
||||||
|
?.createDirectory("local")
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun getFilesInBaseDirectory(): List<File> {
|
actual fun getFilesInBaseDirectory(): List<UniFile> {
|
||||||
return getBaseDirectory().listFiles().orEmpty().toList()
|
return getBaseDirectory()?.listFiles().orEmpty().toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun getMangaDirectory(name: String): File? {
|
actual fun getMangaDirectory(name: String): UniFile? {
|
||||||
return getFilesInBaseDirectory()
|
return getFilesInBaseDirectory()
|
||||||
// Get the first mangaDir or null
|
// Get the first mangaDir or null
|
||||||
.firstOrNull { it.isDirectory && it.name == name }
|
.firstOrNull { it.isDirectory && it.name == name }
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun getFilesInMangaDirectory(name: String): List<File> {
|
actual fun getFilesInMangaDirectory(name: String): List<UniFile> {
|
||||||
return getFilesInBaseDirectory()
|
return getFilesInBaseDirectory()
|
||||||
// Filter out ones that are not related to the manga and is not a directory
|
// Filter out ones that are not related to the manga and is not a directory
|
||||||
.filter { it.isDirectory && it.name == name }
|
.filter { it.isDirectory && it.name == name }
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package tachiyomi.source.local.image
|
package tachiyomi.source.local.image
|
||||||
|
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
expect class LocalCoverManager {
|
expect class LocalCoverManager {
|
||||||
|
|
||||||
fun find(mangaUrl: String): File?
|
fun find(mangaUrl: String): UniFile?
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
fun update(manga: SManga, inputStream: InputStream, encrypted: Boolean = false): File?
|
fun update(manga: SManga, inputStream: InputStream, encrypted: Boolean = false): UniFile?
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package tachiyomi.source.local.io
|
package tachiyomi.source.local.io
|
||||||
|
|
||||||
import java.io.File
|
import com.hippo.unifile.UniFile
|
||||||
|
import tachiyomi.core.storage.extension
|
||||||
|
|
||||||
object Archive {
|
object Archive {
|
||||||
|
|
||||||
private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "epub")
|
private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "epub")
|
||||||
|
|
||||||
fun isSupported(file: File): Boolean = with(file) {
|
fun isSupported(file: UniFile): Boolean {
|
||||||
return extension.lowercase() in SUPPORTED_ARCHIVE_TYPES
|
return file.extension in SUPPORTED_ARCHIVE_TYPES
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
package tachiyomi.source.local.io
|
package tachiyomi.source.local.io
|
||||||
|
|
||||||
import java.io.File
|
import com.hippo.unifile.UniFile
|
||||||
|
import tachiyomi.core.storage.extension
|
||||||
|
|
||||||
sealed interface Format {
|
sealed interface Format {
|
||||||
data class Directory(val file: File) : Format
|
data class Directory(val file: UniFile) : Format
|
||||||
data class Zip(val file: File) : Format
|
data class Zip(val file: UniFile) : Format
|
||||||
data class Rar(val file: File) : Format
|
data class Rar(val file: UniFile) : Format
|
||||||
data class Epub(val file: File) : Format
|
data class Epub(val file: UniFile) : Format
|
||||||
|
|
||||||
class UnknownFormatException : Exception()
|
class UnknownFormatException : Exception()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun valueOf(file: File) = with(file) {
|
fun valueOf(file: UniFile) = with(file) {
|
||||||
when {
|
when {
|
||||||
isDirectory -> Directory(this)
|
isDirectory -> Directory(this)
|
||||||
extension.equals("zip", true) || extension.equals("cbz", true) -> Zip(this)
|
extension.equals("zip", true) || extension.equals("cbz", true) -> Zip(this)
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package tachiyomi.source.local.io
|
package tachiyomi.source.local.io
|
||||||
|
|
||||||
import java.io.File
|
import com.hippo.unifile.UniFile
|
||||||
|
|
||||||
expect class LocalSourceFileSystem {
|
expect class LocalSourceFileSystem {
|
||||||
|
|
||||||
fun getBaseDirectory(): File
|
fun getBaseDirectory(): UniFile?
|
||||||
|
|
||||||
fun getFilesInBaseDirectory(): List<File>
|
fun getFilesInBaseDirectory(): List<UniFile>
|
||||||
|
|
||||||
fun getMangaDirectory(name: String): File?
|
fun getMangaDirectory(name: String): UniFile?
|
||||||
|
|
||||||
fun getFilesInMangaDirectory(name: String): List<File>
|
fun getFilesInMangaDirectory(name: String): List<UniFile>
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user