From 306ce9a36ceddeb5ea8414bb553ecb477c16051f Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Sun, 5 Jan 2025 12:14:28 -0500 Subject: [PATCH] Adds iMeta tags to GIF urls --- .../amethyst/ImageUploadTesting.kt | 4 +-- .../amethyst/service/uploads/FileHeader.kt | 4 +-- .../service/uploads/ImageDownloader.kt | 16 +++++++++--- .../service/uploads/UploadOrchestrator.kt | 6 ++--- .../amethyst/ui/actions/NewPostViewModel.kt | 26 +++++++++++++++++++ 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt index ec8ca4da5..90f60eef8 100644 --- a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt +++ b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt @@ -105,7 +105,7 @@ class ImageUploadTesting { assertEquals("${server.baseUrl}/$initialHash", result.url?.removeSuffix(".png")) val imageData: ByteArray = - ImageDownloader().waitAndGetImage(result.url!!, false) + ImageDownloader().waitAndGetImage(result.url!!, false)?.bytes ?: run { fail("${server.name}: Should not be null") return @@ -148,7 +148,7 @@ class ImageUploadTesting { Assert.assertTrue("${server.name}: Invalid result url", url.startsWith("http")) val imageData: ByteArray = - ImageDownloader().waitAndGetImage(url, false) + ImageDownloader().waitAndGetImage(url, false)?.bytes ?: run { fail("${server.name}: Should not be null") return diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/FileHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/FileHeader.kt index 2564574f7..cf0417942 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/FileHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/FileHeader.kt @@ -55,10 +55,10 @@ class FileHeader( forceProxy: Boolean, ): Result = try { - val imageData: ByteArray? = ImageDownloader().waitAndGetImage(fileUrl, forceProxy) + val imageData: ImageDownloader.Blob? = ImageDownloader().waitAndGetImage(fileUrl, forceProxy) if (imageData != null) { - prepare(imageData, mimeType, dimPrecomputed) + prepare(imageData.bytes, mimeType ?: imageData.contentType, dimPrecomputed) } else { Result.failure(UnableToDownload(fileUrl)) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/ImageDownloader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/ImageDownloader.kt index 7645ba560..065d2178d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/ImageDownloader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/ImageDownloader.kt @@ -29,12 +29,17 @@ import java.net.HttpURLConnection import java.net.URL class ImageDownloader { + class Blob( + val bytes: ByteArray, + val contentType: String?, + ) + suspend fun waitAndGetImage( imageUrl: String, forceProxy: Boolean, - ): ByteArray? = + ): Blob? = withContext(Dispatchers.IO) { - var imageData: ByteArray? = null + var imageData: Blob? = null var tentatives = 0 // Servers are usually not ready.. so tries to download it for 15 times/seconds. @@ -59,7 +64,7 @@ class ImageDownloader { private suspend fun tryGetTheImage( imageUrl: String, forceProxy: Boolean, - ): ByteArray? = + ): Blob? = withContext(Dispatchers.IO) { // TODO: Migrate to OkHttp HttpURLConnection.setFollowRedirects(true) @@ -88,7 +93,10 @@ class ImageDownloader { } return@withContext if (responseCode in 200..300) { - huc.inputStream.use { it.readBytes() } + Blob( + huc.inputStream.use { it.readBytes() }, + huc.headerFields.get("Content-Type")?.firstOrNull(), + ) } else { null } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/UploadOrchestrator.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/UploadOrchestrator.kt index 958e75f13..ba663d559 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/UploadOrchestrator.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/UploadOrchestrator.kt @@ -224,15 +224,15 @@ class UploadOrchestrator { updateState(0.6, UploadingState.Downloading) - val imageData: ByteArray? = ImageDownloader().waitAndGetImage(uploadResult.url, forceProxy(uploadResult.url)) + val imageData: ImageDownloader.Blob? = ImageDownloader().waitAndGetImage(uploadResult.url, forceProxy(uploadResult.url)) if (imageData != null) { updateState(0.8, UploadingState.Hashing) val result = FileHeader.prepare( - imageData, - uploadResult.type ?: localContentType, + imageData.bytes, + uploadResult.type ?: localContentType ?: imageData.contentType, uploadResult.dimension, ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt index f9cf51ea6..87f7cc44d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt @@ -20,8 +20,10 @@ */ package com.vitorpamplona.amethyst.ui.actions +import android.R.attr.mimeType import android.content.Context import android.util.Log +import android.webkit.MimeTypeMap import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf @@ -43,6 +45,7 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.LocationState import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource +import com.vitorpamplona.amethyst.service.uploads.FileHeader import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator @@ -92,6 +95,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.util.Locale import java.util.UUID import kotlin.math.round @@ -1228,6 +1232,28 @@ open class NewPostViewModel : ViewModel() { val lastWordStart = it.end - lastWord.length val wordToInsert = item.url.url + " " + viewModelScope.launch(Dispatchers.IO) { + val fileExtension: String = MimeTypeMap.getFileExtensionFromUrl(item.url.url) + val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension.lowercase(Locale.getDefault())) + + val forceProxy = accountViewModel?.account?.shouldUseTorForImageDownload() ?: false + val imeta = + FileHeader.prepare(item.url.url, mimeType, null, forceProxy).getOrNull()?.let { + IMetaTagBuilder(item.url.url) + .apply { + hash(it.hash) + size(it.size) + it.mimeType?.let { mimeType(it) } + it.dim?.let { dims(it) } + it.blurHash?.let { blurhash(it.blurhash) } + }.build() + } + + if (imeta != null) { + iMetaAttachments += imeta + } + } + message = TextFieldValue( message.text.replaceRange(lastWordStart, it.end, wordToInsert),