Add MediaSource.withCleanUrl method that removes invalid fragment data from MXC urls

We've seen some MXC urls in the wild having some `mxc://foo/bar#auto` fragment suffix, which is invalid, but the URL before that fragment part is valid and can be displayed
This commit is contained in:
Jorge Martín
2026-01-19 09:55:45 +01:00
parent 992a1133c9
commit 7fe0cc4d45
3 changed files with 36 additions and 7 deletions

View File

@@ -9,6 +9,7 @@
package io.element.android.libraries.matrix.api.media
import android.os.Parcelable
import androidx.core.net.toUri
import kotlinx.parcelize.Parcelize
@Parcelize
@@ -22,3 +23,25 @@ data class MediaSource(
*/
val json: String? = null,
) : Parcelable
/**
* Returns a new [MediaSource] with a valid URL.
*/
fun MediaSource.withCleanUrl(): MediaSource {
val uri = this.url.toUri()
if (uri.scheme != "mxc") return this
// We've seen some MXC urls in the wild having some `mxc://foo/bar#auto` fragment suffix, which is invalid
val cleanedUrl = buildString {
append(uri.scheme)
if (!this.endsWith("://")) {
append("://")
}
append(uri.host)
if (uri.path != null) {
append(uri.path)
}
}
return this.copy(url = cleanedUrl)
}

View File

@@ -14,6 +14,7 @@ import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.media.MediaFile
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.media.withCleanUrl
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.use
@@ -91,7 +92,7 @@ class RustMediaLoader(
return if (json != null) {
RustMediaSource.fromJson(json)
} else {
RustMediaSource.fromUrl(url)
RustMediaSource.fromUrl(withCleanUrl().url)
}
}
}

View File

@@ -16,6 +16,7 @@ import coil3.fetch.SourceFetchResult
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.media.toFile
import io.element.android.libraries.matrix.api.media.withCleanUrl
import okio.Buffer
import okio.FileSystem
import okio.Path.Companion.toOkioPath
@@ -28,14 +29,18 @@ internal class CoilMediaFetcher(
) : Fetcher {
override suspend fun fetch(): FetchResult? {
val source = mediaData.source
if (source == null) {
Timber.e("MediaData source is null")
return null
val mediaSource = when {
source == null -> {
Timber.e("MediaData source is null")
return null
}
source.url.startsWith("mxc:") -> source.withCleanUrl()
else -> source
}
return when (val kind = mediaData.kind) {
is MediaRequestData.Kind.Content -> fetchContent(source)
is MediaRequestData.Kind.Thumbnail -> fetchThumbnail(source, kind)
is MediaRequestData.Kind.File -> fetchFile(source, kind)
is MediaRequestData.Kind.Content -> fetchContent(mediaSource)
is MediaRequestData.Kind.Thumbnail -> fetchThumbnail(mediaSource, kind)
is MediaRequestData.Kind.File -> fetchFile(mediaSource, kind)
}
}