code simplification and deduplication
This commit is contained in:
+52
-69
@@ -50,64 +50,54 @@ class ImageDownloader {
|
|||||||
* Stream download and calculate hash for verification without loading entire file into memory.
|
* Stream download and calculate hash for verification without loading entire file into memory.
|
||||||
* This is memory-efficient for large files (videos, high-res images, etc.)
|
* This is memory-efficient for large files (videos, high-res images, etc.)
|
||||||
*/
|
*/
|
||||||
suspend fun waitAndVerifyStream(
|
private suspend fun <T> retryWithDelay(
|
||||||
imageUrl: String,
|
maxAttempts: Int = 15,
|
||||||
okHttpClient: (url: String) -> OkHttpClient,
|
delayMs: Long = 1000,
|
||||||
): StreamVerification? =
|
operation: suspend () -> T?,
|
||||||
|
): T? =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
var verification: StreamVerification? = null
|
var result: T? = null
|
||||||
var tentatives = 0
|
var tentatives = 0
|
||||||
|
|
||||||
// Servers are usually not ready, so tries to download it for 15 times/seconds.
|
// Servers are usually not ready, so tries to download it for 15 times/seconds.
|
||||||
while (verification == null && tentatives < 15) {
|
while (result == null && tentatives < maxAttempts) {
|
||||||
verification =
|
result =
|
||||||
try {
|
try {
|
||||||
tryStreamAndVerify(imageUrl, okHttpClient)
|
operation()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verification == null) {
|
if (result == null) {
|
||||||
tentatives++
|
tentatives++
|
||||||
delay(1000)
|
delay(delayMs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return@withContext verification
|
return@withContext result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun waitAndVerifyStream(
|
||||||
|
imageUrl: String,
|
||||||
|
okHttpClient: (url: String) -> OkHttpClient,
|
||||||
|
): StreamVerification? = retryWithDelay { tryStreamAndVerify(imageUrl, okHttpClient) }
|
||||||
|
|
||||||
suspend fun waitAndGetImage(
|
suspend fun waitAndGetImage(
|
||||||
imageUrl: String,
|
imageUrl: String,
|
||||||
okHttpClient: (url: String) -> OkHttpClient,
|
okHttpClient: (url: String) -> OkHttpClient,
|
||||||
): Blob? =
|
): Blob? = retryWithDelay { tryGetTheImage(imageUrl, okHttpClient) }
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
var imageData: Blob? = null
|
|
||||||
var tentatives = 0
|
|
||||||
|
|
||||||
// Servers are usually not ready, so tries to download it for 15 times/seconds.
|
private data class HttpConnection(
|
||||||
while (imageData == null && tentatives < 15) {
|
val connection: HttpURLConnection,
|
||||||
imageData =
|
val responseCode: Int,
|
||||||
try {
|
val contentType: String?,
|
||||||
tryGetTheImage(imageUrl, okHttpClient)
|
)
|
||||||
} catch (e: Exception) {
|
|
||||||
if (e is CancellationException) throw e
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageData == null) {
|
private suspend fun openHttpConnection(
|
||||||
tentatives++
|
|
||||||
delay(1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return@withContext imageData
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun tryStreamAndVerify(
|
|
||||||
imageUrl: String,
|
imageUrl: String,
|
||||||
okHttpClient: (url: String) -> OkHttpClient,
|
okHttpClient: (url: String) -> OkHttpClient,
|
||||||
): StreamVerification? =
|
): HttpConnection =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
// TODO: Migrate to OkHttp
|
// TODO: Migrate to OkHttp
|
||||||
HttpURLConnection.setFollowRedirects(true)
|
HttpURLConnection.setFollowRedirects(true)
|
||||||
@@ -122,6 +112,7 @@ class ImageDownloader {
|
|||||||
huc.instanceFollowRedirects = true
|
huc.instanceFollowRedirects = true
|
||||||
var responseCode = huc.responseCode
|
var responseCode = huc.responseCode
|
||||||
|
|
||||||
|
// Handle redirects
|
||||||
if (responseCode in 300..400) {
|
if (responseCode in 300..400) {
|
||||||
val newUrl: String = huc.getHeaderField("Location")
|
val newUrl: String = huc.getHeaderField("Location")
|
||||||
|
|
||||||
@@ -137,24 +128,38 @@ class ImageDownloader {
|
|||||||
responseCode = huc.responseCode
|
responseCode = huc.responseCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return@withContext HttpConnection(
|
||||||
|
connection = huc,
|
||||||
|
responseCode = responseCode,
|
||||||
|
contentType = huc.headerFields.get("Content-Type")?.firstOrNull(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun tryStreamAndVerify(
|
||||||
|
imageUrl: String,
|
||||||
|
okHttpClient: (url: String) -> OkHttpClient,
|
||||||
|
): StreamVerification? =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val httpConn = openHttpConnection(imageUrl, okHttpClient)
|
||||||
|
|
||||||
return@withContext try {
|
return@withContext try {
|
||||||
if (responseCode in 200..300) {
|
if (httpConn.responseCode in 200..300) {
|
||||||
val (hash, totalBytes) =
|
val (hash, totalBytes) =
|
||||||
huc.inputStream.use {
|
httpConn.connection.inputStream.use {
|
||||||
sha256StreamWithCount(it)
|
sha256StreamWithCount(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamVerification(
|
StreamVerification(
|
||||||
hash = hash.toHexKey(),
|
hash = hash.toHexKey(),
|
||||||
size = totalBytes,
|
size = totalBytes,
|
||||||
contentType = huc.headerFields.get("Content-Type")?.firstOrNull(),
|
contentType = httpConn.contentType,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// Always disconnect to release connection resources
|
// Always disconnect to release connection resources
|
||||||
huc.disconnect()
|
httpConn.connection.disconnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,41 +168,19 @@ class ImageDownloader {
|
|||||||
okHttpClient: (url: String) -> OkHttpClient,
|
okHttpClient: (url: String) -> OkHttpClient,
|
||||||
): Blob? =
|
): Blob? =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
// TODO: Migrate to OkHttp
|
val httpConn = openHttpConnection(imageUrl, okHttpClient)
|
||||||
HttpURLConnection.setFollowRedirects(true)
|
|
||||||
var url = URL(imageUrl)
|
|
||||||
var clientProxy = okHttpClient(imageUrl).proxy
|
|
||||||
var huc =
|
|
||||||
if (clientProxy != null) {
|
|
||||||
url.openConnection(clientProxy) as HttpURLConnection
|
|
||||||
} else {
|
|
||||||
url.openConnection() as HttpURLConnection
|
|
||||||
}
|
|
||||||
huc.instanceFollowRedirects = true
|
|
||||||
var responseCode = huc.responseCode
|
|
||||||
|
|
||||||
if (responseCode in 300..400) {
|
return@withContext try {
|
||||||
val newUrl: String = huc.getHeaderField("Location")
|
if (httpConn.responseCode in 200..300) {
|
||||||
|
|
||||||
// open the new connnection again
|
|
||||||
url = URL(newUrl)
|
|
||||||
clientProxy = okHttpClient(newUrl).proxy
|
|
||||||
huc =
|
|
||||||
if (clientProxy != null) {
|
|
||||||
url.openConnection(clientProxy) as HttpURLConnection
|
|
||||||
} else {
|
|
||||||
url.openConnection() as HttpURLConnection
|
|
||||||
}
|
|
||||||
responseCode = huc.responseCode
|
|
||||||
}
|
|
||||||
|
|
||||||
return@withContext if (responseCode in 200..300) {
|
|
||||||
Blob(
|
Blob(
|
||||||
huc.inputStream.use { it.readBytes() },
|
httpConn.connection.inputStream.use { it.readBytes() },
|
||||||
huc.headerFields.get("Content-Type")?.firstOrNull(),
|
httpConn.contentType,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
httpConn.connection.disconnect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,4 +45,12 @@ data class MediaUploadResult(
|
|||||||
val ipfs: String? = null,
|
val ipfs: String? = null,
|
||||||
// blurhash value for previews
|
// blurhash value for previews
|
||||||
val blurHash: BlurhashWrapper? = null,
|
val blurHash: BlurhashWrapper? = null,
|
||||||
|
) {
|
||||||
|
fun mergeLocalMetadata(localMetadata: Pair<BlurhashWrapper?, DimensionTag?>?): MediaUploadResult =
|
||||||
|
localMetadata?.let { (blur, dim) ->
|
||||||
|
copy(
|
||||||
|
dimension = dim ?: dimension,
|
||||||
|
blurHash = blur ?: blurHash,
|
||||||
)
|
)
|
||||||
|
} ?: this
|
||||||
|
}
|
||||||
|
|||||||
+3
-10
@@ -97,8 +97,8 @@ class BlossomUploader {
|
|||||||
val imageInputStream = contentResolver.openInputStream(uri)
|
val imageInputStream = contentResolver.openInputStream(uri)
|
||||||
checkNotNull(imageInputStream) { "Can't open the image input stream" }
|
checkNotNull(imageInputStream) { "Can't open the image input stream" }
|
||||||
|
|
||||||
val serverResult =
|
return imageInputStream
|
||||||
imageInputStream.use { stream ->
|
.use { stream ->
|
||||||
upload(
|
upload(
|
||||||
stream,
|
stream,
|
||||||
hash,
|
hash,
|
||||||
@@ -112,14 +112,7 @@ class BlossomUploader {
|
|||||||
httpAuth,
|
httpAuth,
|
||||||
context,
|
context,
|
||||||
)
|
)
|
||||||
}
|
}.mergeLocalMetadata(localMetadata)
|
||||||
|
|
||||||
return localMetadata?.let { (blur, dim) ->
|
|
||||||
serverResult.copy(
|
|
||||||
dimension = dim ?: serverResult.dimension,
|
|
||||||
blurHash = blur ?: serverResult.blurHash,
|
|
||||||
)
|
|
||||||
} ?: serverResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodeAuth(event: BlossomAuthorizationEvent): String {
|
fun encodeAuth(event: BlossomAuthorizationEvent): String {
|
||||||
|
|||||||
+3
-10
@@ -112,8 +112,8 @@ class Nip96Uploader {
|
|||||||
|
|
||||||
checkNotNull(imageInputStream) { "Can't open the image input stream" }
|
checkNotNull(imageInputStream) { "Can't open the image input stream" }
|
||||||
|
|
||||||
val serverResult =
|
return imageInputStream
|
||||||
imageInputStream.use { stream ->
|
.use { stream ->
|
||||||
upload(
|
upload(
|
||||||
stream,
|
stream,
|
||||||
length,
|
length,
|
||||||
@@ -126,14 +126,7 @@ class Nip96Uploader {
|
|||||||
httpAuth,
|
httpAuth,
|
||||||
context,
|
context,
|
||||||
)
|
)
|
||||||
}
|
}.mergeLocalMetadata(localMetadata)
|
||||||
|
|
||||||
return localMetadata?.let { (blur, dim) ->
|
|
||||||
serverResult.copy(
|
|
||||||
dimension = dim ?: serverResult.dimension,
|
|
||||||
blurHash = blur ?: serverResult.blurHash,
|
|
||||||
)
|
|
||||||
} ?: serverResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun upload(
|
suspend fun upload(
|
||||||
|
|||||||
Reference in New Issue
Block a user