feat: unify audio and video attachment playback (#1188)
This commit is contained in:
23 files changed
+2640
-496
No files matched your search
@@ -1,5 +1,7 @@
|
||||
package com.m57.hermescontrol.data.model
|
||||
|
||||
import com.m57.hermescontrol.ui.chat.MediaKind
|
||||
import com.m57.hermescontrol.ui.chat.classifyMedia
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
@@ -25,22 +27,23 @@ data class Attachment(
|
||||
val gatewayUrl: String? = null,
|
||||
val source: AttachmentSource = AttachmentSource.LOCAL,
|
||||
) {
|
||||
val mediaKind: MediaKind
|
||||
get() = classifyMedia(mimeType = mimeType, name = name, uri = gatewayUrl ?: uri)
|
||||
|
||||
val isImage: Boolean
|
||||
get() = mimeType.startsWith("image/")
|
||||
get() = mediaKind == MediaKind.IMAGE
|
||||
|
||||
val isGif: Boolean
|
||||
get() =
|
||||
mimeType.equals("image/gif", ignoreCase = true) ||
|
||||
fileExtension == "gif" ||
|
||||
uri.contains(".gif", ignoreCase = true) ||
|
||||
uri.startsWith("data:image/gif", ignoreCase = true)
|
||||
|
||||
val isVideo: Boolean
|
||||
get() =
|
||||
mimeType.startsWith("video/") ||
|
||||
fileExtension in listOf("mp4", "webm", "mkv", "mov", "avi", "3gp") ||
|
||||
uri.contains(".mp4", ignoreCase = true) ||
|
||||
uri.contains(".webm", ignoreCase = true)
|
||||
get() = mediaKind == MediaKind.VIDEO
|
||||
|
||||
val isAudio: Boolean
|
||||
get() = mediaKind == MediaKind.AUDIO
|
||||
|
||||
val isGateway: Boolean
|
||||
get() = source == AttachmentSource.GATEWAY
|
||||
|
||||
@@ -277,6 +277,7 @@ object GatewayFileClient {
|
||||
coroutineContext.ensureActive()
|
||||
val read = input.read(buffer)
|
||||
if (read < 0) break
|
||||
coroutineContext.ensureActive()
|
||||
output.write(buffer, 0, read)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.m57.hermescontrol.data.remote
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.datasource.DataSource
|
||||
import androidx.media3.datasource.DefaultDataSource
|
||||
import androidx.media3.datasource.okhttp.OkHttpDataSource
|
||||
import com.m57.hermescontrol.data.local.AuthManager
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Response
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
object MediaDataSourceProvider {
|
||||
class SameOriginAuthInterceptor(
|
||||
private val baseUrlProvider: () -> String = { runCatching { AuthManager.baseUrl() }.getOrDefault("") },
|
||||
private val tokenProvider: () -> String? = { runCatching { AuthManager.getToken() }.getOrNull() },
|
||||
private val isGatedModeProvider: () -> Boolean = {
|
||||
runCatching { AuthManager.isGatedMode() }.getOrDefault(
|
||||
false,
|
||||
)
|
||||
},
|
||||
) : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
// In gated mode (basic auth), the dashboard authenticates via session cookies;
|
||||
// stamping an Authorization: Bearer header causes 401s (issue #470).
|
||||
if (isGatedModeProvider()) {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
val url = request.url
|
||||
val base = baseUrlProvider().toHttpUrlOrNull()
|
||||
val isSameOrigin =
|
||||
base != null &&
|
||||
url.scheme.equals(base.scheme, ignoreCase = true) &&
|
||||
url.host.equals(base.host, ignoreCase = true) &&
|
||||
url.port == base.port
|
||||
|
||||
if (isSameOrigin) {
|
||||
val token = tokenProvider()
|
||||
if (!token.isNullOrBlank() && request.header("Authorization") == null) {
|
||||
return chain.proceed(
|
||||
request
|
||||
.newBuilder()
|
||||
.header("Authorization", "Bearer $token")
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
}
|
||||
return chain.proceed(request)
|
||||
}
|
||||
}
|
||||
|
||||
@Volatile
|
||||
internal var testClient: OkHttpClient? = null
|
||||
|
||||
val mediaOkHttpClient: OkHttpClient
|
||||
get() = testClient ?: defaultClient
|
||||
|
||||
private val defaultClient: OkHttpClient by lazy {
|
||||
OkHttpProvider.base
|
||||
.newBuilder()
|
||||
.addInterceptor(SameOriginAuthInterceptor())
|
||||
.build()
|
||||
}
|
||||
|
||||
fun createDataSourceFactory(context: Context): DataSource.Factory {
|
||||
val httpDataSourceFactory = OkHttpDataSource.Factory(mediaOkHttpClient)
|
||||
return DefaultDataSource.Factory(context, httpDataSourceFactory)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user