Support closing Okio FileSystem

master
Koitharu 1 year ago
parent 6360731f34
commit 169539f42f
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -55,117 +55,119 @@ class LocalMangaParser(private val uri: Uri) {
private val rootFile: File = File(uri.schemeSpecificPart) private val rootFile: File = File(uri.schemeSpecificPart)
suspend fun getManga(withDetails: Boolean): LocalManga = runInterruptible(Dispatchers.IO) { suspend fun getManga(withDetails: Boolean): LocalManga = runInterruptible(Dispatchers.IO) {
val (fileSystem, rootPath) = uri.resolveFsAndPath() (uri.resolveFsAndPath()).use { (fileSystem, rootPath) ->
val index = MangaIndex.read(fileSystem, rootPath / ENTRY_NAME_INDEX) val index = MangaIndex.read(fileSystem, rootPath / ENTRY_NAME_INDEX)
val mangaInfo = index?.getMangaInfo() val mangaInfo = index?.getMangaInfo()
if (mangaInfo != null) { if (mangaInfo != null) {
val coverEntry: Path? = index.getCoverEntry()?.let { rootPath / it } ?: fileSystem.findFirstImage(rootPath) val coverEntry: Path? =
mangaInfo.copy( index.getCoverEntry()?.let { rootPath / it } ?: fileSystem.findFirstImage(rootPath)
source = LocalMangaSource, mangaInfo.copy(
url = rootFile.toUri().toString(), source = LocalMangaSource,
coverUrl = coverEntry?.let { uri.child(it, resolve = true).toString() }.orEmpty(), url = rootFile.toUri().toString(),
largeCoverUrl = null, coverUrl = coverEntry?.let { uri.child(it, resolve = true).toString() },
chapters = if (withDetails) { largeCoverUrl = null,
mangaInfo.chapters?.mapNotNull { c -> chapters = if (withDetails) {
val path = index.getChapterFileName(c.id)?.toPath() mangaInfo.chapters?.mapNotNull { c ->
if (path != null && !fileSystem.exists(rootPath / path)) { val path = index.getChapterFileName(c.id)?.toPath()
null if (path != null && !fileSystem.exists(rootPath / path)) {
} else { null
c.copy( } else {
url = path?.let { c.copy(
uri.child(it, resolve = false).toString() url = path?.let {
} ?: uri.toString(), uri.child(it, resolve = false).toString()
} ?: uri.toString(),
source = LocalMangaSource,
)
}
}
} else {
null
},
)
} else {
val title = rootFile.name.fileNameToTitle()
val coverEntry = fileSystem.findFirstImage(rootPath)
Manga(
id = rootFile.absolutePath.longHashCode(),
title = title,
url = rootFile.toUri().toString(),
publicUrl = rootFile.toUri().toString(),
source = LocalMangaSource,
coverUrl = coverEntry?.let { uri.child(it, resolve = true).toString() },
chapters = if (withDetails) {
val chapters = fileSystem.listRecursively(rootPath)
.mapNotNullTo(HashSet()) { path ->
when {
path == coverEntry -> null
!fileSystem.isRegularFile(path) -> null
path.isImage() -> path.parent
hasZipExtension(path.name) -> path
else -> null
}
}.sortedWith(compareBy(AlphanumComparator()) { x -> x.toString() })
chapters.mapIndexed { i, p ->
val s = if (p.root == rootPath.root) {
p.relativeTo(rootPath).toString()
} else {
p
}.toString().removePrefix(Path.DIRECTORY_SEPARATOR)
MangaChapter(
id = "$i$s".longHashCode(),
name = s.fileNameToTitle().ifEmpty { title },
number = 0f,
volume = 0,
source = LocalMangaSource, source = LocalMangaSource,
uploadDate = 0L,
url = uri.child(p.relativeTo(rootPath), resolve = false).toString(),
scanlator = null,
branch = null,
) )
} }
} } else {
} else { null
null },
}, altTitle = null,
) rating = -1f,
} else { isNsfw = false,
val title = rootFile.name.fileNameToTitle() tags = setOf(),
val coverEntry = fileSystem.findFirstImage(rootPath) state = null,
Manga( author = null,
id = rootFile.absolutePath.longHashCode(), largeCoverUrl = null,
title = title, description = null,
url = rootFile.toUri().toString(), )
publicUrl = rootFile.toUri().toString(), }.let { LocalManga(it, rootFile) }
source = LocalMangaSource, }
coverUrl = coverEntry?.let {
uri.child(it, resolve = true).toString()
}.orEmpty(),
chapters = if (withDetails) {
val chapters = fileSystem.listRecursively(rootPath)
.mapNotNullTo(HashSet()) { path ->
when {
path == coverEntry -> null
!fileSystem.isRegularFile(path) -> null
path.isImage() -> path.parent
hasZipExtension(path.name) -> path
else -> null
}
}.sortedWith(compareBy(AlphanumComparator()) { x -> x.toString() })
chapters.mapIndexed { i, p ->
val s = if (p.root == rootPath.root) {
p.relativeTo(rootPath).toString()
} else {
p
}.toString().removePrefix(Path.DIRECTORY_SEPARATOR)
MangaChapter(
id = "$i$s".longHashCode(),
name = s.fileNameToTitle().ifEmpty { title },
number = 0f,
volume = 0,
source = LocalMangaSource,
uploadDate = 0L,
url = uri.child(p.relativeTo(rootPath), resolve = false).toString(),
scanlator = null,
branch = null,
)
}
} else {
null
},
altTitle = null,
rating = -1f,
isNsfw = false,
tags = setOf(),
state = null,
author = null,
largeCoverUrl = null,
description = null,
)
}.let { LocalManga(it, rootFile) }
} }
suspend fun getMangaInfo(): Manga? = runInterruptible(Dispatchers.IO) { suspend fun getMangaInfo(): Manga? = runInterruptible(Dispatchers.IO) {
val (fileSystem, rootPath) = uri.resolveFsAndPath() uri.resolveFsAndPath().use { (fileSystem, rootPath) ->
val index = MangaIndex.read(fileSystem, rootPath / ENTRY_NAME_INDEX) val index = MangaIndex.read(fileSystem, rootPath / ENTRY_NAME_INDEX)
index?.getMangaInfo() index?.getMangaInfo()
}
} }
suspend fun getPages(chapter: MangaChapter): List<MangaPage> = runInterruptible(Dispatchers.IO) { suspend fun getPages(chapter: MangaChapter): List<MangaPage> = runInterruptible(Dispatchers.IO) {
val chapterUri = chapter.url.toUri().resolve() val chapterUri = chapter.url.toUri().resolve()
val (fileSystem, rootPath) = chapterUri.resolveFsAndPath() chapterUri.resolveFsAndPath().use { (fileSystem, rootPath) ->
val index = MangaIndex.read(fileSystem, rootPath / ENTRY_NAME_INDEX) val index = MangaIndex.read(fileSystem, rootPath / ENTRY_NAME_INDEX)
val entries = fileSystem.listRecursively(rootPath) val entries = fileSystem.listRecursively(rootPath)
.filter { fileSystem.isRegularFile(it) } .filter { fileSystem.isRegularFile(it) }
if (index != null) { if (index != null) {
val pattern = index.getChapterNamesPattern(chapter) val pattern = index.getChapterNamesPattern(chapter)
entries.filter { x -> x.name.substringBefore('.').matches(pattern) } entries.filter { x -> x.name.substringBefore('.').matches(pattern) }
} else { } else {
entries.filter { x -> x.isImage() && x.parent == rootPath } entries.filter { x -> x.isImage() && x.parent == rootPath }
}.toListSorted(compareBy(AlphanumComparator()) { x -> x.toString() }) }.toListSorted(compareBy(AlphanumComparator()) { x -> x.toString() })
.map { x -> .map { x ->
val entryUri = chapterUri.child(x, resolve = true).toString() val entryUri = chapterUri.child(x, resolve = true).toString()
MangaPage( MangaPage(
id = entryUri.longHashCode(), id = entryUri.longHashCode(),
url = entryUri, url = entryUri,
preview = null, preview = null,
source = LocalMangaSource, source = LocalMangaSource,
) )
} }
}
} }
private fun Uri.child(path: Path, resolve: Boolean): Uri { private fun Uri.child(path: Path, resolve: Boolean): Uri {
@ -184,6 +186,23 @@ class LocalMangaParser(private val uri: Uri) {
return builder.build() return builder.build()
} }
private class FsAndPath(
val fileSystem: FileSystem,
val path: Path,
private val isCloseable: Boolean,
) : AutoCloseable {
override fun close() {
if (isCloseable) {
fileSystem.close()
}
}
operator fun component1() = fileSystem
operator fun component2() = path
}
companion object { companion object {
@Blocking @Blocking
@ -240,20 +259,25 @@ class LocalMangaParser(private val uri: Uri) {
} }
@Blocking @Blocking
private fun Uri.resolveFsAndPath(): Pair<FileSystem, Path> { private fun Uri.resolveFsAndPath(): FsAndPath {
val resolved = resolve() val resolved = resolve()
return when { return when {
resolved.isZipUri() -> { resolved.isZipUri() -> FsAndPath(
FileSystem.SYSTEM.openZip(resolved.schemeSpecificPart.toPath()) to resolved.fragment.orEmpty() FileSystem.SYSTEM.openZip(resolved.schemeSpecificPart.toPath()),
.toRootedPath() resolved.fragment.orEmpty().toRootedPath(),
} isCloseable = true,
)
isFileUri() -> { isFileUri() -> {
val file = toFile() val file = toFile()
if (file.isZipArchive) { if (file.isZipArchive) {
FileSystem.SYSTEM.openZip(schemeSpecificPart.toPath()) to fragment.orEmpty().toRootedPath() FsAndPath(
FileSystem.SYSTEM.openZip(schemeSpecificPart.toPath()),
fragment.orEmpty().toRootedPath(),
isCloseable = true,
)
} else { } else {
FileSystem.SYSTEM to file.toOkioPath() FsAndPath(FileSystem.SYSTEM, file.toOkioPath(), isCloseable = false)
} }
} }

Loading…
Cancel
Save