mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
fix: resolve blossom: URIs when saving media to gallery
The download/save-to-local button handed the raw content URL straight to OkHttp. For BUD-10 `blossom:` URIs this failed with "expected scheme http or https but was blossom" because OkHttp only speaks http/https. Resolve `blossom:` URIs to a concrete server URL via BlossomServerResolver (the same resolver the Coil/ExoPlayer pipeline uses) before downloading, in both save entry points (AccountViewModel.saveMediaToGallery and the zoomable dialog's save action). When no hosting server can be found, the save reports an error instead of crashing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BawgXifvcPidMMqJ719Ka2
This commit is contained in:
@@ -44,6 +44,7 @@ import okio.buffer
|
||||
import okio.sink
|
||||
import okio.source
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
@@ -53,6 +54,7 @@ object MediaSaverToDisk {
|
||||
okHttpClient: (String) -> OkHttpClient,
|
||||
mimeType: String?,
|
||||
localContext: Context,
|
||||
resolveBlossom: suspend (String) -> String? = { null },
|
||||
onSuccess: () -> Any?,
|
||||
onError: (Throwable) -> Any?,
|
||||
) = withContext(Dispatchers.IO) {
|
||||
@@ -77,6 +79,7 @@ object MediaSaverToDisk {
|
||||
mimeType = mimeType,
|
||||
okHttpClient = okHttpClient,
|
||||
context = localContext,
|
||||
resolveBlossom = resolveBlossom,
|
||||
onSuccess = onSuccess,
|
||||
onError = onError,
|
||||
)
|
||||
@@ -87,6 +90,11 @@ object MediaSaverToDisk {
|
||||
/**
|
||||
* Saves the image to the gallery. May require a storage permission.
|
||||
*
|
||||
* `blossom:` (BUD-10) URIs are resolved to a concrete `http(s)` server URL
|
||||
* via [resolveBlossom] before downloading, since OkHttp only speaks
|
||||
* http/https. When resolution fails the save reports an error instead of
|
||||
* crashing with "expected scheme http or https but was blossom".
|
||||
*
|
||||
* @see AMETHYST_SUBDIRECTORY
|
||||
*/
|
||||
suspend fun downloadAndSave(
|
||||
@@ -94,26 +102,35 @@ object MediaSaverToDisk {
|
||||
mimeType: String?,
|
||||
okHttpClient: (String) -> OkHttpClient,
|
||||
context: Context,
|
||||
resolveBlossom: suspend (String) -> String? = { null },
|
||||
onSuccess: () -> Any?,
|
||||
onError: (Throwable) -> Any?,
|
||||
) {
|
||||
try {
|
||||
val client = okHttpClient(url)
|
||||
val downloadUrl =
|
||||
if (url.startsWith(BLOSSOM_SCHEME, ignoreCase = true)) {
|
||||
resolveBlossom(url)
|
||||
?: throw IOException("Could not find a Blossom server that hosts $url")
|
||||
} else {
|
||||
url
|
||||
}
|
||||
|
||||
val client = okHttpClient(downloadUrl)
|
||||
|
||||
val request =
|
||||
Request
|
||||
.Builder()
|
||||
.get()
|
||||
.url(url)
|
||||
.url(downloadUrl)
|
||||
.build()
|
||||
|
||||
client.newCall(request).executeAsync().use { response ->
|
||||
withContext(Dispatchers.IO) {
|
||||
check(response.isSuccessful) {
|
||||
"Failed to download $url: HTTP ${response.code} ${response.message}"
|
||||
"Failed to download $downloadUrl: HTTP ${response.code} ${response.message}"
|
||||
}
|
||||
|
||||
val trimmedUrl = trimInlineMetaData(url)
|
||||
val trimmedUrl = trimInlineMetaData(downloadUrl)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val headerType =
|
||||
response
|
||||
@@ -292,4 +309,5 @@ object MediaSaverToDisk {
|
||||
|
||||
private const val AMETHYST_SUBDIRECTORY = "Amethyst"
|
||||
private const val PDF_MIME_TYPE = "application/pdf"
|
||||
private const val BLOSSOM_SCHEME = "blossom:"
|
||||
}
|
||||
|
||||
+6
@@ -83,6 +83,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
@@ -532,6 +533,11 @@ internal suspend fun saveMediaToGallery(
|
||||
}
|
||||
},
|
||||
localContext,
|
||||
resolveBlossom = {
|
||||
Amethyst.instance.blossomResolver
|
||||
.findServers(it)
|
||||
?.serverUrl
|
||||
},
|
||||
onSuccess = {
|
||||
showToastOnMain(localContext, success)
|
||||
},
|
||||
|
||||
+5
@@ -2254,6 +2254,11 @@ class AccountViewModel(
|
||||
okHttpClient = httpClientBuilder::okHttpClientForVideo,
|
||||
mimeType = mimeType,
|
||||
localContext = localContext,
|
||||
resolveBlossom = {
|
||||
Amethyst.instance.blossomResolver
|
||||
.findServers(it)
|
||||
?.serverUrl
|
||||
},
|
||||
onSuccess = {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
Toast
|
||||
|
||||
Reference in New Issue
Block a user