mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
refactor: Move archive related code to core.archive module
This commit is contained in:
parent
54a3059730
commit
b4377a4609
19 changed files with 61 additions and 55 deletions
|
@ -144,6 +144,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(projects.core.archive)
|
||||||
implementation(projects.core.main)
|
implementation(projects.core.main)
|
||||||
implementation(projects.data)
|
implementation(projects.data)
|
||||||
implementation(projects.domain)
|
implementation(projects.domain)
|
||||||
|
|
|
@ -12,8 +12,6 @@ import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
import eu.kanade.tachiyomi.util.storage.EpubFile
|
|
||||||
import eu.kanade.tachiyomi.util.storage.fillMetadata
|
|
||||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||||
import eu.kanade.tachiyomi.util.system.extension
|
import eu.kanade.tachiyomi.util.system.extension
|
||||||
import eu.kanade.tachiyomi.util.system.nameWithoutExtension
|
import eu.kanade.tachiyomi.util.system.nameWithoutExtension
|
||||||
|
@ -33,7 +31,8 @@ import nl.adaptivity.xmlutil.serialization.XML
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import yokai.core.archive.archiveReader
|
import yokai.core.archive.util.archiveReader
|
||||||
|
import yokai.core.archive.util.epubReader
|
||||||
import yokai.core.metadata.COMIC_INFO_FILE
|
import yokai.core.metadata.COMIC_INFO_FILE
|
||||||
import yokai.core.metadata.ComicInfo
|
import yokai.core.metadata.ComicInfo
|
||||||
import yokai.core.metadata.copyFromComicInfo
|
import yokai.core.metadata.copyFromComicInfo
|
||||||
|
@ -42,6 +41,7 @@ import yokai.domain.chapter.services.ChapterRecognition
|
||||||
import yokai.domain.source.SourcePreferences
|
import yokai.domain.source.SourcePreferences
|
||||||
import yokai.domain.storage.StorageManager
|
import yokai.domain.storage.StorageManager
|
||||||
import yokai.i18n.MR
|
import yokai.i18n.MR
|
||||||
|
import yokai.util.fillMetadata
|
||||||
import yokai.util.lang.getString
|
import yokai.util.lang.getString
|
||||||
|
|
||||||
class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSource {
|
class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSource {
|
||||||
|
@ -410,7 +410,7 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Format.Epub -> {
|
is Format.Epub -> {
|
||||||
EpubFile(format.file.archiveReader(context)).use { epub ->
|
format.file.epubReader(context).use { epub ->
|
||||||
val entry = epub.getImagesFromPages().firstOrNull()
|
val entry = epub.getImagesFromPages().firstOrNull()
|
||||||
|
|
||||||
entry?.let { updateCover(manga, epub.getInputStream(it)!!, context) }
|
entry?.let { updateCover(manga, epub.getInputStream(it)!!, context) }
|
||||||
|
@ -433,7 +433,7 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
is Format.Epub -> {
|
is Format.Epub -> {
|
||||||
EpubFile(format.file.archiveReader(context)).use { epub ->
|
format.file.epubReader(context).use { epub ->
|
||||||
epub.fillMetadata(chapter, manga)
|
epub.fillMetadata(chapter, manga)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
|
@ -10,7 +10,8 @@ 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 eu.kanade.tachiyomi.util.system.withIOContext
|
import eu.kanade.tachiyomi.util.system.withIOContext
|
||||||
import yokai.core.archive.archiveReader
|
import yokai.core.archive.util.archiveReader
|
||||||
|
import yokai.core.archive.util.epubReader
|
||||||
import yokai.i18n.MR
|
import yokai.i18n.MR
|
||||||
import yokai.util.lang.getString
|
import yokai.util.lang.getString
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ class ChapterLoader(
|
||||||
when (format) {
|
when (format) {
|
||||||
is LocalSource.Format.Directory -> DirectoryPageLoader(format.file)
|
is LocalSource.Format.Directory -> DirectoryPageLoader(format.file)
|
||||||
is LocalSource.Format.Archive -> ArchivePageLoader(format.file.archiveReader(context))
|
is LocalSource.Format.Archive -> ArchivePageLoader(format.file.archiveReader(context))
|
||||||
is LocalSource.Format.Epub -> EpubPageLoader(format.file.archiveReader(context))
|
is LocalSource.Format.Epub -> EpubPageLoader(format.file.epubReader(context))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> error(context.getString(MR.strings.source_not_installed))
|
else -> error(context.getString(MR.strings.source_not_installed))
|
||||||
|
|
|
@ -11,7 +11,7 @@ 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 uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import yokai.core.archive.archiveReader
|
import yokai.core.archive.util.archiveReader
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loader used to load a chapter from the downloaded chapters.
|
* Loader used to load a chapter from the downloaded chapters.
|
||||||
|
|
|
@ -2,21 +2,15 @@ package eu.kanade.tachiyomi.ui.reader.loader
|
||||||
|
|
||||||
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 yokai.core.archive.EpubReader
|
||||||
import yokai.core.archive.ArchiveReader
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loader used to load a chapter from a .epub file.
|
* Loader used to load a chapter from a .epub file.
|
||||||
*/
|
*/
|
||||||
class EpubPageLoader(reader: ArchiveReader) : PageLoader() {
|
class EpubPageLoader(private val epub: EpubReader) : PageLoader() {
|
||||||
|
|
||||||
override val isLocal: Boolean = true
|
override val isLocal: Boolean = true
|
||||||
|
|
||||||
/**
|
|
||||||
* The epub file.
|
|
||||||
*/
|
|
||||||
private val epub = EpubFile(reader)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recycles this loader and the open zip.
|
* Recycles this loader and the open zip.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
package eu.kanade.tachiyomi.util.storage
|
package yokai.util
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import java.text.ParseException
|
import java.text.ParseException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.Locale
|
||||||
|
import yokai.core.archive.EpubReader
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills manga and chapter metadata using this epub file's metadata.
|
* Fills manga and chapter metadata using this epub file's metadata.
|
||||||
*/
|
*/
|
||||||
fun EpubFile.fillMetadata(chapter: SChapter, manga: SManga) {
|
fun EpubReader.fillMetadata(chapter: SChapter, manga: SManga) {
|
||||||
val ref = getPackageHref()
|
val ref = getPackageHref()
|
||||||
val doc = getPackageDocument(ref)
|
val doc = getPackageDocument(ref)
|
||||||
|
|
15
core/archive/build.gradle.kts
Normal file
15
core/archive/build.gradle.kts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
plugins {
|
||||||
|
id("yokai.android.library")
|
||||||
|
kotlin("android")
|
||||||
|
alias(kotlinx.plugins.serialization)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "yokai.core.archive"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.jsoup)
|
||||||
|
implementation(libs.libarchive)
|
||||||
|
implementation(libs.unifile)
|
||||||
|
}
|
2
core/archive/src/main/AndroidManifest.xml
Normal file
2
core/archive/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest />
|
|
@ -1,12 +1,13 @@
|
||||||
package yokai.core.archive
|
package yokai.core.archive
|
||||||
|
|
||||||
|
import java.io.InputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import kotlin.concurrent.Volatile
|
import kotlin.concurrent.Volatile
|
||||||
import me.zhanghai.android.libarchive.Archive
|
import me.zhanghai.android.libarchive.Archive
|
||||||
import me.zhanghai.android.libarchive.ArchiveEntry
|
import me.zhanghai.android.libarchive.ArchiveEntry
|
||||||
import me.zhanghai.android.libarchive.ArchiveException
|
import me.zhanghai.android.libarchive.ArchiveException
|
||||||
|
|
||||||
class AndroidArchiveInputStream(buffer: Long, size: Long) : ArchiveInputStream() {
|
class ArchiveInputStream(buffer: Long, size: Long) : InputStream() {
|
||||||
private val lock = Any()
|
private val lock = Any()
|
||||||
@Volatile
|
@Volatile
|
||||||
private var isClosed = false
|
private var isClosed = false
|
|
@ -1,23 +1,22 @@
|
||||||
package yokai.core.archive
|
package yokai.core.archive
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.system.Os
|
import android.system.Os
|
||||||
import android.system.OsConstants
|
import android.system.OsConstants
|
||||||
import com.hippo.unifile.UniFile
|
import java.io.Closeable
|
||||||
import eu.kanade.tachiyomi.util.system.openFileDescriptor
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import me.zhanghai.android.libarchive.ArchiveException
|
import me.zhanghai.android.libarchive.ArchiveException
|
||||||
|
|
||||||
class AndroidArchiveReader(pfd: ParcelFileDescriptor) : ArchiveReader {
|
class ArchiveReader(pfd: ParcelFileDescriptor) : Closeable {
|
||||||
val size = pfd.statSize
|
val size = pfd.statSize
|
||||||
val address = Os.mmap(0, size, OsConstants.PROT_READ, OsConstants.MAP_PRIVATE, pfd.fileDescriptor, 0)
|
val address = Os.mmap(0, size, OsConstants.PROT_READ, OsConstants.MAP_PRIVATE, pfd.fileDescriptor, 0)
|
||||||
|
|
||||||
override fun <T> useEntries(block: (Sequence<ArchiveEntry>) -> T): T =
|
fun <T> useEntries(block: (Sequence<ArchiveEntry>) -> T): T = ArchiveInputStream(address, size).use {
|
||||||
AndroidArchiveInputStream(address, size).use { block(generateSequence { it.getNextEntry() }) }
|
block(generateSequence { it.getNextEntry() })
|
||||||
|
}
|
||||||
|
|
||||||
override fun getInputStream(entryName: String): InputStream? {
|
fun getInputStream(entryName: String): InputStream? {
|
||||||
val archive = AndroidArchiveInputStream(address, size)
|
val archive = ArchiveInputStream(address, size)
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
val entry = archive.getNextEntry() ?: break
|
val entry = archive.getNextEntry() ?: break
|
||||||
|
@ -37,6 +36,3 @@ class AndroidArchiveReader(pfd: ParcelFileDescriptor) : ArchiveReader {
|
||||||
Os.munmap(address, size)
|
Os.munmap(address, size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun UniFile.archiveReader(context: Context): ArchiveReader =
|
|
||||||
openFileDescriptor(context, "r").use { AndroidArchiveReader(it) }
|
|
|
@ -1,16 +1,15 @@
|
||||||
package eu.kanade.tachiyomi.util.storage
|
package yokai.core.archive
|
||||||
|
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import yokai.core.archive.ArchiveReader
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper over ZipFile to load files in epub format.
|
* Wrapper over ZipFile to load files in epub format.
|
||||||
*/
|
*/
|
||||||
class EpubFile(private val reader: ArchiveReader) : Closeable by reader {
|
class EpubReader(private val reader: ArchiveReader) : Closeable by reader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path separator used by this epub.
|
* Path separator used by this epub.
|
|
@ -4,12 +4,12 @@ import android.content.Context
|
||||||
import android.system.Os
|
import android.system.Os
|
||||||
import android.system.StructStat
|
import android.system.StructStat
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.util.system.openFileDescriptor
|
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import me.zhanghai.android.libarchive.Archive
|
import me.zhanghai.android.libarchive.Archive
|
||||||
import me.zhanghai.android.libarchive.ArchiveEntry
|
import me.zhanghai.android.libarchive.ArchiveEntry
|
||||||
import me.zhanghai.android.libarchive.ArchiveException
|
import me.zhanghai.android.libarchive.ArchiveException
|
||||||
|
import yokai.core.archive.util.openFileDescriptor
|
||||||
|
|
||||||
class ZipWriter(val context: Context, file: UniFile) : Closeable {
|
class ZipWriter(val context: Context, file: UniFile) : Closeable {
|
||||||
private val pfd = file.openFileDescriptor(context, "wt")
|
private val pfd = file.openFileDescriptor(context, "wt")
|
|
@ -0,0 +1,14 @@
|
||||||
|
package yokai.core.archive.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.ParcelFileDescriptor
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
|
import yokai.core.archive.ArchiveReader
|
||||||
|
import yokai.core.archive.EpubReader
|
||||||
|
|
||||||
|
fun UniFile.openFileDescriptor(context: Context, mode: String): ParcelFileDescriptor =
|
||||||
|
context.contentResolver.openFileDescriptor(uri, mode) ?: error("Failed to open file descriptor: ${filePath ?: uri.toString()}")
|
||||||
|
|
||||||
|
fun UniFile.archiveReader(context: Context): ArchiveReader = openFileDescriptor(context, "r").use { ArchiveReader(it) }
|
||||||
|
|
||||||
|
fun UniFile.epubReader(context: Context): EpubReader = EpubReader(archiveReader(context))
|
|
@ -60,7 +60,7 @@ kotlin {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "yokai.core"
|
namespace = "yokai.core.main"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.util.system
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.FileUtils
|
import android.os.FileUtils
|
||||||
import android.os.ParcelFileDescriptor
|
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -48,6 +47,3 @@ fun UniFile.writeText(string: String, onComplete: () -> Unit = {}) {
|
||||||
onComplete()
|
onComplete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun UniFile.openFileDescriptor(context: Context, mode: String): ParcelFileDescriptor =
|
|
||||||
context.contentResolver.openFileDescriptor(uri, mode) ?: error("Failed to open file descriptor: $displayablePath")
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package yokai.core.archive
|
|
||||||
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
// TODO: Use Okio's Source
|
|
||||||
abstract class ArchiveInputStream : InputStream()
|
|
|
@ -1,9 +0,0 @@
|
||||||
package yokai.core.archive
|
|
||||||
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
interface ArchiveReader : Closeable {
|
|
||||||
fun <T> useEntries(block: (Sequence<ArchiveEntry>) -> T): T
|
|
||||||
fun getInputStream(entryName: String): InputStream?
|
|
||||||
}
|
|
|
@ -31,6 +31,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||||
|
|
||||||
rootProject.name = "Yokai"
|
rootProject.name = "Yokai"
|
||||||
include(":app")
|
include(":app")
|
||||||
|
include(":core:archive")
|
||||||
include(":core:main")
|
include(":core:main")
|
||||||
include(":data")
|
include(":data")
|
||||||
include(":domain")
|
include(":domain")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue