diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 22b8a81..f615157 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,6 +22,8 @@ jellyfinMedia3 = "1.9.0+1" mpv = "1.0.0" retrofit = "3.0.0" okhttp = "5.4.0" +cronetEmbedded = "143.7445.0" +cronetOkhttp = "0.1.1" coil3 = "3.4.0" room = "2.8.4" jna = "5.17.0" @@ -106,6 +108,8 @@ retrofit-gson = { group = "com.squareup.retrofit2", name = "converter-gson okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor",version.ref = "okhttp" } okhttp-doh = { group = "com.squareup.okhttp3", name = "okhttp-dnsoverhttps",version.ref = "okhttp" } okhttp-mockwebserver= { group = "com.squareup.okhttp3", name = "mockwebserver", version.ref = "okhttp" } +cronet-embedded = { group = "org.chromium.net", name = "cronet-embedded", version.ref = "cronetEmbedded" } +cronet-okhttp = { group = "com.google.net.cronet", name = "cronet-okhttp", version.ref = "cronetOkhttp" } # Coil coil3 = { group = "io.coil-kt.coil3", name = "coil", version.ref = "coil3" } diff --git a/player/build.gradle.kts b/player/build.gradle.kts index 21814de..6b27066 100644 --- a/player/build.gradle.kts +++ b/player/build.gradle.kts @@ -51,6 +51,8 @@ kotlin { implementation(libs.mpv) implementation(libs.bundles.retrofit) implementation(libs.okhttp.logging) + implementation(libs.cronet.embedded) + implementation(libs.cronet.okhttp) } } } diff --git a/player/src/androidMain/kotlin/com/fluxa/app/player/CronetTransport.kt b/player/src/androidMain/kotlin/com/fluxa/app/player/CronetTransport.kt new file mode 100644 index 0000000..28ab6ad --- /dev/null +++ b/player/src/androidMain/kotlin/com/fluxa/app/player/CronetTransport.kt @@ -0,0 +1,31 @@ +package com.fluxa.app.player + +import android.content.Context +import android.util.Log +import com.google.net.cronet.okhttptransport.CronetInterceptor +import okhttp3.Interceptor +import org.chromium.net.CronetEngine + +private var cronetEngine: CronetEngine? = null +private var cronetEngineInitAttempted = false + +private fun sharedCronetEngine(context: Context): CronetEngine? { + if (!cronetEngineInitAttempted) { + cronetEngineInitAttempted = true + cronetEngine = runCatching { + CronetEngine.Builder(context.applicationContext) + .enableHttp2(true) + .enableQuic(true) + .enableBrotli(true) + .build() + }.onFailure { + Log.w("CronetTransport", "Cronet engine unavailable, staying on OkHttp", it) + }.getOrNull() + } + return cronetEngine +} + +fun cronetTransportInterceptor(context: Context): Interceptor? = + sharedCronetEngine(context)?.let { engine -> + runCatching { CronetInterceptor.newBuilder(engine).build() }.getOrNull() + } diff --git a/player/src/androidMain/kotlin/com/fluxa/app/player/MediaPlayerController.kt b/player/src/androidMain/kotlin/com/fluxa/app/player/MediaPlayerController.kt index 79b6da7..5d60a89 100644 --- a/player/src/androidMain/kotlin/com/fluxa/app/player/MediaPlayerController.kt +++ b/player/src/androidMain/kotlin/com/fluxa/app/player/MediaPlayerController.kt @@ -196,6 +196,7 @@ class MediaPlayerController(internal val context: Context, val exoPlayer: ExoPla if (!shouldUsePlayerDiskCache(uri)) return val okHttp = OkHttpClient.Builder() .callTimeout(10, java.util.concurrent.TimeUnit.SECONDS) + .apply { cronetTransportInterceptor(context)?.let { addInterceptor(it) } } .build() val upstream = OkHttpDataSource.Factory(okHttp) .setUserAgent(StreamRequestPolicy.DEFAULT_USER_AGENT) @@ -266,6 +267,7 @@ class MediaPlayerController(internal val context: Context, val exoPlayer: ExoPla chain.proceed(chain.request()) } } + .apply { cronetTransportInterceptor(context)?.let { addInterceptor(it) } } .build() val httpDataSourceFactory = OkHttpDataSource.Factory(okHttpClient)