mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 08:47:33 +00:00
Merge pull request #3638 from vitorpamplona/claude/post-image-render-shift-b4vx28
Fix media detection for malformed imeta MIME types
This commit is contained in:
+14
-4
@@ -60,9 +60,9 @@ class RichTextParser {
|
||||
|
||||
val contentType = frags[MimeTypeTag.TAG_NAME] ?: tags[MimeTypeTag.TAG_NAME]?.firstOrNull()
|
||||
|
||||
val isImage: Boolean
|
||||
val isVideo: Boolean
|
||||
val isPdf: Boolean
|
||||
var isImage = false
|
||||
var isVideo = false
|
||||
var isPdf = false
|
||||
|
||||
if (contentType != null) {
|
||||
isImage = contentType.startsWith("image/")
|
||||
@@ -77,7 +77,17 @@ class RichTextParser {
|
||||
isImage = fullUrl.startsWith("data:image/")
|
||||
isVideo = fullUrl.startsWith("data:video/") || fullUrl.startsWith("data:audio/")
|
||||
isPdf = fullUrl.startsWith("data:application/pdf")
|
||||
} else {
|
||||
}
|
||||
|
||||
// Fall back to file-extension detection when the type is still unknown. This covers both
|
||||
// the no-MIME case and a *malformed* imeta MIME — e.g. Primal iOS emits `m jpeg` instead
|
||||
// of `m image/jpeg`, which matches none of the `startsWith` prefixes above. Without this
|
||||
// fallback such a URL returns null and drops to a plain link: that discards the imeta
|
||||
// `dim`/blurhash (so the loading placeholder can't reserve the image's height and the
|
||||
// feed jumps once the bitmap arrives) and forces a needless URL-preview network
|
||||
// round-trip just to rediscover the type the imeta already declared. `data:` URIs carry
|
||||
// their type in the prefix, so a miss there is genuine — don't extension-probe them.
|
||||
if (!isImage && !isVideo && !isPdf && !fullUrl.startsWith("data:")) {
|
||||
val removedParamsFromUrl = removeQueryParamsForExtensionComparison(fullUrl)
|
||||
isImage = imageExtensions.any { removedParamsFromUrl.endsWith(it) }
|
||||
isVideo = videoExtensions.any { removedParamsFromUrl.endsWith(it) }
|
||||
|
||||
+62
@@ -66,4 +66,66 @@ class PdfParserTest {
|
||||
assertTrue(RichTextParser.isPdfUrl("https://example.com/doc.PDF"))
|
||||
assertTrue(RichTextParser.isPdfUrl("https://example.com/doc.pdf?sig=abc"))
|
||||
}
|
||||
|
||||
// Primal iOS writes a bare subtype (`m jpeg`) instead of a full MIME (`m image/jpeg`).
|
||||
// The bare subtype matches none of the `image/`/`video/`/`application/pdf` prefixes, so before
|
||||
// the extension fallback the whole imeta was dropped: the URL rendered as a plain link, losing
|
||||
// the `dim` needed to reserve the image's height (feed jump) and forcing a URL-preview fetch.
|
||||
// The `.jpg` extension must still route it to a MediaUrlImage that carries `dim`.
|
||||
@Test
|
||||
fun detectsImageFromMalformedImetaMimeWithImageExtension() {
|
||||
val url = "https://blossom.primal.net/33e7c01afbea894a64e1db44dece460b09a2426108f47143754e1cf4bfdf747c.jpg"
|
||||
val content = "\n$url"
|
||||
val tags =
|
||||
ImmutableListOfLists(
|
||||
arrayOf(
|
||||
arrayOf("imeta", "url $url", "m jpeg", "dim 1009.0x680.0"),
|
||||
arrayOf("client", "Primal iOS"),
|
||||
),
|
||||
)
|
||||
|
||||
val state = RichTextParser().parseText(content, tags, null)
|
||||
|
||||
val imageMedia = state.mediaForPager[url]
|
||||
assertTrue(imageMedia is MediaUrlImage, "Expected MediaUrlImage despite the malformed `m jpeg` mime")
|
||||
assertEquals("1009x680", imageMedia.dim?.toString(), "The imeta dim must survive so the loader can reserve space")
|
||||
assertEquals(1009f / 680f, imageMedia.dim?.aspectRatio())
|
||||
|
||||
val segment = state.paragraphs.flatMap { it.words }.first { it.segmentText == url }
|
||||
assertTrue(segment is ImageSegment, "Expected ImageSegment, got ${segment::class.simpleName}")
|
||||
}
|
||||
|
||||
// A malformed video subtype (`m mp4`) must likewise fall back to the `.mp4` extension.
|
||||
@Test
|
||||
fun detectsVideoFromMalformedImetaMimeWithVideoExtension() {
|
||||
val url = "https://cdn.example.com/clip.mp4"
|
||||
val tags =
|
||||
ImmutableListOfLists(
|
||||
arrayOf(
|
||||
arrayOf("imeta", "url $url", "m mp4"),
|
||||
),
|
||||
)
|
||||
|
||||
val state = RichTextParser().parseText(url, tags, null)
|
||||
|
||||
val videoMedia = state.mediaForPager[url]
|
||||
assertTrue(videoMedia is MediaUrlVideo, "Expected MediaUrlVideo despite the malformed `m mp4` mime")
|
||||
}
|
||||
|
||||
// A malformed mime on a URL with *no* recognizable extension can't be recovered — it stays a
|
||||
// link. This documents the boundary of the fallback so it isn't mistaken for a regression.
|
||||
@Test
|
||||
fun malformedImetaMimeWithoutExtensionStaysUnclassified() {
|
||||
val url = "https://files.example.com/abcd1234"
|
||||
val tags =
|
||||
ImmutableListOfLists(
|
||||
arrayOf(
|
||||
arrayOf("imeta", "url $url", "m jpeg"),
|
||||
),
|
||||
)
|
||||
|
||||
val state = RichTextParser().parseText(url, tags, null)
|
||||
|
||||
assertEquals(null, state.mediaForPager[url], "No extension to recover from -> not treated as media")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user