4 files changed
+64
-64
No files matched your search
@@ -114,15 +114,12 @@ object ImageBytesResolver {
|
||||
}.getOrElse { e -> Result.Error(e.message ?: "Could not download image") }
|
||||
}
|
||||
|
||||
/** Image-only view of the shared MIME table: anything that isn't `image/` saves as `.img`. */
|
||||
fun extensionForMime(mime: String): String =
|
||||
when (mime.substringBefore(';').trim().lowercase()) {
|
||||
"image/jpeg", "image/jpg" -> "jpg"
|
||||
"image/png" -> "png"
|
||||
"image/gif" -> "gif"
|
||||
"image/webp" -> "webp"
|
||||
"image/bmp" -> "bmp"
|
||||
"image/heic", "image/heif" -> "heic"
|
||||
"image/svg+xml" -> "svg"
|
||||
else -> "img"
|
||||
if (mime.trim().startsWith("image/", ignoreCase = true)) {
|
||||
com.m57.hermescontrol.ui.chat
|
||||
.extensionForMime(mime)
|
||||
} else {
|
||||
"img"
|
||||
}
|
||||
}
|
||||
@@ -140,67 +140,41 @@ fun normalizeFrameworkMime(mime: String?): String =
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?: "application/octet-stream"
|
||||
|
||||
/**
|
||||
* MIME aliases and preferred extensions that [MEDIA_BY_EXT] can't express
|
||||
* (it's keyed by extension, so `image/jpeg` would otherwise resolve to its
|
||||
* first entry, `jpeg`). HEIC is save-only here: it isn't classified as inline media.
|
||||
*/
|
||||
private val EXT_BY_MIME_ALIAS: Map<String, String> =
|
||||
mapOf(
|
||||
"image/jpeg" to "jpg",
|
||||
"image/jpg" to "jpg",
|
||||
"image/heic" to "heic",
|
||||
"image/heif" to "heic",
|
||||
"audio/mp3" to "mp3",
|
||||
"audio/x-m4a" to "m4a",
|
||||
"audio/x-wav" to "wav",
|
||||
"audio/wave" to "wav",
|
||||
"application/x-flac" to "flac",
|
||||
"application/ogg" to "ogg",
|
||||
)
|
||||
|
||||
/** Returns the canonical file extension for a given MIME type. */
|
||||
fun extensionForMime(mime: String): String {
|
||||
val clean = mime.substringBefore(';').trim().lowercase()
|
||||
for ((ext, pair) in MEDIA_BY_EXT) {
|
||||
if (pair.second
|
||||
EXT_BY_MIME_ALIAS[clean]?.let { return it }
|
||||
MEDIA_BY_EXT.entries
|
||||
.firstOrNull { (_, pair) ->
|
||||
pair.second
|
||||
.substringBefore(';')
|
||||
.trim()
|
||||
.lowercase() == clean
|
||||
) {
|
||||
return ext
|
||||
}
|
||||
}
|
||||
return when (clean) {
|
||||
"image/jpeg", "image/jpg" -> {
|
||||
"jpg"
|
||||
}
|
||||
|
||||
"video/mp4" -> {
|
||||
"mp4"
|
||||
}
|
||||
|
||||
"video/x-matroska" -> {
|
||||
"mkv"
|
||||
}
|
||||
|
||||
"video/quicktime" -> {
|
||||
"mov"
|
||||
}
|
||||
|
||||
"audio/mpeg", "audio/mp3" -> {
|
||||
"mp3"
|
||||
}
|
||||
|
||||
"audio/mp4", "audio/x-m4a" -> {
|
||||
"m4a"
|
||||
}
|
||||
|
||||
"audio/wav", "audio/x-wav", "audio/wave" -> {
|
||||
"wav"
|
||||
}
|
||||
|
||||
"audio/flac", "application/x-flac" -> {
|
||||
"flac"
|
||||
}
|
||||
|
||||
"audio/ogg", "application/ogg" -> {
|
||||
"ogg"
|
||||
}
|
||||
|
||||
"audio/aac" -> {
|
||||
"aac"
|
||||
}
|
||||
|
||||
else -> {
|
||||
when {
|
||||
clean.startsWith("video/") -> "mp4"
|
||||
clean.startsWith("audio/") -> "mp3"
|
||||
clean.startsWith("image/") -> "img"
|
||||
else -> "bin"
|
||||
}
|
||||
}
|
||||
}?.let { return it.key }
|
||||
return when {
|
||||
clean.startsWith("video/") -> "mp4"
|
||||
clean.startsWith("audio/") -> "mp3"
|
||||
clean.startsWith("image/") -> "img"
|
||||
else -> "bin"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,11 @@ class ImageBytesResolverTest {
|
||||
assertEquals("heic", ImageBytesResolver.extensionForMime("image/heic"))
|
||||
assertEquals("svg", ImageBytesResolver.extensionForMime("image/svg+xml"))
|
||||
assertEquals("img", ImageBytesResolver.extensionForMime("image/unknown"))
|
||||
assertEquals("heic", ImageBytesResolver.extensionForMime("image/heif"))
|
||||
assertEquals("bmp", ImageBytesResolver.extensionForMime("image/bmp"))
|
||||
// Non-image content types stay `.img`, not `.bin`/`.pdf`.
|
||||
assertEquals("img", ImageBytesResolver.extensionForMime("application/pdf"))
|
||||
assertEquals("img", ImageBytesResolver.extensionForMime("text/html"))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -30,6 +30,30 @@ class MediaTypesTest {
|
||||
assertEquals("application/octet-stream", mediaMimeForPath("/tmp/x.weird"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extension for mime uses one table plus aliases`() {
|
||||
// Table entries, including params and case.
|
||||
assertEquals("png", extensionForMime("image/png"))
|
||||
assertEquals("png", extensionForMime(" IMAGE/PNG; charset=binary"))
|
||||
assertEquals("ogg", extensionForMime("audio/ogg; codecs=opus"))
|
||||
assertEquals("mkv", extensionForMime("video/x-matroska"))
|
||||
assertEquals("pdf", extensionForMime("application/pdf"))
|
||||
// Aliases win over first-table-match.
|
||||
assertEquals("jpg", extensionForMime("image/jpeg"))
|
||||
assertEquals("jpg", extensionForMime("image/jpg"))
|
||||
assertEquals("heic", extensionForMime("image/heif"))
|
||||
assertEquals("mp3", extensionForMime("audio/mp3"))
|
||||
assertEquals("m4a", extensionForMime("audio/x-m4a"))
|
||||
assertEquals("wav", extensionForMime("audio/wave"))
|
||||
assertEquals("flac", extensionForMime("application/x-flac"))
|
||||
assertEquals("ogg", extensionForMime("application/ogg"))
|
||||
// Family fallbacks.
|
||||
assertEquals("mp4", extensionForMime("video/unknown"))
|
||||
assertEquals("mp3", extensionForMime("audio/unknown"))
|
||||
assertEquals("img", extensionForMime("image/unknown"))
|
||||
assertEquals("bin", extensionForMime("application/unknown"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `name extraction handles urls and paths`() {
|
||||
assertEquals("report.pdf", mediaNameFromPath("https://gw/api/files/download?path=%2Ftmp%2Freport.pdf"))
|
||||
|
||||
Reference in new issue
Block a user