Improves upload of music and documents.

This commit is contained in:
Vitor Pamplona
2026-03-10 19:49:15 -04:00
parent 8a03f0b501
commit 8cd9449ba6
7 changed files with 81 additions and 57 deletions
@@ -79,7 +79,7 @@ class BlossomServerListState(
emptyList(),
)
fun mergeServerList(blossom: List<String>?): List<ServerName> = blossom?.map { ServerName(host(it), it, ServerType.Blossom) } ?: emptyList()
fun mergeServerList(blossom: List<String>?): List<ServerName> = blossom?.map { ServerName(host(it), it, ServerType.Blossom) }?.ifEmpty { DEFAULT_MEDIA_SERVERS } ?: DEFAULT_MEDIA_SERVERS
val hostNameFlow: StateFlow<List<ServerName>> =
flow
@@ -49,6 +49,8 @@ class MultiOrchestrator(
fun hasVideo() = list.any { it.media.mimeType?.startsWith("video", ignoreCase = true) == true }
fun hasNonMedia() = list.any { it.media.isNotMedia() }
suspend fun upload(
alt: String?,
contentWarningReason: String?,
@@ -54,6 +54,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
@@ -204,7 +205,8 @@ fun ImageVideoPost(
fileServers
.firstOrNull { it == accountViewModel.account.settings.defaultFileServer }
?.name
?: fileServers[0].name,
?: fileServers.firstOrNull()?.name
?: DEFAULT_MEDIA_SERVERS[0].name,
options = fileServerOptions,
onSelect = { postViewModel.selectedServer = fileServers[it] },
)
@@ -58,6 +58,11 @@ class SelectedMedia(
fun isAudio() = mimeType?.startsWith("audio")
fun isNotMedia() =
mimeType?.let {
!(it.startsWith("image") || it.startsWith("video") || it.startsWith("audio"))
} ?: true
fun isDocument() = mimeType == "application/pdf"
}
@@ -129,7 +129,7 @@ fun ShowImageGallery(
icon = Icons.Default.AudioFile,
label = media.mimeType ?: "audio/*",
)
} else if (media.isDocument() == true) {
} else if (media.isDocument()) {
FilePreviewPlaceholder(
icon = Icons.Default.PictureAsPdf,
label = media.mimeType ?: "application/pdf",
@@ -236,39 +236,44 @@ fun UploadingState(
progress: Double,
progressState: UploadingState,
) {
Box(Modifier.size(55.dp), contentAlignment = Alignment.Center) {
val animatedProgress by animateFloatAsState(
targetValue = progress.toFloat(),
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize(),
) {
Box(Modifier.size(55.dp), contentAlignment = Alignment.Center) {
val animatedProgress by animateFloatAsState(
targetValue = progress.toFloat(),
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
)
CircularProgressIndicator(
progress = { animatedProgress },
modifier =
Size55Modifier
.clip(CircleShape)
.background(MaterialTheme.colorScheme.background),
strokeWidth = 5.dp,
)
CircularProgressIndicator(
progress = { animatedProgress },
modifier =
Size55Modifier
.clip(CircleShape)
.background(MaterialTheme.colorScheme.background),
strokeWidth = 5.dp,
)
val txt =
when (progressState) {
is UploadingState.Ready -> stringRes(R.string.uploading_state_ready)
is UploadingState.Compressing -> stringRes(R.string.uploading_state_compressing)
is UploadingState.Uploading -> stringRes(R.string.uploading_state_uploading)
is UploadingState.ServerProcessing -> stringRes(R.string.uploading_state_server_processing)
is UploadingState.Downloading -> stringRes(R.string.uploading_state_downloading)
is UploadingState.Hashing -> stringRes(R.string.uploading_state_hashing)
is UploadingState.Finished -> stringRes(R.string.uploading_state_finished)
is UploadingState.Error -> stringRes(R.string.uploading_state_error)
}
val txt =
when (progressState) {
is UploadingState.Ready -> stringRes(R.string.uploading_state_ready)
is UploadingState.Compressing -> stringRes(R.string.uploading_state_compressing)
is UploadingState.Uploading -> stringRes(R.string.uploading_state_uploading)
is UploadingState.ServerProcessing -> stringRes(R.string.uploading_state_server_processing)
is UploadingState.Downloading -> stringRes(R.string.uploading_state_downloading)
is UploadingState.Hashing -> stringRes(R.string.uploading_state_hashing)
is UploadingState.Finished -> stringRes(R.string.uploading_state_finished)
is UploadingState.Error -> stringRes(R.string.uploading_state_error)
}
Text(
txt,
color = MaterialTheme.colorScheme.onSurface,
fontSize = 10.sp,
textAlign = TextAlign.Center,
)
Text(
txt,
color = MaterialTheme.colorScheme.onSurface,
fontSize = 10.sp,
textAlign = TextAlign.Center,
)
}
}
}
@@ -280,7 +285,7 @@ fun FilePreviewPlaceholder(
Box(
modifier =
Modifier
.fillMaxWidth()
.fillMaxSize()
.background(MaterialTheme.colorScheme.surfaceVariant),
contentAlignment = Alignment.Center,
) {
@@ -57,6 +57,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing
import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery
@@ -92,14 +93,16 @@ fun ImageVideoDescription(
var selectedServer by remember {
mutableStateOf(
fileServers.firstOrNull { it == defaultServer } ?: fileServers[0],
fileServers.firstOrNull { it == defaultServer } ?: fileServers.firstOrNull() ?: DEFAULT_MEDIA_SERVERS[0],
)
}
var message by remember { mutableStateOf("") }
var sensitiveContent by remember { mutableStateOf(false) }
// 0 = Low, 1 = Medium, 2 = High, 3=UNCOMPRESSED
var mediaQualitySlider by remember { mutableIntStateOf(1) }
var mediaQualitySlider by remember {
mutableIntStateOf(if (uris.hasNonMedia()) 3 else 1)
}
// Codec selection: false = H264, true = H265
var useH265Codec by remember { mutableStateOf(false) }
@@ -189,7 +192,8 @@ fun ImageVideoDescription(
fileServers
.firstOrNull { it == defaultServer }
?.name
?: fileServers[0].name,
?: fileServers.firstOrNull()?.name
?: DEFAULT_MEDIA_SERVERS[0].name,
options = fileServerOptions,
onSelect = { selectedServer = fileServers[it] },
modifier =
@@ -270,27 +274,31 @@ fun ImageVideoDescription(
}
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Box(modifier = Modifier.fillMaxWidth()) {
Text(
text =
when (mediaQualitySlider) {
0 -> stringRes(R.string.media_compression_quality_low)
1 -> stringRes(R.string.media_compression_quality_medium)
2 -> stringRes(R.string.media_compression_quality_high)
3 -> stringRes(R.string.media_compression_quality_uncompressed)
else -> stringRes(R.string.media_compression_quality_medium)
},
modifier = Modifier.align(Alignment.Center),
val firstMedia = uris.first().media
if (firstMedia.isVideo() == true || firstMedia.isImage() == true || firstMedia.isAudio() == true) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Box(modifier = Modifier.fillMaxWidth()) {
Text(
text =
when (mediaQualitySlider) {
0 -> stringRes(R.string.media_compression_quality_low)
1 -> stringRes(R.string.media_compression_quality_medium)
2 -> stringRes(R.string.media_compression_quality_high)
3 -> stringRes(R.string.media_compression_quality_uncompressed)
else -> stringRes(R.string.media_compression_quality_medium)
},
modifier = Modifier.align(Alignment.Center),
)
}
Slider(
value = mediaQualitySlider.toFloat(),
onValueChange = { mediaQualitySlider = it.toInt() },
valueRange = 0f..3f,
steps = 2,
)
}
Slider(
value = mediaQualitySlider.toFloat(),
onValueChange = { mediaQualitySlider = it.toInt() },
valueRange = 0f..3f,
steps = 2,
)
}
if (uris.first().media.isVideo() == true) {
@@ -55,6 +55,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.components.TextSpinner
@@ -196,7 +197,8 @@ private fun ImageVideoPostChat(
fileServers
.firstOrNull { it == accountViewModel.account.settings.defaultFileServer }
?.name
?: fileServers[0].name,
?: fileServers.firstOrNull()?.name
?: DEFAULT_MEDIA_SERVERS[0].name,
options = fileServerOptions,
onSelect = { fileUploadState.selectedServer = fileServers[it] },
)