Load pages from cbz
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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<MangaPage, Unit>(parent, R.layout.item_page),
|
||||
class PageHolder(parent: ViewGroup, private val loader: PageLoader) :
|
||||
BaseViewHolder<MangaPage, Unit>(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
|
||||
}
|
||||
}
|
||||
@@ -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<String, Job>()
|
||||
@@ -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<File>) -> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
class AlphanumComparator : Comparator<String> {
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user