mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-22 04:22:32 +01:00
Minor cleanup of TachideskPageLoader
This commit is contained in:
@@ -26,17 +26,21 @@ import com.seiko.imageloader.request.ImageRequestBuilder
|
|||||||
import com.seiko.imageloader.request.Options
|
import com.seiko.imageloader.request.Options
|
||||||
import com.seiko.imageloader.request.SourceResult
|
import com.seiko.imageloader.request.SourceResult
|
||||||
import io.ktor.client.plugins.onDownload
|
import io.ktor.client.plugins.onDownload
|
||||||
|
import io.ktor.client.statement.HttpResponse
|
||||||
import io.ktor.client.statement.bodyAsChannel
|
import io.ktor.client.statement.bodyAsChannel
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okio.BufferedSource
|
import okio.BufferedSource
|
||||||
@@ -48,7 +52,7 @@ import org.lighthousegames.logging.logging
|
|||||||
class TachideskPageLoader(
|
class TachideskPageLoader(
|
||||||
val chapter: ReaderChapter,
|
val chapter: ReaderChapter,
|
||||||
readerPreferences: ReaderPreferences,
|
readerPreferences: ReaderPreferences,
|
||||||
getChapterPage: GetChapterPage,
|
private val getChapterPage: GetChapterPage,
|
||||||
private val chapterCache: DiskCache,
|
private val chapterCache: DiskCache,
|
||||||
private val bitmapDecoderFactory: BitmapDecoderFactory
|
private val bitmapDecoderFactory: BitmapDecoderFactory
|
||||||
) : PageLoader() {
|
) : PageLoader() {
|
||||||
@@ -75,84 +79,101 @@ class TachideskPageLoader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
repeat(readerPreferences.threads().get()) {
|
readerPreferences.threads()
|
||||||
scope.launch {
|
.stateIn(scope)
|
||||||
while (true) {
|
.mapLatest {
|
||||||
try {
|
coroutineScope {
|
||||||
for (priorityPage in channel) {
|
repeat(it) {
|
||||||
val page = priorityPage.page
|
launch {
|
||||||
if (page.status.value == ReaderPage.Status.QUEUE) {
|
while (true) {
|
||||||
page.status.value = ReaderPage.Status.WORKING
|
try {
|
||||||
log.debug { "Loading page ${page.index}" }
|
for (priorityPage in channel) {
|
||||||
getChapterPage.asFlow(chapter.chapter, page.index) {
|
val page = priorityPage.page
|
||||||
onDownload { bytesSentTotal, contentLength ->
|
if (page.status.value == ReaderPage.Status.QUEUE) {
|
||||||
page.progress.value = (bytesSentTotal.toFloat() / contentLength).coerceAtMost(1.0F)
|
page.status.value = ReaderPage.Status.WORKING
|
||||||
|
fetchImage(page)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.throwIfCancellation()
|
||||||
|
log.warn(e) { "Error in loop" }
|
||||||
}
|
}
|
||||||
.onEach {
|
|
||||||
val editor = chapterCache.edit(page.cacheKey)
|
|
||||||
?: throw Exception("Couldn't open cache")
|
|
||||||
try {
|
|
||||||
FileSystem.SYSTEM.write(editor.data) {
|
|
||||||
it.bodyAsChannel().toSource().use {
|
|
||||||
writeAll(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
editor.commit()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
editor.abortQuietly()
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
page.bitmap.value = StableHolder {
|
|
||||||
chapterCache[page.cacheKey]?.use {
|
|
||||||
it.source().use { source ->
|
|
||||||
val decoder = bitmapDecoderFactory.create(
|
|
||||||
SourceResult(
|
|
||||||
ImageRequestBuilder().build(),
|
|
||||||
source
|
|
||||||
),
|
|
||||||
Options()
|
|
||||||
)
|
|
||||||
if (decoder != null) {
|
|
||||||
runCatching { decoder.decode() as DecodeImageResult }
|
|
||||||
.mapCatching {
|
|
||||||
ReaderPage.ImageDecodeState.Success(
|
|
||||||
it.image.asImageBitmap().also {
|
|
||||||
page.bitmapInfo.value = ReaderPage.BitmapInfo(
|
|
||||||
IntSize(it.width, it.height)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.getOrElse {
|
|
||||||
ReaderPage.ImageDecodeState.FailedToDecode(it)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ReaderPage.ImageDecodeState.UnknownDecoder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} ?: ReaderPage.ImageDecodeState.FailedToGetSnapShot
|
|
||||||
}
|
|
||||||
page.status.value = ReaderPage.Status.READY
|
|
||||||
page.error.value = null
|
|
||||||
}
|
|
||||||
.catch {
|
|
||||||
page.bitmap.value = StableHolder(null)
|
|
||||||
page.status.value = ReaderPage.Status.ERROR
|
|
||||||
page.error.value = it.message
|
|
||||||
log.warn(it) { "Failed to get page ${page.index} for chapter ${chapter.chapter.index} for ${chapter.chapter.mangaId}" }
|
|
||||||
}
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
e.throwIfCancellation()
|
|
||||||
log.warn(e) { "Error in loop" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.launchIn(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun fetchImage(page: ReaderPage) {
|
||||||
|
log.debug { "Loading page ${page.index}" }
|
||||||
|
getChapterPage.asFlow(chapter.chapter, page.index) {
|
||||||
|
onDownload { bytesSentTotal, contentLength ->
|
||||||
|
page.progress.value = (bytesSentTotal.toFloat() / contentLength).coerceAtMost(1.0F)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.onEach {
|
||||||
|
putImageInCache(it, page)
|
||||||
|
page.bitmap.value = StableHolder { getImageFromCache(page) }
|
||||||
|
page.status.value = ReaderPage.Status.READY
|
||||||
|
page.error.value = null
|
||||||
|
}
|
||||||
|
.catch {
|
||||||
|
page.bitmap.value = StableHolder(null)
|
||||||
|
page.status.value = ReaderPage.Status.ERROR
|
||||||
|
page.error.value = it.message
|
||||||
|
log.warn(it) { "Failed to get page ${page.index} for chapter ${chapter.chapter.index} for ${chapter.chapter.mangaId}" }
|
||||||
|
}
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun putImageInCache(response: HttpResponse, page: ReaderPage) {
|
||||||
|
val editor = chapterCache.edit(page.cacheKey)
|
||||||
|
?: throw Exception("Couldn't open cache")
|
||||||
|
try {
|
||||||
|
FileSystem.SYSTEM.write(editor.data) {
|
||||||
|
response.bodyAsChannel().toSource().use {
|
||||||
|
writeAll(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editor.commit()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
editor.abortQuietly()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getImageFromCache(page: ReaderPage): ReaderPage.ImageDecodeState {
|
||||||
|
return chapterCache[page.cacheKey]?.use {
|
||||||
|
it.source().use { source ->
|
||||||
|
val decoder = bitmapDecoderFactory.create(
|
||||||
|
SourceResult(
|
||||||
|
ImageRequestBuilder().build(),
|
||||||
|
source
|
||||||
|
),
|
||||||
|
Options()
|
||||||
|
)
|
||||||
|
if (decoder != null) {
|
||||||
|
runCatching { decoder.decode() as DecodeImageResult }
|
||||||
|
.mapCatching {
|
||||||
|
ReaderPage.ImageDecodeState.Success(
|
||||||
|
it.image.asImageBitmap().also {
|
||||||
|
page.bitmapInfo.value = ReaderPage.BitmapInfo(
|
||||||
|
IntSize(it.width, it.height)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.getOrElse {
|
||||||
|
ReaderPage.ImageDecodeState.FailedToDecode(it)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReaderPage.ImageDecodeState.UnknownDecoder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ?: ReaderPage.ImageDecodeState.FailedToGetSnapShot
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user