Avoid hard crash if cached image file was already deleted
Closes #9720
Cette révision appartient à :
Parent
36f307e3bb
révision
3ea026e311
2 fichiers modifiés avec 68 ajouts et 50 suppressions
|
@ -17,10 +17,12 @@ import kotlinx.coroutines.MainScope
|
|||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
import tachiyomi.core.util.lang.withUIContext
|
||||
import tachiyomi.core.util.system.ImageUtil
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.InputStream
|
||||
|
@ -136,40 +138,47 @@ class PagerPageHolder(
|
|||
|
||||
val streamFn = page.stream ?: return
|
||||
|
||||
val (bais, isAnimated, background) = withIOContext {
|
||||
streamFn().buffered(16).use { stream ->
|
||||
process(item, stream).use { itemStream ->
|
||||
val bais = ByteArrayInputStream(itemStream.readBytes())
|
||||
val isAnimated = ImageUtil.isAnimatedAndSupported(bais)
|
||||
bais.reset()
|
||||
val background = if (!isAnimated && viewer.config.automaticBackground) {
|
||||
ImageUtil.chooseBackground(context, bais)
|
||||
} else {
|
||||
null
|
||||
try {
|
||||
val (bais, isAnimated, background) = withIOContext {
|
||||
streamFn().buffered(16).use { stream ->
|
||||
process(item, stream).use { itemStream ->
|
||||
val bais = ByteArrayInputStream(itemStream.readBytes())
|
||||
val isAnimated = ImageUtil.isAnimatedAndSupported(bais)
|
||||
bais.reset()
|
||||
val background = if (!isAnimated && viewer.config.automaticBackground) {
|
||||
ImageUtil.chooseBackground(context, bais)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
bais.reset()
|
||||
Triple(bais, isAnimated, background)
|
||||
}
|
||||
bais.reset()
|
||||
Triple(bais, isAnimated, background)
|
||||
}
|
||||
}
|
||||
}
|
||||
withUIContext {
|
||||
bais.use {
|
||||
setImage(
|
||||
it,
|
||||
isAnimated,
|
||||
Config(
|
||||
zoomDuration = viewer.config.doubleTapAnimDuration,
|
||||
minimumScaleType = viewer.config.imageScaleType,
|
||||
cropBorders = viewer.config.imageCropBorders,
|
||||
zoomStartPosition = viewer.config.imageZoomType,
|
||||
landscapeZoom = viewer.config.landscapeZoom,
|
||||
),
|
||||
)
|
||||
if (!isAnimated) {
|
||||
pageBackground = background
|
||||
withUIContext {
|
||||
bais.use {
|
||||
setImage(
|
||||
it,
|
||||
isAnimated,
|
||||
Config(
|
||||
zoomDuration = viewer.config.doubleTapAnimDuration,
|
||||
minimumScaleType = viewer.config.imageScaleType,
|
||||
cropBorders = viewer.config.imageCropBorders,
|
||||
zoomStartPosition = viewer.config.imageZoomType,
|
||||
landscapeZoom = viewer.config.landscapeZoom,
|
||||
),
|
||||
)
|
||||
if (!isAnimated) {
|
||||
pageBackground = background
|
||||
}
|
||||
}
|
||||
removeErrorLayout()
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
withUIContext {
|
||||
setError()
|
||||
}
|
||||
removeErrorLayout()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,12 @@ import kotlinx.coroutines.flow.collectLatest
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
import tachiyomi.core.util.lang.withUIContext
|
||||
import tachiyomi.core.util.system.ImageUtil
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.InputStream
|
||||
|
||||
|
@ -184,28 +186,35 @@ class WebtoonPageHolder(
|
|||
|
||||
val streamFn = page?.stream ?: return
|
||||
|
||||
val (openStream, isAnimated) = withIOContext {
|
||||
val stream = streamFn().buffered(16)
|
||||
val openStream = process(stream)
|
||||
try {
|
||||
val (openStream, isAnimated) = withIOContext {
|
||||
val stream = streamFn().buffered(16)
|
||||
val openStream = process(stream)
|
||||
|
||||
val isAnimated = ImageUtil.isAnimatedAndSupported(stream)
|
||||
Pair(openStream, isAnimated)
|
||||
}
|
||||
withUIContext {
|
||||
frame.setImage(
|
||||
openStream,
|
||||
isAnimated,
|
||||
ReaderPageImageView.Config(
|
||||
zoomDuration = viewer.config.doubleTapAnimDuration,
|
||||
minimumScaleType = SubsamplingScaleImageView.SCALE_TYPE_FIT_WIDTH,
|
||||
cropBorders = viewer.config.imageCropBorders,
|
||||
),
|
||||
)
|
||||
removeErrorLayout()
|
||||
}
|
||||
// Suspend the coroutine to close the input stream only when the WebtoonPageHolder is recycled
|
||||
suspendCancellableCoroutine<Nothing> { continuation ->
|
||||
continuation.invokeOnCancellation { openStream.close() }
|
||||
val isAnimated = ImageUtil.isAnimatedAndSupported(stream)
|
||||
Pair(openStream, isAnimated)
|
||||
}
|
||||
withUIContext {
|
||||
frame.setImage(
|
||||
openStream,
|
||||
isAnimated,
|
||||
ReaderPageImageView.Config(
|
||||
zoomDuration = viewer.config.doubleTapAnimDuration,
|
||||
minimumScaleType = SubsamplingScaleImageView.SCALE_TYPE_FIT_WIDTH,
|
||||
cropBorders = viewer.config.imageCropBorders,
|
||||
),
|
||||
)
|
||||
removeErrorLayout()
|
||||
}
|
||||
// Suspend the coroutine to close the input stream only when the WebtoonPageHolder is recycled
|
||||
suspendCancellableCoroutine<Nothing> { continuation ->
|
||||
continuation.invokeOnCancellation { openStream.close() }
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
withUIContext {
|
||||
setError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Référencer dans un nouveau ticket