refactor(downloader/split): Use UniFile to split tall images

Hopefully will improve stability
This commit is contained in:
Ahmad Ansori Palembani 2024-08-12 07:17:58 +07:00
parent 5ac816225f
commit d19aca3e0b
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
2 changed files with 26 additions and 29 deletions

View file

@ -55,6 +55,7 @@ import yokai.domain.download.DownloadPreferences
import yokai.i18n.MR import yokai.i18n.MR
import yokai.util.lang.getString import yokai.util.lang.getString
import java.io.File import java.io.File
import java.util.*
import java.util.zip.* import java.util.zip.*
/** /**
@ -540,23 +541,20 @@ class Downloader(
return ImageUtil.getExtensionFromMimeType(mime) { file.openInputStream() } return ImageUtil.getExtensionFromMimeType(mime) { file.openInputStream() }
} }
private fun splitTallImageIfNeeded(page: Page, tmpDir: UniFile): Boolean { private fun splitTallImageIfNeeded(page: Page, tmpDir: UniFile) {
if (!preferences.splitTallImages().get()) return true if (!preferences.splitTallImages().get()) return
val filename = String.format("%03d", page.number) try {
val imageFile = tmpDir.listFiles()?.find { it.name.orEmpty().startsWith(filename) } val fileName = "%03d".format(Locale.ENGLISH, page.number)
?: throw Error(context.getString(MR.strings.download_notifier_split_page_not_found, page.number)) val imageFile = tmpDir.listFiles()?.firstOrNull { it.name.orEmpty().startsWith(fileName) }
val imageFilePath = imageFile.filePath
?: throw Error(context.getString(MR.strings.download_notifier_split_page_not_found, page.number)) ?: throw Error(context.getString(MR.strings.download_notifier_split_page_not_found, page.number))
// check if the original page was previously split before then skip. // Check if the original page was previously split before then skip.
if (imageFile.name.orEmpty().contains("__")) return true if (imageFile.name.orEmpty().startsWith("${fileName}__")) return
return try { ImageUtil.splitTallImage(tmpDir, imageFile, fileName)
ImageUtil.splitTallImage(imageFile, imageFilePath)
} catch (e: Exception) { } catch (e: Exception) {
Logger.e(e) Logger.e(e) { "Failed to split downloaded image"}
false
} }
} }
@ -582,7 +580,7 @@ class Downloader(
val downloadedImagesCount = tmpDir.listFiles().orEmpty().count { val downloadedImagesCount = tmpDir.listFiles().orEmpty().count {
val fileName = it.name.orEmpty() val fileName = it.name.orEmpty()
when { when {
fileName in listOf(/*COMIC_INFO_FILE, */NOMEDIA_FILE) -> false fileName in listOf(COMIC_INFO_FILE, NOMEDIA_FILE) -> false
fileName.endsWith(".tmp") -> false fileName.endsWith(".tmp") -> false
// Only count the first split page and not the others // Only count the first split page and not the others
fileName.contains("__") && !fileName.endsWith("__001.jpg") -> false fileName.contains("__") && !fileName.endsWith("__001.jpg") -> false

View file

@ -15,7 +15,6 @@ import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.os.Build import android.os.Build
import android.webkit.MimeTypeMap
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.core.graphics.alpha import androidx.core.graphics.alpha
@ -26,17 +25,12 @@ import androidx.core.graphics.scale
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import yokai.i18n.MR
import yokai.util.lang.getString
import dev.icerock.moko.resources.compose.stringResource
import okio.Buffer import okio.Buffer
import okio.BufferedSource import okio.BufferedSource
import tachiyomi.decoder.Format import tachiyomi.decoder.Format
import tachiyomi.decoder.ImageDecoder import tachiyomi.decoder.ImageDecoder
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream import java.io.InputStream
import java.net.URLConnection import java.util.*
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@ -539,7 +533,7 @@ object ImageUtil {
/** /**
* Splits tall images to improve performance of reader * Splits tall images to improve performance of reader
*/ */
fun splitTallImage(imageFile: UniFile, imageFilePath: String): Boolean { fun splitTallImage(tmpDir: UniFile, imageFile: UniFile, fileName: String): Boolean {
val imageSource = imageFile.openInputStream().use { Buffer().readFrom(it) } val imageSource = imageFile.openInputStream().use { Buffer().readFrom(it) }
if (isAnimatedAndSupported(imageSource) || !isTallImage(imageSource)) { if (isAnimatedAndSupported(imageSource) || !isTallImage(imageSource)) {
return true return true
@ -564,17 +558,22 @@ object ImageUtil {
return try { return try {
splitDataList.forEach { splitData -> splitDataList.forEach { splitData ->
val splitPath = splitImagePath(imageFilePath, splitData.index) val splitImageName = splitImageName(fileName, splitData.index)
// Remove pre-existing split if exists (this split shouldn't exist under normal circumstances)
tmpDir.findFile(splitImageName)?.delete()
val splitFile = tmpDir.createFile(splitImageName)!!
val region = Rect(0, splitData.topOffset, splitData.splitWidth, splitData.bottomOffset) val region = Rect(0, splitData.topOffset, splitData.splitWidth, splitData.bottomOffset)
FileOutputStream(splitPath).use { outputStream -> splitFile.openOutputStream().use { outputStream ->
val splitBitmap = bitmapRegionDecoder.decodeRegion(region, options) val splitBitmap = bitmapRegionDecoder.decodeRegion(region, options)
splitBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream) splitBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
splitBitmap.recycle() splitBitmap.recycle()
} }
Logger.d { Logger.d {
"Success: Split #${splitData.index + 1} with topOffset=${splitData.topOffset} height=${splitData.splitHeight} bottomOffset=${splitData.bottomOffset}" "Success: Split #${splitData.index + 1} with topOffset=${splitData.topOffset} " +
"height=${splitData.splitHeight} bottomOffset=${splitData.bottomOffset}"
} }
} }
imageFile.delete() imageFile.delete()
@ -582,8 +581,8 @@ object ImageUtil {
} catch (e: Exception) { } catch (e: Exception) {
// Image splits were not successfully saved so delete them and keep the original image // Image splits were not successfully saved so delete them and keep the original image
splitDataList splitDataList
.map { splitImagePath(imageFilePath, it.index) } .map { splitImageName(fileName, it.index) }
.forEach { File(it).delete() } .forEach { tmpDir.findFile(it)?.delete() }
Logger.e(e) Logger.e(e)
false false
} finally { } finally {
@ -591,8 +590,8 @@ object ImageUtil {
} }
} }
private fun splitImagePath(imageFilePath: String, index: Int) = private fun splitImageName(fileName: String, index: Int) =
imageFilePath.substringBeforeLast(".") + "__${"%03d".format(index + 1)}.jpg" "${fileName}__${"%03d".format(Locale.ENGLISH, index + 1)}.jpg"
private val BitmapFactory.Options.splitData private val BitmapFactory.Options.splitData
get(): List<SplitData> { get(): List<SplitData> {