feat: add media upload API endpoints and repository method for video/audio

- Create MediaModels.kt with MediaUploadResponse and UploadedMedia data classes
- Add uploadMedia() and uploadMediaThumbnail() to GhostApiService
- Add uploadMediaFile(uri) to PostRepository with MIME detection
This commit is contained in:
Paweł Orzech 2026-03-20 00:42:58 +01:00
parent 807c6d559e
commit 2410d05bd6
3 changed files with 60 additions and 3 deletions

View file

@ -1,6 +1,7 @@
package com.swoosh.microblog.data.api package com.swoosh.microblog.data.api
import com.swoosh.microblog.data.model.GhostSite import com.swoosh.microblog.data.model.GhostSite
import com.swoosh.microblog.data.model.MediaUploadResponse
import com.swoosh.microblog.data.model.MembersResponse import com.swoosh.microblog.data.model.MembersResponse
import com.swoosh.microblog.data.model.PageWrapper import com.swoosh.microblog.data.model.PageWrapper
import com.swoosh.microblog.data.model.PagesResponse import com.swoosh.microblog.data.model.PagesResponse
@ -114,6 +115,20 @@ interface GhostApiService {
@Part file: MultipartBody.Part, @Part file: MultipartBody.Part,
@Part("purpose") purpose: RequestBody @Part("purpose") purpose: RequestBody
): Response<ImageUploadResponse> ): Response<ImageUploadResponse>
@Multipart
@POST("ghost/api/admin/media/upload/")
suspend fun uploadMedia(
@Part file: MultipartBody.Part,
@Part("ref") ref: RequestBody? = null
): Response<MediaUploadResponse>
@Multipart
@POST("ghost/api/admin/media/thumbnail/upload/")
suspend fun uploadMediaThumbnail(
@Part file: MultipartBody.Part,
@Part("ref") ref: RequestBody? = null
): Response<MediaUploadResponse>
} }
data class ImageUploadResponse( data class ImageUploadResponse(

View file

@ -0,0 +1,11 @@
package com.swoosh.microblog.data.model
data class MediaUploadResponse(
val media: List<UploadedMedia>
)
data class UploadedMedia(
val url: String,
val ref: String?,
val fileName: String?
)

View file

@ -113,8 +113,8 @@ class PostRepository(private val context: Context) {
suspend fun uploadImage(uri: Uri): Result<String> = suspend fun uploadImage(uri: Uri): Result<String> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
try { try {
val file = copyUriToTempFile(uri)
val mimeType = context.contentResolver.getType(uri) ?: "image/jpeg" val mimeType = context.contentResolver.getType(uri) ?: "image/jpeg"
val file = copyUriToTempFile(uri, ".jpg")
val requestBody = file.asRequestBody(mimeType.toMediaType()) val requestBody = file.asRequestBody(mimeType.toMediaType())
val part = MultipartBody.Part.createFormData("file", file.name, requestBody) val part = MultipartBody.Part.createFormData("file", file.name, requestBody)
val purpose = "image".toRequestBody("text/plain".toMediaType()) val purpose = "image".toRequestBody("text/plain".toMediaType())
@ -156,10 +156,41 @@ class PostRepository(private val context: Context) {
} }
} }
private fun copyUriToTempFile(uri: Uri): File { /**
* Uploads a media file (video or audio) to the Ghost media upload endpoint.
* Determines MIME type from the content resolver. Returns the uploaded URL on success.
*/
suspend fun uploadMediaFile(uri: Uri): Result<String> =
withContext(Dispatchers.IO) {
try {
val mimeType = context.contentResolver.getType(uri) ?: "application/octet-stream"
val extension = when {
mimeType.startsWith("video/") -> mimeType.substringAfter("video/").let { ".$it" }
mimeType.startsWith("audio/") -> mimeType.substringAfter("audio/").let { ".$it" }
else -> ""
}
val file = copyUriToTempFile(uri, extension)
val requestBody = file.asRequestBody(mimeType.toMediaType())
val part = MultipartBody.Part.createFormData("file", file.name, requestBody)
val response = getApi().uploadMedia(part)
file.delete()
if (response.isSuccessful) {
val url = response.body()!!.media.first().url
Result.success(url)
} else {
Result.failure(Exception("Media upload failed ${response.code()}"))
}
} catch (e: Exception) {
Result.failure(e)
}
}
private fun copyUriToTempFile(uri: Uri, extension: String = ".jpg"): File {
val inputStream = context.contentResolver.openInputStream(uri) val inputStream = context.contentResolver.openInputStream(uri)
?: throw IllegalStateException("Cannot open URI") ?: throw IllegalStateException("Cannot open URI")
val tempFile = File.createTempFile("upload_", ".jpg", context.cacheDir) val tempFile = File.createTempFile("upload_", extension, context.cacheDir)
FileOutputStream(tempFile).use { output -> FileOutputStream(tempFile).use { output ->
inputStream.copyTo(output) inputStream.copyTo(output)
} }