diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/FileHeader.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/FileHeader.kt index 941aab60e..81a09ec10 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/FileHeader.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/FileHeader.kt @@ -4,6 +4,7 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.util.Log import com.vitorpamplona.amethyst.model.toHexKey +import com.vitorpamplona.amethyst.ui.actions.ImageDownloader import io.trbl.blurhash.BlurHash import java.net.URL import java.security.MessageDigest @@ -19,11 +20,15 @@ class FileHeader( val description: String? = null ) { companion object { - fun prepare(fileUrl: String, mimeType: String?, description: String?, onReady: (FileHeader) -> Unit, onError: () -> Unit) { + suspend fun prepare(fileUrl: String, mimeType: String?, description: String?, onReady: (FileHeader) -> Unit, onError: () -> Unit) { try { - val imageData = URL(fileUrl).readBytes() + val imageData: ByteArray? = ImageDownloader().waitAndGetImage(fileUrl) - prepare(imageData, fileUrl, mimeType, description, onReady, onError) + if (imageData != null) + prepare(imageData, fileUrl, mimeType, description, onReady, onError) + else { + onError() + } } catch (e: Exception) { Log.e("ImageDownload", "Couldn't download image from server: ${e.message}") onError() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/ImageDownloader.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/ImageDownloader.kt new file mode 100644 index 000000000..c2bd13abf --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/ImageDownloader.kt @@ -0,0 +1,47 @@ +package com.vitorpamplona.amethyst.ui.actions + +import kotlinx.coroutines.delay +import java.net.HttpURLConnection +import java.net.URL + +class ImageDownloader { + suspend fun waitAndGetImage(imageUrl: String): ByteArray? { + var imageData: ByteArray? = null + var tentatives = 0 + + // Servers are usually not ready.. so tries to download it for 15 times/seconds. + while (imageData == null && tentatives < 15) { + imageData = try { + HttpURLConnection.setFollowRedirects(true) + var url = URL(imageUrl) + var huc = url.openConnection() as HttpURLConnection + huc.instanceFollowRedirects = true + var responseCode = huc.responseCode + + if (responseCode in 300..400) { + val newUrl: String = huc.getHeaderField("Location") + + // open the new connnection again + url = URL(newUrl) + huc = url.openConnection() as HttpURLConnection + responseCode = huc.responseCode + } + + if (responseCode in 200..300) { + huc.inputStream.use { it.readBytes() } + } else { + tentatives++ + delay(1000) + + null + } + } catch (e: Exception) { + tentatives++ + delay(1000) + null + } + } + + return imageData + } +} \ No newline at end of file diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/ImageUploader.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/ImageUploader.kt index b85bfaec0..52a83020f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/ImageUploader.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/ImageUploader.kt @@ -38,16 +38,22 @@ object ImageUploader { checkNotNull(imageInputStream) { "Can't open the image input stream" } - val myServer = if (server == ServersAvailable.IMGUR) { - ImgurServer() - } else if (server == ServersAvailable.NOSTRIMG) { - NostrImgServer() - } else if (server == ServersAvailable.NOSTR_BUILD) { - NostrBuildServer() - } else if (server == ServersAvailable.NOSTRFILES_DEV) { - NostrFilesDevServer() - } else { - ImgurServer() + val myServer = when (server) { + ServersAvailable.IMGUR, ServersAvailable.IMGUR_NIP_94 -> { + ImgurServer() + } + ServersAvailable.NOSTRIMG, ServersAvailable.NOSTRIMG_NIP_94 -> { + NostrImgServer() + } + ServersAvailable.NOSTR_BUILD, ServersAvailable.NOSTR_BUILD_NIP_94 -> { + NostrBuildServer() + } + ServersAvailable.NOSTRFILES_DEV, ServersAvailable.NOSTRFILES_DEV_NIP_94 -> { + NostrFilesDevServer() + } + else -> { + ImgurServer() + } } uploadImage(imageInputStream, length, contentType, myServer, onSuccess, onError) @@ -101,7 +107,7 @@ object ImageUploader { try { check(response.isSuccessful) response.body.use { body -> - val url = server.parseUrlFromSucess(body.string()) + val url = server.parseUrlFromSuccess(body.string()) checkNotNull(url) { "There must be an uploaded image URL in the response" } @@ -124,7 +130,7 @@ object ImageUploader { abstract class FileServer { abstract fun postUrl(contentType: String?): String - abstract fun parseUrlFromSucess(body: String): String? + abstract fun parseUrlFromSuccess(body: String): String? abstract fun inputParameterName(contentType: String?): String open fun clientID(): String? = null @@ -133,7 +139,7 @@ abstract class FileServer { class NostrImgServer : FileServer() { override fun postUrl(contentType: String?) = "https://nostrimg.com/api/upload" - override fun parseUrlFromSucess(body: String): String? { + override fun parseUrlFromSuccess(body: String): String? { val tree = jacksonObjectMapper().readTree(body) val url = tree?.get("data")?.get("link")?.asText() return url @@ -152,7 +158,7 @@ class ImgurServer : FileServer() { return if (category == "image") "https://api.imgur.com/3/image" else "https://api.imgur.com/3/upload" } - override fun parseUrlFromSucess(body: String): String? { + override fun parseUrlFromSuccess(body: String): String? { val tree = jacksonObjectMapper().readTree(body) val url = tree?.get("data")?.get("link")?.asText() return url @@ -167,7 +173,7 @@ class ImgurServer : FileServer() { class NostrBuildServer : FileServer() { override fun postUrl(contentType: String?) = "https://nostr.build/api/upload/android.php" - override fun parseUrlFromSucess(body: String): String? { + override fun parseUrlFromSuccess(body: String): String? { val url = jacksonObjectMapper().readTree(body) // return url.toString() return url.toString().replace("\"", "") } @@ -181,10 +187,9 @@ class NostrBuildServer : FileServer() { class NostrFilesDevServer : FileServer() { override fun postUrl(contentType: String?) = "https://nostrfiles.dev/upload_image" - override fun parseUrlFromSucess(body: String): String? { + override fun parseUrlFromSuccess(body: String): String? { val tree = jacksonObjectMapper().readTree(body) - val url = tree?.get("url")?.asText() - return url + return tree?.get("url")?.asText() } override fun inputParameterName(contentType: String?): String { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt index 73703fe6b..5ee0705fa 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt @@ -14,8 +14,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.launch +import java.net.HttpURLConnection import java.net.URL + open class NewMediaModel : ViewModel() { var account: Account? = null @@ -116,19 +118,7 @@ open class NewMediaModel : ViewModel() { uploadingDescription.value = "Server Processing" // Images don't seem to be ready immediately after upload - var imageData: ByteArray? = null - var tentatives = 0 - - // Servers are usually not ready.. so tries to download it for 15 times/seconds. - while (imageData == null && tentatives < 15) { - imageData = try { - URL(imageUrl).readBytes() - } catch (e: Exception) { - tentatives++ - delay(1000) - null - } - } + val imageData: ByteArray? = ImageDownloader().waitAndGetImage(imageUrl) uploadingDescription.value = "Downloading" uploadingPercentage.value = 0.60f diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt index cb01b6e80..0d3c4ae74 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt @@ -110,7 +110,10 @@ fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, ac } fun isNIP94Server(selectedServer: ServersAvailable?): Boolean { - return selectedServer == ServersAvailable.NOSTRIMG_NIP_94 || selectedServer == ServersAvailable.IMGUR_NIP_94 || selectedServer == ServersAvailable.NOSTR_BUILD_NIP_94 || selectedServer == ServersAvailable.NOSTRFILES_DEV_NIP_94 + return selectedServer == ServersAvailable.NOSTRIMG_NIP_94 + || selectedServer == ServersAvailable.IMGUR_NIP_94 + || selectedServer == ServersAvailable.NOSTR_BUILD_NIP_94 + || selectedServer == ServersAvailable.NOSTRFILES_DEV_NIP_94 } @Composable diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt index 571565a3b..620e393c5 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt @@ -614,11 +614,11 @@ fun ImageVideoDescription( Triple(ServersAvailable.IMGUR, stringResource(id = R.string.upload_server_imgur), stringResource(id = R.string.upload_server_imgur_explainer)), Triple(ServersAvailable.NOSTRIMG, stringResource(id = R.string.upload_server_nostrimg), stringResource(id = R.string.upload_server_nostrimg_explainer)), Triple(ServersAvailable.NOSTR_BUILD, stringResource(id = R.string.upload_server_nostrbuild), stringResource(id = R.string.upload_server_nostrbuild_explainer)), - // Triple(ServersAvailable.NOSTRFILES_DEV, stringResource(id = R.string.upload_server_nostrfilesdev), stringResource(id = R.string.upload_server_nostrfilesdev_explainer)), + Triple(ServersAvailable.NOSTRFILES_DEV, stringResource(id = R.string.upload_server_nostrfilesdev), stringResource(id = R.string.upload_server_nostrfilesdev_explainer)), Triple(ServersAvailable.IMGUR_NIP_94, stringResource(id = R.string.upload_server_imgur_nip94), stringResource(id = R.string.upload_server_imgur_nip94_explainer)), Triple(ServersAvailable.NOSTRIMG_NIP_94, stringResource(id = R.string.upload_server_nostrimg_nip94), stringResource(id = R.string.upload_server_nostrimg_nip94_explainer)), Triple(ServersAvailable.NOSTR_BUILD_NIP_94, stringResource(id = R.string.upload_server_nostrbuild_nip94), stringResource(id = R.string.upload_server_nostrbuild_nip94_explainer)), - // Triple(ServersAvailable.NOSTRFILES_DEV_NIP_94, stringResource(id = R.string.upload_server_nostrfilesdev_nip94), stringResource(id = R.string.upload_server_nostrfilesdev_nip94_explainer)), + Triple(ServersAvailable.NOSTRFILES_DEV_NIP_94, stringResource(id = R.string.upload_server_nostrfilesdev_nip94), stringResource(id = R.string.upload_server_nostrfilesdev_nip94_explainer)), Triple(ServersAvailable.NIP95, stringResource(id = R.string.upload_server_relays_nip95), stringResource(id = R.string.upload_server_relays_nip95_explainer)) ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt index ded3776fb..8899b7beb 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt @@ -287,13 +287,6 @@ open class NewPostViewModel : ViewModel() { fun createNIP94Record(imageUrl: String, mimeType: String?, description: String) { viewModelScope.launch(Dispatchers.IO) { // Images don't seem to be ready immediately after upload - - if (mimeType?.startsWith("image/") == true) { - delay(2000) - } else { - delay(5000) - } - FileHeader.prepare( imageUrl, mimeType, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt index fec774000..5c34d567f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt @@ -259,7 +259,6 @@ private fun UrlImageView( content: ZoomableUrlImage, mainImageModifier: Modifier ) { - println("UrlImageView") val scope = rememberCoroutineScope() val context = LocalContext.current