mirror of
https://git.mihon.tech/mihonapp/mihon
synced 2024-11-21 20:55:41 +03:00
Fix some issues when reading/saving images (#993)
* Fix unsupported mime type error when saving images Avoid using platform mime type map to get extensions as it may not have all mime types we support. * Fix jxl images downloading/reading
This commit is contained in:
parent
cbcd8bd668
commit
daa47e0493
4 changed files with 20 additions and 32 deletions
|
@ -17,6 +17,7 @@ import logcat.LogPriority
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.core.common.storage.extension
|
import tachiyomi.core.common.storage.extension
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
|
import tachiyomi.core.common.util.system.ImageUtil
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
|
@ -160,7 +161,7 @@ class DownloadManager(
|
||||||
fun buildPageList(source: Source, manga: Manga, chapter: Chapter): List<Page> {
|
fun buildPageList(source: Source, manga: Manga, chapter: Chapter): List<Page> {
|
||||||
val chapterDir = provider.findChapterDir(chapter.name, chapter.scanlator, manga.title, source)
|
val chapterDir = provider.findChapterDir(chapter.name, chapter.scanlator, manga.title, source)
|
||||||
val files = chapterDir?.listFiles().orEmpty()
|
val files = chapterDir?.listFiles().orEmpty()
|
||||||
.filter { "image" in it.type.orEmpty() }
|
.filter { it.isFile && ImageUtil.isImage(it.name) { it.openInputStream() } }
|
||||||
|
|
||||||
if (files.isEmpty()) {
|
if (files.isEmpty()) {
|
||||||
throw Exception(context.stringResource(MR.strings.page_list_empty_error))
|
throw Exception(context.stringResource(MR.strings.page_list_empty_error))
|
||||||
|
|
|
@ -523,14 +523,8 @@ class Downloader(
|
||||||
* @param file the file where the image is already downloaded.
|
* @param file the file where the image is already downloaded.
|
||||||
*/
|
*/
|
||||||
private fun getImageExtension(response: Response, file: UniFile): String {
|
private fun getImageExtension(response: Response, file: UniFile): String {
|
||||||
// Read content type if available.
|
|
||||||
val mime = response.body.contentType()?.run { if (type == "image") "image/$subtype" else null }
|
val mime = response.body.contentType()?.run { if (type == "image") "image/$subtype" else null }
|
||||||
// Else guess from the uri.
|
return ImageUtil.getExtensionFromMimeType(mime) { file.openInputStream() }
|
||||||
?: context.contentResolver.getType(file.uri)
|
|
||||||
// Else read magic numbers.
|
|
||||||
?: ImageUtil.findImageType { file.openInputStream() }?.mime
|
|
||||||
|
|
||||||
return ImageUtil.getExtensionFromMimeType(mime)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun splitTallImageIfNeeded(page: Page, tmpDir: UniFile) {
|
private fun splitTallImageIfNeeded(page: Page, tmpDir: UniFile) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.content.contentValuesOf
|
import androidx.core.content.contentValuesOf
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
@ -65,21 +66,26 @@ class ImageSaver(
|
||||||
filename: String,
|
filename: String,
|
||||||
data: () -> InputStream,
|
data: () -> InputStream,
|
||||||
): Uri {
|
): Uri {
|
||||||
val pictureDir =
|
val isMimeTypeSupported = MimeTypeMap.getSingleton().hasMimeType(type.mime)
|
||||||
|
|
||||||
|
val pictureDir = if (isMimeTypeSupported) {
|
||||||
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||||
|
} else {
|
||||||
|
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||||
|
}
|
||||||
|
|
||||||
val imageLocation = (image.location as Location.Pictures).relativePath
|
val imageLocation = (image.location as Location.Pictures).relativePath
|
||||||
val relativePath = listOf(
|
val relativePath = listOf(
|
||||||
Environment.DIRECTORY_PICTURES,
|
if (isMimeTypeSupported) Environment.DIRECTORY_PICTURES else Environment.DIRECTORY_DOCUMENTS,
|
||||||
context.stringResource(MR.strings.app_name),
|
context.stringResource(MR.strings.app_name),
|
||||||
imageLocation,
|
imageLocation,
|
||||||
).joinToString(File.separator)
|
).joinToString(File.separator)
|
||||||
|
|
||||||
val contentValues = contentValuesOf(
|
val contentValues = contentValuesOf(
|
||||||
MediaStore.Images.Media.RELATIVE_PATH to relativePath,
|
MediaStore.MediaColumns.RELATIVE_PATH to relativePath,
|
||||||
MediaStore.Images.Media.DISPLAY_NAME to image.name,
|
MediaStore.MediaColumns.DISPLAY_NAME to if (isMimeTypeSupported) image.name else filename,
|
||||||
MediaStore.Images.Media.MIME_TYPE to type.mime,
|
MediaStore.MediaColumns.MIME_TYPE to type.mime,
|
||||||
MediaStore.Images.Media.DATE_MODIFIED to Instant.now().epochSecond,
|
MediaStore.MediaColumns.DATE_MODIFIED to Instant.now().epochSecond,
|
||||||
)
|
)
|
||||||
|
|
||||||
val picture = findUriOrDefault(relativePath, filename) {
|
val picture = findUriOrDefault(relativePath, filename) {
|
||||||
|
|
|
@ -13,7 +13,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.alpha
|
import androidx.core.graphics.alpha
|
||||||
import androidx.core.graphics.applyCanvas
|
import androidx.core.graphics.applyCanvas
|
||||||
|
@ -29,7 +28,6 @@ import okio.BufferedSource
|
||||||
import tachiyomi.decoder.Format
|
import tachiyomi.decoder.Format
|
||||||
import tachiyomi.decoder.ImageDecoder
|
import tachiyomi.decoder.ImageDecoder
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.URLConnection
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
@ -40,12 +38,8 @@ object ImageUtil {
|
||||||
fun isImage(name: String?, openStream: (() -> InputStream)? = null): Boolean {
|
fun isImage(name: String?, openStream: (() -> InputStream)? = null): Boolean {
|
||||||
if (name == null) return false
|
if (name == null) return false
|
||||||
|
|
||||||
val contentType = try {
|
val extension = name.substringAfterLast('.')
|
||||||
URLConnection.guessContentTypeFromName(name)
|
return ImageType.entries.any { it.extension == extension } || openStream?.let { findImageType(it) } != null
|
||||||
} catch (e: Exception) {
|
|
||||||
null
|
|
||||||
} ?: openStream?.let { findImageType(it)?.mime }
|
|
||||||
return contentType?.startsWith("image/") ?: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findImageType(openStream: () -> InputStream): ImageType? {
|
fun findImageType(openStream: () -> InputStream): ImageType? {
|
||||||
|
@ -69,10 +63,9 @@ object ImageUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getExtensionFromMimeType(mime: String?): String {
|
fun getExtensionFromMimeType(mime: String?, openStream: () -> InputStream): String {
|
||||||
return MimeTypeMap.getSingleton().getExtensionFromMimeType(mime)
|
val type = mime?.let { ImageType.entries.find { it.mime == mime } } ?: findImageType(openStream)
|
||||||
?: SUPPLEMENTARY_MIMETYPE_MAPPING[mime]
|
return type?.extension ?: "jpg"
|
||||||
?: "jpg"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isAnimatedAndSupported(source: BufferedSource): Boolean {
|
fun isAnimatedAndSupported(source: BufferedSource): Boolean {
|
||||||
|
@ -558,12 +551,6 @@ object ImageUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
private val optimalImageHeight = getDisplayMaxHeightInPx * 2
|
private val optimalImageHeight = getDisplayMaxHeightInPx * 2
|
||||||
|
|
||||||
// Android doesn't include some mappings
|
|
||||||
private val SUPPLEMENTARY_MIMETYPE_MAPPING = mapOf(
|
|
||||||
// https://issuetracker.google.com/issues/182703810
|
|
||||||
"image/jxl" to "jxl",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val getDisplayMaxHeightInPx: Int
|
val getDisplayMaxHeightInPx: Int
|
||||||
|
|
Loading…
Reference in a new issue