code simplification and deduplication
This commit is contained in:
+27
-18
@@ -31,10 +31,24 @@ import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
|
||||
object BlurhashMetadataCalculator {
|
||||
fun shouldAttempt(mimeType: String?): Boolean =
|
||||
mimeType?.let {
|
||||
it.startsWith("image/", ignoreCase = true) || it.startsWith("video/", ignoreCase = true)
|
||||
} ?: false
|
||||
private fun isImage(mimeType: String?) = mimeType?.startsWith("image/", ignoreCase = true) == true
|
||||
|
||||
private fun isVideo(mimeType: String?) = mimeType?.startsWith("video/", ignoreCase = true) == true
|
||||
|
||||
fun shouldAttempt(mimeType: String?): Boolean = isImage(mimeType) || isVideo(mimeType)
|
||||
|
||||
private fun createBitmapOptions() =
|
||||
BitmapFactory.Options().apply {
|
||||
inPreferredConfig = Bitmap.Config.ARGB_8888
|
||||
}
|
||||
|
||||
private fun processImage(
|
||||
bitmap: Bitmap?,
|
||||
dimPrecomputed: DimensionTag?,
|
||||
): Pair<BlurhashWrapper?, DimensionTag?> {
|
||||
val (blur, dim) = processBitmap(bitmap)
|
||||
return blur to (dim ?: dimPrecomputed)
|
||||
}
|
||||
|
||||
fun computeFromBytes(
|
||||
data: ByteArray,
|
||||
@@ -42,14 +56,11 @@ object BlurhashMetadataCalculator {
|
||||
dimPrecomputed: DimensionTag?,
|
||||
): Pair<BlurhashWrapper?, DimensionTag?> =
|
||||
when {
|
||||
mimeType?.startsWith("image/", ignoreCase = true) == true -> {
|
||||
val options = BitmapFactory.Options()
|
||||
options.inPreferredConfig = Bitmap.Config.ARGB_8888
|
||||
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
||||
val (blur, dim) = processBitmap(bitmap)
|
||||
blur to (dim ?: dimPrecomputed)
|
||||
isImage(mimeType) -> {
|
||||
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size, createBitmapOptions())
|
||||
processImage(bitmap, dimPrecomputed)
|
||||
}
|
||||
mimeType?.startsWith("video/", ignoreCase = true) == true -> {
|
||||
isVideo(mimeType) -> {
|
||||
val retriever = MediaMetadataRetriever()
|
||||
try {
|
||||
retriever.setDataSource(ByteArrayMediaDataSource(data))
|
||||
@@ -71,16 +82,13 @@ object BlurhashMetadataCalculator {
|
||||
|
||||
return try {
|
||||
when {
|
||||
mimeType?.startsWith("image/", ignoreCase = true) == true ->
|
||||
isImage(mimeType) ->
|
||||
context.contentResolver.openInputStream(uri)?.use { stream ->
|
||||
val options = BitmapFactory.Options()
|
||||
options.inPreferredConfig = Bitmap.Config.ARGB_8888
|
||||
val bitmap = BitmapFactory.decodeStream(stream, null, options)
|
||||
val (blur, dim) = processBitmap(bitmap)
|
||||
blur to (dim ?: dimPrecomputed)
|
||||
val bitmap = BitmapFactory.decodeStream(stream, null, createBitmapOptions())
|
||||
processImage(bitmap, dimPrecomputed)
|
||||
} ?: (null to dimPrecomputed)
|
||||
|
||||
mimeType?.startsWith("video/", ignoreCase = true) == true -> {
|
||||
isVideo(mimeType) -> {
|
||||
val retriever = MediaMetadataRetriever()
|
||||
try {
|
||||
retriever.setDataSource(context, uri)
|
||||
@@ -89,6 +97,7 @@ object BlurhashMetadataCalculator {
|
||||
retriever.release()
|
||||
}
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
+24
-26
@@ -232,35 +232,33 @@ class UploadOrchestrator {
|
||||
updateState(0.6, UploadingState.Downloading)
|
||||
|
||||
// Use streaming verification for memory efficiency with large files
|
||||
val verification: ImageDownloader.StreamVerification? = ImageDownloader().waitAndVerifyStream(uploadResult.url, okHttpClient)
|
||||
val verification =
|
||||
ImageDownloader().waitAndVerifyStream(uploadResult.url, okHttpClient)
|
||||
?: return error(R.string.could_not_download_from_the_server)
|
||||
|
||||
if (verification != null) {
|
||||
updateState(0.8, UploadingState.Hashing)
|
||||
updateState(0.8, UploadingState.Hashing)
|
||||
|
||||
// Create FileHeader with hash from streaming verification
|
||||
// Note: We skip blurhash/dimensions since we already have them from upload
|
||||
val fileHeader =
|
||||
FileHeader(
|
||||
mimeType = uploadResult.type ?: localContentType ?: verification.contentType,
|
||||
hash = verification.hash,
|
||||
size = verification.size.toInt(),
|
||||
dim = uploadResult.dimension,
|
||||
blurHash = uploadResult.blurHash,
|
||||
)
|
||||
|
||||
return finish(
|
||||
OrchestratorResult.ServerResult(
|
||||
fileHeader,
|
||||
uploadResult.url,
|
||||
uploadResult.magnet,
|
||||
uploadResult.sha256,
|
||||
originalContentType,
|
||||
originalHash,
|
||||
),
|
||||
// Create FileHeader with hash from streaming verification
|
||||
// Note: We skip blurhash/dimensions since we already have them from upload
|
||||
val fileHeader =
|
||||
FileHeader(
|
||||
mimeType = uploadResult.type ?: localContentType ?: verification.contentType,
|
||||
hash = verification.hash,
|
||||
size = verification.size.toInt(),
|
||||
dim = uploadResult.dimension,
|
||||
blurHash = uploadResult.blurHash,
|
||||
)
|
||||
} else {
|
||||
return error(R.string.could_not_download_from_the_server)
|
||||
}
|
||||
|
||||
return finish(
|
||||
OrchestratorResult.ServerResult(
|
||||
fileHeader,
|
||||
uploadResult.url,
|
||||
uploadResult.magnet,
|
||||
uploadResult.sha256,
|
||||
originalContentType,
|
||||
originalHash,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
sealed class OrchestratorResult {
|
||||
|
||||
+6
-20
@@ -52,20 +52,6 @@ import java.io.InputStream
|
||||
import java.util.Base64
|
||||
|
||||
class BlossomUploader {
|
||||
data class StreamInfo(
|
||||
val hash: HexKey,
|
||||
val size: Long,
|
||||
)
|
||||
|
||||
/**
|
||||
* Calculate SHA256 hash and size of a file by streaming it in chunks
|
||||
* to avoid loading the entire file into memory.
|
||||
*/
|
||||
private fun calculateHashAndSize(inputStream: InputStream): StreamInfo {
|
||||
val (hash, size) = sha256StreamWithCount(inputStream)
|
||||
return StreamInfo(hash.toHexKey(), size)
|
||||
}
|
||||
|
||||
fun Context.getFileName(uri: Uri): String? =
|
||||
when (uri.scheme) {
|
||||
ContentResolver.SCHEME_CONTENT -> getContentFileName(uri)
|
||||
@@ -100,23 +86,23 @@ class BlossomUploader {
|
||||
val imageInputStreamForHash = contentResolver.openInputStream(uri)
|
||||
checkNotNull(imageInputStreamForHash) { "Can't open the image input stream" }
|
||||
|
||||
val streamInfo =
|
||||
imageInputStreamForHash.use {
|
||||
calculateHashAndSize(it)
|
||||
val (hash, size) =
|
||||
imageInputStreamForHash.use { stream ->
|
||||
val (hashBytes, totalBytes) = sha256StreamWithCount(stream)
|
||||
hashBytes.toHexKey() to totalBytes
|
||||
}
|
||||
|
||||
val localMetadata = BlurhashMetadataCalculator.computeFromUri(context, uri, myContentType)
|
||||
|
||||
val imageInputStream = contentResolver.openInputStream(uri)
|
||||
|
||||
checkNotNull(imageInputStream) { "Can't open the image input stream" }
|
||||
|
||||
val serverResult =
|
||||
imageInputStream.use { stream ->
|
||||
upload(
|
||||
stream,
|
||||
streamInfo.hash,
|
||||
streamInfo.size,
|
||||
hash,
|
||||
size,
|
||||
fileName,
|
||||
myContentType,
|
||||
alt,
|
||||
|
||||
+6
-9
@@ -128,15 +128,12 @@ class Nip96Uploader {
|
||||
)
|
||||
}
|
||||
|
||||
val merged =
|
||||
localMetadata?.let { (blur, dim) ->
|
||||
serverResult.copy(
|
||||
dimension = dim ?: serverResult.dimension,
|
||||
blurHash = blur ?: serverResult.blurHash,
|
||||
)
|
||||
} ?: serverResult
|
||||
|
||||
return merged
|
||||
return localMetadata?.let { (blur, dim) ->
|
||||
serverResult.copy(
|
||||
dimension = dim ?: serverResult.dimension,
|
||||
blurHash = blur ?: serverResult.blurHash,
|
||||
)
|
||||
} ?: serverResult
|
||||
}
|
||||
|
||||
suspend fun upload(
|
||||
|
||||
Reference in New Issue
Block a user