diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4eb9d6561..e00cdea0a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -141,6 +141,7 @@ android { dependencies { implementation(projects.i18n) + implementation(projects.core.archive) implementation(projects.core.common) implementation(projects.coreMetadata) implementation(projects.sourceApi) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index a8fd85f43..0ca27494d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -38,7 +38,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import logcat.LogPriority -import mihon.core.common.archive.ZipWriter +import mihon.core.archive.ZipWriter import nl.adaptivity.xmlutil.serialization.XML import okhttp3.Response import tachiyomi.core.common.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ArchivePageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ArchivePageLoader.kt index 397ac51bc..656d1c797 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ArchivePageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ArchivePageLoader.kt @@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.ui.reader.loader import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder -import mihon.core.common.archive.ArchiveReader +import mihon.core.archive.ArchiveReader import tachiyomi.core.common.util.system.ImageUtil /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt index 3c1b34d6c..903938a47 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt @@ -6,7 +6,8 @@ import eu.kanade.tachiyomi.data.download.DownloadProvider import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter -import mihon.core.common.archive.archiveReader +import mihon.core.archive.archiveReader +import mihon.core.archive.epubReader import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.logcat @@ -95,7 +96,7 @@ class ChapterLoader( when (format) { is Format.Directory -> DirectoryPageLoader(format.file) is Format.Archive -> ArchivePageLoader(format.file.archiveReader(context)) - is Format.Epub -> EpubPageLoader(format.file.archiveReader(context)) + is Format.Epub -> EpubPageLoader(format.file.epubReader(context)) } } source is HttpSource -> HttpPageLoader(chapter, source) 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 7b0f5c36c..d85bb4f75 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 @@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage -import mihon.core.common.archive.archiveReader +import mihon.core.archive.archiveReader import tachiyomi.domain.manga.model.Manga import uy.kohesive.injekt.injectLazy 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 8ace2fdee..cc43487a8 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 @@ -2,27 +2,22 @@ package eu.kanade.tachiyomi.ui.reader.loader import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage -import eu.kanade.tachiyomi.util.storage.EpubFile -import mihon.core.common.archive.ArchiveReader +import mihon.core.archive.EpubReader /** * Loader used to load a chapter from a .epub file. */ -internal class EpubPageLoader(reader: ArchiveReader) : PageLoader() { - - private val epub = EpubFile(reader) +internal class EpubPageLoader(private val reader: EpubReader) : PageLoader() { override var isLocal: Boolean = true override suspend fun getPages(): List { - return epub.getImagesFromPages() - .mapIndexed { i, path -> - val streamFn = { epub.getInputStream(path)!! } - ReaderPage(i).apply { - stream = streamFn - status = Page.State.READY - } + return reader.getImagesFromPages().mapIndexed { i, path -> + ReaderPage(i).apply { + stream = { reader.getInputStream(path)!! } + status = Page.State.READY } + } } override suspend fun loadPage(page: ReaderPage) { @@ -31,6 +26,6 @@ internal class EpubPageLoader(reader: ArchiveReader) : PageLoader() { override fun recycle() { super.recycle() - epub.close() + reader.close() } } diff --git a/core/archive/.gitignore b/core/archive/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/core/archive/.gitignore @@ -0,0 +1 @@ +/build diff --git a/core/archive/build.gradle.kts b/core/archive/build.gradle.kts new file mode 100644 index 000000000..ae2bda74d --- /dev/null +++ b/core/archive/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("mihon.library") + kotlin("android") + kotlin("plugin.serialization") +} + +android { + namespace = "mihon.core.archive" +} + +dependencies { + implementation(libs.jsoup) + implementation(libs.libarchive) + implementation(libs.unifile) +} diff --git a/core/archive/src/main/AndroidManifest.xml b/core/archive/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8072ee00d --- /dev/null +++ b/core/archive/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveEntry.kt b/core/archive/src/main/kotlin/mihon/core/archive/ArchiveEntry.kt similarity index 67% rename from core/common/src/main/kotlin/mihon/core/common/archive/ArchiveEntry.kt rename to core/archive/src/main/kotlin/mihon/core/archive/ArchiveEntry.kt index 26240de00..a4959a95c 100644 --- a/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveEntry.kt +++ b/core/archive/src/main/kotlin/mihon/core/archive/ArchiveEntry.kt @@ -1,4 +1,4 @@ -package mihon.core.common.archive +package mihon.core.archive class ArchiveEntry( val name: String, diff --git a/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt b/core/archive/src/main/kotlin/mihon/core/archive/ArchiveInputStream.kt similarity index 94% rename from core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt rename to core/archive/src/main/kotlin/mihon/core/archive/ArchiveInputStream.kt index 1499867c8..a14d803b1 100644 --- a/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt +++ b/core/archive/src/main/kotlin/mihon/core/archive/ArchiveInputStream.kt @@ -1,4 +1,4 @@ -package mihon.core.common.archive +package mihon.core.archive import me.zhanghai.android.libarchive.Archive import me.zhanghai.android.libarchive.ArchiveEntry @@ -7,7 +7,7 @@ import java.io.InputStream import java.nio.ByteBuffer import kotlin.concurrent.Volatile -class ArchiveInputStream(buffer: Long, size: Long) : InputStream() { +internal class ArchiveInputStream(buffer: Long, size: Long) : InputStream() { private val lock = Any() @Volatile diff --git a/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveReader.kt b/core/archive/src/main/kotlin/mihon/core/archive/ArchiveReader.kt similarity index 59% rename from core/common/src/main/kotlin/mihon/core/common/archive/ArchiveReader.kt rename to core/archive/src/main/kotlin/mihon/core/archive/ArchiveReader.kt index 28467d0fe..0c9aaddae 100644 --- a/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveReader.kt +++ b/core/archive/src/main/kotlin/mihon/core/archive/ArchiveReader.kt @@ -1,21 +1,19 @@ -package mihon.core.common.archive +package mihon.core.archive -import android.content.Context import android.os.ParcelFileDescriptor import android.system.Os import android.system.OsConstants -import com.hippo.unifile.UniFile import me.zhanghai.android.libarchive.ArchiveException -import tachiyomi.core.common.storage.openFileDescriptor import java.io.Closeable import java.io.InputStream class ArchiveReader(pfd: ParcelFileDescriptor) : Closeable { - val size = pfd.statSize - val address = Os.mmap(0, size, OsConstants.PROT_READ, OsConstants.MAP_PRIVATE, pfd.fileDescriptor, 0) + private val size = pfd.statSize + private val address = Os.mmap(0, size, OsConstants.PROT_READ, OsConstants.MAP_PRIVATE, pfd.fileDescriptor, 0) - inline fun useEntries(block: (Sequence) -> T): T = - ArchiveInputStream(address, size).use { block(generateSequence { it.getNextEntry() }) } + fun useEntries(block: (Sequence) -> T): T = ArchiveInputStream(address, size).use { + block(generateSequence { it.getNextEntry() }) + } fun getInputStream(entryName: String): InputStream? { val archive = ArchiveInputStream(address, size) @@ -38,5 +36,3 @@ class ArchiveReader(pfd: ParcelFileDescriptor) : Closeable { Os.munmap(address, size) } } - -fun UniFile.archiveReader(context: Context) = openFileDescriptor(context, "r").use { ArchiveReader(it) } diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt b/core/archive/src/main/kotlin/mihon/core/archive/EpubReader.kt similarity index 95% rename from core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt rename to core/archive/src/main/kotlin/mihon/core/archive/EpubReader.kt index b194c5ee3..620602c81 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt +++ b/core/archive/src/main/kotlin/mihon/core/archive/EpubReader.kt @@ -1,6 +1,5 @@ -package eu.kanade.tachiyomi.util.storage +package mihon.core.archive -import mihon.core.common.archive.ArchiveReader import org.jsoup.Jsoup import org.jsoup.nodes.Document import java.io.Closeable @@ -8,9 +7,9 @@ import java.io.File import java.io.InputStream /** - * Wrapper over ZipFile to load files in epub format. + * Wrapper over ArchiveReader 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. diff --git a/core/archive/src/main/kotlin/mihon/core/archive/UniFileExtensions.kt b/core/archive/src/main/kotlin/mihon/core/archive/UniFileExtensions.kt new file mode 100644 index 000000000..a87d9c304 --- /dev/null +++ b/core/archive/src/main/kotlin/mihon/core/archive/UniFileExtensions.kt @@ -0,0 +1,12 @@ +package mihon.core.archive + +import android.content.Context +import android.os.ParcelFileDescriptor +import com.hippo.unifile.UniFile + +internal 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) = openFileDescriptor(context, "r").use { ArchiveReader(it) } + +fun UniFile.epubReader(context: Context) = EpubReader(archiveReader(context)) diff --git a/core/common/src/main/kotlin/mihon/core/common/archive/ZipWriter.kt b/core/archive/src/main/kotlin/mihon/core/archive/ZipWriter.kt similarity index 88% rename from core/common/src/main/kotlin/mihon/core/common/archive/ZipWriter.kt rename to core/archive/src/main/kotlin/mihon/core/archive/ZipWriter.kt index b5d201516..6a7cc0de7 100644 --- a/core/common/src/main/kotlin/mihon/core/common/archive/ZipWriter.kt +++ b/core/archive/src/main/kotlin/mihon/core/archive/ZipWriter.kt @@ -1,4 +1,4 @@ -package mihon.core.common.archive +package mihon.core.archive import android.content.Context import android.system.Os @@ -7,7 +7,6 @@ import com.hippo.unifile.UniFile import me.zhanghai.android.libarchive.Archive import me.zhanghai.android.libarchive.ArchiveEntry import me.zhanghai.android.libarchive.ArchiveException -import tachiyomi.core.common.storage.openFileDescriptor import java.io.Closeable import java.nio.ByteBuffer @@ -65,10 +64,10 @@ private fun StructStat.toArchiveStat() = ArchiveEntry.StructStat().apply { stSize = st_size stBlksize = st_blksize stBlocks = st_blocks - stAtim = timespec(st_atime) - stMtim = timespec(st_mtime) - stCtim = timespec(st_ctime) + stAtim = st_atime.toTimespec() + stMtim = st_mtime.toTimespec() + stCtim = st_ctime.toTimespec() stIno = st_ino } -private fun timespec(tvSec: Long) = ArchiveEntry.StructTimespec().also { it.tvSec = tvSec } +private fun Long.toTimespec() = ArchiveEntry.StructTimespec().also { it.tvSec = this } diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt b/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt index 4b04ff405..8bbd9b3a7 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt @@ -1,7 +1,5 @@ package tachiyomi.core.common.storage -import android.content.Context -import android.os.ParcelFileDescriptor import com.hippo.unifile.UniFile val UniFile.extension: String? @@ -12,6 +10,3 @@ val UniFile.nameWithoutExtension: String? val UniFile.displayablePath: String get() = filePath ?: uri.toString() - -fun UniFile.openFileDescriptor(context: Context, mode: String): ParcelFileDescriptor = - context.contentResolver.openFileDescriptor(uri, mode) ?: error("Failed to open file descriptor: $displayablePath") diff --git a/settings.gradle.kts b/settings.gradle.kts index b1a2a2dd5..1edc5e222 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -40,6 +40,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") rootProject.name = "Mihon" include(":app") include(":core-metadata") +include(":core:archive") include(":core:common") include(":data") include(":domain") diff --git a/source-local/build.gradle.kts b/source-local/build.gradle.kts index 25c268a34..1e1d3e072 100644 --- a/source-local/build.gradle.kts +++ b/source-local/build.gradle.kts @@ -16,6 +16,7 @@ kotlin { } val androidMain by getting { dependencies { + implementation(projects.core.archive) implementation(projects.core.common) implementation(projects.coreMetadata) 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 87b888257..12a62f73c 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -11,13 +11,13 @@ import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder -import eu.kanade.tachiyomi.util.storage.EpubFile import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream import logcat.LogPriority -import mihon.core.common.archive.archiveReader +import mihon.core.archive.archiveReader +import mihon.core.archive.epubReader import nl.adaptivity.xmlutil.AndroidXmlReader import nl.adaptivity.xmlutil.serialization.XML import tachiyomi.core.common.i18n.stringResource @@ -253,7 +253,7 @@ actual class LocalSource( val format = Format.valueOf(chapterFile) if (format is Format.Epub) { - EpubFile(format.file.archiveReader(context)).use { epub -> + format.file.epubReader(context).use { epub -> epub.fillMetadata(manga, this) } } @@ -323,7 +323,7 @@ actual class LocalSource( } } is Format.Epub -> { - EpubFile(format.file.archiveReader(context)).use { epub -> + format.file.epubReader(context).use { epub -> val entry = epub.getImagesFromPages().firstOrNull() entry?.let { coverManager.update(manga, epub.getInputStream(it)!!) } diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/metadata/EpubFile.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/metadata/EpubReaderExtensions.kt similarity index 92% rename from source-local/src/androidMain/kotlin/tachiyomi/source/local/metadata/EpubFile.kt rename to source-local/src/androidMain/kotlin/tachiyomi/source/local/metadata/EpubReaderExtensions.kt index 6bade530b..388156bc7 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/metadata/EpubFile.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/metadata/EpubReaderExtensions.kt @@ -2,7 +2,7 @@ package tachiyomi.source.local.metadata import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.util.storage.EpubFile +import mihon.core.archive.EpubReader import java.text.ParseException import java.text.SimpleDateFormat import java.util.Locale @@ -10,7 +10,7 @@ import java.util.Locale /** * Fills manga and chapter metadata using this epub file's metadata. */ -fun EpubFile.fillMetadata(manga: SManga, chapter: SChapter) { +fun EpubReader.fillMetadata(manga: SManga, chapter: SChapter) { val ref = getPackageHref() val doc = getPackageDocument(ref)