Refactoring the nostr.build uploader

This commit is contained in:
Vitor Pamplona
2023-05-07 18:37:33 -04:00
parent eba14cf9f1
commit cb09a26ef5
5 changed files with 59 additions and 58 deletions
@@ -9,7 +9,6 @@ import junit.framework.TestCase.assertNotNull
import junit.framework.TestCase.fail import junit.framework.TestCase.fail
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.util.Base64 import java.util.Base64
@@ -21,12 +20,12 @@ class ImageUploadTesting {
@Test() @Test()
fun testImgurUpload() = runBlocking { fun testImgurUpload() = runBlocking {
val inputStream = Base64.getDecoder().decode(image).inputStream() val bytes = Base64.getDecoder().decode(image)
val inputStream = bytes.inputStream()
println("Uploading")
ImageUploader.uploadImage( ImageUploader.uploadImage(
inputStream, inputStream,
bytes.size.toLong(),
"image/gif", "image/gif",
ImgurServer(), ImgurServer(),
onSuccess = { url, contentType -> onSuccess = { url, contentType ->
@@ -43,14 +42,13 @@ class ImageUploadTesting {
} }
@Test() @Test()
@Ignore
fun testNostrBuildUpload() = runBlocking { fun testNostrBuildUpload() = runBlocking {
val inputStream = Base64.getDecoder().decode(image).inputStream() val bytes = Base64.getDecoder().decode(image)
val inputStream = bytes.inputStream()
println("Uploading")
ImageUploader.uploadImage( ImageUploader.uploadImage(
inputStream, inputStream,
bytes.size.toLong(),
"image/gif", "image/gif",
NostrBuildServer(), NostrBuildServer(),
onSuccess = { url, contentType -> onSuccess = { url, contentType ->
@@ -68,12 +66,12 @@ class ImageUploadTesting {
@Test() @Test()
fun testNostrImgUpload() = runBlocking { fun testNostrImgUpload() = runBlocking {
val inputStream = Base64.getDecoder().decode(image).inputStream() val bytes = Base64.getDecoder().decode(image)
val inputStream = bytes.inputStream()
println("Uploading")
ImageUploader.uploadImage( ImageUploader.uploadImage(
inputStream, inputStream,
bytes.size.toLong(),
"image/gif", "image/gif",
NostrImgServer(), NostrImgServer(),
onSuccess = { url, contentType -> onSuccess = { url, contentType ->
@@ -10,7 +10,6 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.vitorpamplona.amethyst.BuildConfig import com.vitorpamplona.amethyst.BuildConfig
import okhttp3.* import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.asRequestBody
import okio.BufferedSink import okio.BufferedSink
import okio.source import okio.source
import java.io.File import java.io.File
@@ -27,13 +26,19 @@ object ImageUploader {
fun uploadImage( fun uploadImage(
uri: Uri, uri: Uri,
server: ServersAvailable, server: ServersAvailable,
context: Context,
contentResolver: ContentResolver, contentResolver: ContentResolver,
onSuccess: (String, String?) -> Unit, onSuccess: (String, String?) -> Unit,
onError: (Throwable) -> Unit onError: (Throwable) -> Unit
) { ) {
val contentType = contentResolver.getType(uri) val contentType = contentResolver.getType(uri)
val imageInputStream = contentResolver.openInputStream(uri) val imageInputStream = contentResolver.openInputStream(uri)
val length = contentResolver.query(uri, null, null, null, null)?.use {
it.moveToFirst()
val sizeIndex = it.getColumnIndex(OpenableColumns.SIZE)
it.getLong(sizeIndex)
} ?: 0
checkNotNull(imageInputStream) { checkNotNull(imageInputStream) {
"Can't open the image input stream" "Can't open the image input stream"
} }
@@ -47,23 +52,17 @@ object ImageUploader {
ImgurServer() ImgurServer()
} }
val file = getRealPathFromURI(uri, context)?.let { File(it) } // create path from uri uploadImage(imageInputStream, length, contentType, myServer, onSuccess, onError)
if (file != null) {
uploadImage(file, imageInputStream, contentType, myServer, server, onSuccess, onError)
}
} }
fun uploadImage( fun uploadImage(
file: File,
inputStream: InputStream, inputStream: InputStream,
length: Long,
contentType: String?, contentType: String?,
server: FileServer, server: FileServer,
serverType: ServersAvailable,
onSuccess: (String, String?) -> Unit, onSuccess: (String, String?) -> Unit,
onError: (Throwable) -> Unit onError: (Throwable) -> Unit
) { ) {
val category = contentType?.toMediaType()?.toString()?.split("/")?.get(0) ?: "image"
val fileName = randomChars() val fileName = randomChars()
val extension = contentType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(it) } ?: "" val extension = contentType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(it) } ?: ""
@@ -71,35 +70,23 @@ object ImageUploader {
val requestBody: RequestBody val requestBody: RequestBody
val requestBuilder = Request.Builder() val requestBuilder = Request.Builder()
if (serverType == ServersAvailable.NOSTR_BUILD) { requestBody = MultipartBody.Builder()
requestBuilder.addHeader("Content-Type", "multipart/form-data") .setType(MultipartBody.FORM)
requestBody = MultipartBody.Builder() .addFormDataPart(
.setType(MultipartBody.FORM) server.inputParameterName(contentType),
.addFormDataPart( "$fileName.$extension",
"fileToUpload",
"$fileName.$extension",
file.asRequestBody(contentType?.toMediaType())
) object : RequestBody() {
.build() override fun contentType() = contentType?.toMediaType()
} else {
requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
category,
"$fileName.$extension",
object : RequestBody() { override fun contentLength() = length
override fun contentType(): MediaType? =
contentType?.toMediaType()
override fun writeTo(sink: BufferedSink) { override fun writeTo(sink: BufferedSink) {
inputStream.source().use(sink::writeAll) inputStream.source().use(sink::writeAll)
}
} }
) }
.build() )
} .build()
server.clientID()?.let { server.clientID()?.let {
requestBuilder.addHeader("Authorization", it) requestBuilder.addHeader("Authorization", it)
@@ -172,6 +159,7 @@ fun getRealPathFromURI(uri: Uri, context: Context): String? {
abstract class FileServer { abstract class FileServer {
abstract fun postUrl(contentType: String?): String abstract fun postUrl(contentType: String?): String
abstract fun parseUrlFromSucess(body: String): String? abstract fun parseUrlFromSucess(body: String): String?
abstract fun inputParameterName(contentType: String?): String
open fun clientID(): String? = null open fun clientID(): String? = null
} }
@@ -185,6 +173,10 @@ class NostrImgServer : FileServer() {
return url return url
} }
override fun inputParameterName(contentType: String?): String {
return contentType?.toMediaType()?.toString()?.split("/")?.get(0) ?: "image"
}
override fun clientID() = null override fun clientID() = null
} }
@@ -200,6 +192,10 @@ class ImgurServer : FileServer() {
return url return url
} }
override fun inputParameterName(contentType: String?): String {
return contentType?.toMediaType()?.toString()?.split("/")?.get(0) ?: "image"
}
override fun clientID() = "Client-ID e6aea87296f3f96" override fun clientID() = "Client-ID e6aea87296f3f96"
} }
@@ -210,5 +206,9 @@ class NostrBuildServer : FileServer() {
return url.toString().replace("\"", "") return url.toString().replace("\"", "")
} }
override fun inputParameterName(contentType: String?): String {
return "fileToUpload"
}
override fun clientID() = null override fun clientID() = null
} }
@@ -77,7 +77,6 @@ open class NewMediaModel : ViewModel() {
ImageUploader.uploadImage( ImageUploader.uploadImage(
uri = uri, uri = uri,
server = serverToUse, server = serverToUse,
context = context,
contentResolver = contentResolver, contentResolver = contentResolver,
onSuccess = { imageUrl, mimeType -> onSuccess = { imageUrl, mimeType ->
createNIP94Record(imageUrl, mimeType, description) createNIP94Record(imageUrl, mimeType, description)
@@ -115,18 +114,24 @@ open class NewMediaModel : ViewModel() {
uploadingDescription.value = "Server Processing" uploadingDescription.value = "Server Processing"
// Images don't seem to be ready immediately after upload // Images don't seem to be ready immediately after upload
if (mimeType?.startsWith("image/") == true) { var imageData: ByteArray? = null
delay(2000) var tentatives = 0
} else {
delay(15000) // 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
}
} }
uploadingDescription.value = "Downloading" uploadingDescription.value = "Downloading"
uploadingPercentage.value = 0.60f uploadingPercentage.value = 0.60f
try { if (imageData != null) {
val imageData = URL(imageUrl).readBytes()
uploadingPercentage.value = 0.80f uploadingPercentage.value = 0.80f
uploadingDescription.value = "Hashing" uploadingDescription.value = "Hashing"
@@ -154,8 +159,8 @@ open class NewMediaModel : ViewModel() {
} }
} }
) )
} catch (e: Exception) { } else {
Log.e("ImageDownload", "Couldn't download image from server: ${e.message}") Log.e("ImageDownload", "Couldn't download image from server")
cancel() cancel()
uploadingPercentage.value = 0.00f uploadingPercentage.value = 0.00f
uploadingDescription.value = null uploadingDescription.value = null
@@ -145,7 +145,6 @@ open class NewPostViewModel : ViewModel() {
ImageUploader.uploadImage( ImageUploader.uploadImage(
uri = it, uri = it,
server = server, server = server,
context = context,
contentResolver = contentResolver, contentResolver = contentResolver,
onSuccess = { imageUrl, mimeType -> onSuccess = { imageUrl, mimeType ->
if (server == ServersAvailable.IMGUR_NIP_94 || server == ServersAvailable.NOSTRIMG_NIP_94 || server == ServersAvailable.NOSTR_BUILD_NIP_94) { if (server == ServersAvailable.IMGUR_NIP_94 || server == ServersAvailable.NOSTRIMG_NIP_94 || server == ServersAvailable.NOSTR_BUILD_NIP_94) {
@@ -171,7 +171,6 @@ class NewUserMetadataViewModel : ViewModel() {
ImageUploader.uploadImage( ImageUploader.uploadImage(
uri = it, uri = it,
server = account.defaultFileServer, server = account.defaultFileServer,
context = context,
contentResolver = context.contentResolver, contentResolver = context.contentResolver,
onSuccess = { imageUrl, mimeType -> onSuccess = { imageUrl, mimeType ->
onUploading(false) onUploading(false)