From faf8e57619da83e15637c4df2787d12ef9d173a9 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Tue, 11 Feb 2020 21:01:25 +0200 Subject: [PATCH] Load pages from cbz --- .../core/parser/LocalMangaRepository.kt | 5 +- .../koitharu/kotatsu/ui/reader/PageHolder.kt | 23 +++++-- .../koitharu/kotatsu/ui/reader/PageLoader.kt | 39 ++++++++---- .../kotatsu/ui/reader/ReaderActivity.kt | 2 +- .../kotatsu/utils/AlphanumComparator.kt | 63 +++++++++++++++++++ 5 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/utils/AlphanumComparator.kt diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/LocalMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/LocalMangaRepository.kt index aa240bdb8..28700e7cb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/LocalMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/LocalMangaRepository.kt @@ -10,6 +10,7 @@ import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.domain.local.MangaIndex import org.koitharu.kotatsu.domain.local.MangaZip +import org.koitharu.kotatsu.utils.AlphanumComparator import org.koitharu.kotatsu.utils.ext.longHashCode import org.koitharu.kotatsu.utils.ext.readText import org.koitharu.kotatsu.utils.ext.safe @@ -43,7 +44,7 @@ class LocalMangaRepository(loaderContext: MangaLoaderContext) : BaseMangaReposit .filter { x -> !x.isDirectory && x.name.substringBefore('.').matches(pattern) } } else { zip.entries().asSequence().filter { x -> !x.isDirectory } - } + }.toList().sortedWith(compareBy(AlphanumComparator()) { x -> x.name}) return entries.map { x -> val uri = zipUri(file, x.name) MangaPage( @@ -51,7 +52,7 @@ class LocalMangaRepository(loaderContext: MangaLoaderContext) : BaseMangaReposit url = uri, source = MangaSource.LOCAL ) - }.toList() + } } private fun getDetails(file: File): Manga { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/PageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/PageHolder.kt index e993e7076..4767d11ae 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/PageHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/PageHolder.kt @@ -11,7 +11,8 @@ import org.koitharu.kotatsu.core.model.MangaPage import org.koitharu.kotatsu.ui.common.list.BaseViewHolder import org.koitharu.kotatsu.utils.ext.getDisplayMessage -class PageHolder(parent: ViewGroup, private val loader: PageLoader) : BaseViewHolder(parent, R.layout.item_page), +class PageHolder(parent: ViewGroup, private val loader: PageLoader) : + BaseViewHolder(parent, R.layout.item_page), SubsamplingScaleImageView.OnImageEventListener { init { @@ -26,16 +27,20 @@ class PageHolder(parent: ViewGroup, private val loader: PageLoader) : BaseViewHo progressBar.isVisible = true ssiv.recycle() loader.load(data.url) { - ssiv.setImage(ImageSource.uri(it.toUri())) + val uri = it.getOrNull()?.toUri() + if (uri != null) { + ssiv.setImage(ImageSource.uri(uri)) + } + val error = it.exceptionOrNull() + if (error != null) { + onError(error) + } } } override fun onReady() = Unit - override fun onImageLoadError(e: Exception) { - textView_error.text = e.getDisplayMessage(context.resources) - layout_error.isVisible = true - } + override fun onImageLoadError(e: Exception) = onError(e) override fun onImageLoaded() { progressBar.isVisible = false @@ -46,4 +51,10 @@ class PageHolder(parent: ViewGroup, private val loader: PageLoader) : BaseViewHo override fun onPreviewReleased() = Unit override fun onPreviewLoadError(e: Exception?) = Unit + + private fun onError(e: Throwable) { + textView_error.text = e.getDisplayMessage(context.resources) + layout_error.isVisible = true + progressBar.isVisible = false + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/PageLoader.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/PageLoader.kt index c8ee3491c..bcc14bd82 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/PageLoader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/PageLoader.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.ui.reader -import android.content.Context +import android.net.Uri import kotlinx.coroutines.* import okhttp3.OkHttpClient import okhttp3.Request @@ -9,9 +9,10 @@ import org.koin.core.inject import org.koitharu.kotatsu.core.local.PagesCache import org.koitharu.kotatsu.utils.ext.await import java.io.File +import java.util.zip.ZipFile import kotlin.coroutines.CoroutineContext -class PageLoader(context: Context) : KoinComponent, CoroutineScope, DisposableHandle { +class PageLoader : KoinComponent, CoroutineScope, DisposableHandle { private val job = SupervisorJob() private val tasks = HashMap() @@ -21,9 +22,11 @@ class PageLoader(context: Context) : KoinComponent, CoroutineScope, DisposableHa override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job - fun load(url: String, callback: (File) -> Unit) = launch { - val result = withContext(Dispatchers.IO) { - loadFile(url, false) + fun load(url: String, force: Boolean = false, callback: (Result) -> Unit) = launch { + val result = runCatching { + withContext(Dispatchers.IO) { + loadFile(url, force) + } } callback(result) } @@ -31,17 +34,27 @@ class PageLoader(context: Context) : KoinComponent, CoroutineScope, DisposableHa private suspend fun loadFile(url: String, force: Boolean): File { if (!force) { cache[url]?.let { - return it } } - val request = Request.Builder() - .url(url) - .get() - .build() - return okHttp.newCall(request).await().use { response -> - cache.put(url) { out -> - response.body!!.byteStream().copyTo(out) + val uri = Uri.parse(url) + return if (uri.scheme == "cbz") { + val zip = ZipFile(uri.schemeSpecificPart) + val entry = zip.getEntry(uri.fragment) + zip.getInputStream(entry).use { + cache.put(url) { out -> + it.copyTo(out) + } + } + } else { + val request = Request.Builder() + .url(url) + .get() + .build() + okHttp.newCall(request).await().use { response -> + cache.put(url) { out -> + response.body!!.byteStream().copyTo(out) + } } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt index c1925ad8f..1c98a92d2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt @@ -56,7 +56,7 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, GridTouchHelper.OnG insets } - loader = PageLoader(this) + loader = PageLoader() adapter = PagesAdapter(loader) pager.adapter = adapter presenter.loadChapter(state) diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/AlphanumComparator.kt b/app/src/main/java/org/koitharu/kotatsu/utils/AlphanumComparator.kt new file mode 100644 index 000000000..12e276d63 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/utils/AlphanumComparator.kt @@ -0,0 +1,63 @@ +package org.koitharu.kotatsu.utils + +class AlphanumComparator : Comparator { + + override fun compare(s1: String?, s2: String?): Int { + if (s1 == null || s2 == null) { + return 0 + } + var thisMarker = 0 + var thatMarker = 0 + val s1Length = s1.length + val s2Length = s2.length + while (thisMarker < s1Length && thatMarker < s2Length) { + val thisChunk = getChunk(s1, s1Length, thisMarker) + thisMarker += thisChunk.length + val thatChunk = getChunk(s2, s2Length, thatMarker) + thatMarker += thatChunk.length + // If both chunks contain numeric characters, sort them numerically + var result = 0 + if (thisChunk[0].isDigit() && thatChunk[0].isDigit()) { // Simple chunk comparison by length. + val thisChunkLength = thisChunk.length + result = thisChunkLength - thatChunk.length + // If equal, the first different number counts + if (result == 0) { + for (i in 0 until thisChunkLength) { + result = thisChunk[i] - thatChunk[i] + if (result != 0) { + return result + } + } + } + } else { + result = thisChunk.compareTo(thatChunk) + } + if (result != 0) return result + } + return s1Length - s2Length + } + + private fun getChunk(s: String, slength: Int, marker: Int): String { + var marker = marker + val chunk = StringBuilder() + var c = s[marker] + chunk.append(c) + marker++ + if (c.isDigit()) { + while (marker < slength) { + c = s[marker] + if (!c.isDigit()) break + chunk.append(c) + marker++ + } + } else { + while (marker < slength) { + c = s[marker] + if (c.isDigit()) break + chunk.append(c) + marker++ + } + } + return chunk.toString() + } +} \ No newline at end of file