|
|
|
@ -55,15 +55,16 @@ 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? =
|
|
|
|
|
|
|
|
index.getCoverEntry()?.let { rootPath / it } ?: fileSystem.findFirstImage(rootPath)
|
|
|
|
mangaInfo.copy(
|
|
|
|
mangaInfo.copy(
|
|
|
|
source = LocalMangaSource,
|
|
|
|
source = LocalMangaSource,
|
|
|
|
url = rootFile.toUri().toString(),
|
|
|
|
url = rootFile.toUri().toString(),
|
|
|
|
coverUrl = coverEntry?.let { uri.child(it, resolve = true).toString() }.orEmpty(),
|
|
|
|
coverUrl = coverEntry?.let { uri.child(it, resolve = true).toString() },
|
|
|
|
largeCoverUrl = null,
|
|
|
|
largeCoverUrl = null,
|
|
|
|
chapters = if (withDetails) {
|
|
|
|
chapters = if (withDetails) {
|
|
|
|
mangaInfo.chapters?.mapNotNull { c ->
|
|
|
|
mangaInfo.chapters?.mapNotNull { c ->
|
|
|
|
@ -92,9 +93,7 @@ class LocalMangaParser(private val uri: Uri) {
|
|
|
|
url = rootFile.toUri().toString(),
|
|
|
|
url = rootFile.toUri().toString(),
|
|
|
|
publicUrl = rootFile.toUri().toString(),
|
|
|
|
publicUrl = rootFile.toUri().toString(),
|
|
|
|
source = LocalMangaSource,
|
|
|
|
source = LocalMangaSource,
|
|
|
|
coverUrl = coverEntry?.let {
|
|
|
|
coverUrl = coverEntry?.let { uri.child(it, resolve = true).toString() },
|
|
|
|
uri.child(it, resolve = true).toString()
|
|
|
|
|
|
|
|
}.orEmpty(),
|
|
|
|
|
|
|
|
chapters = if (withDetails) {
|
|
|
|
chapters = if (withDetails) {
|
|
|
|
val chapters = fileSystem.listRecursively(rootPath)
|
|
|
|
val chapters = fileSystem.listRecursively(rootPath)
|
|
|
|
.mapNotNullTo(HashSet()) { path ->
|
|
|
|
.mapNotNullTo(HashSet()) { path ->
|
|
|
|
@ -138,16 +137,18 @@ class LocalMangaParser(private val uri: Uri) {
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}.let { LocalManga(it, rootFile) }
|
|
|
|
}.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) }
|
|
|
|
@ -167,6 +168,7 @@ class LocalMangaParser(private val uri: Uri) {
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun Uri.child(path: Path, resolve: Boolean): Uri {
|
|
|
|
private fun Uri.child(path: Path, resolve: Boolean): Uri {
|
|
|
|
val builder = buildUpon()
|
|
|
|
val builder = buildUpon()
|
|
|
|
@ -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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|