net: send the subtracks user agent on every request

Only SubsonicClient tagged requests with 'subtracks/android'; playback (Media3 OkHttpDataSource), downloads (DownloadManager) and artwork (Coil/OkHttpArtworkFetcher) fell back to the OkHttp or platform default. Add a single UserAgent with the app version, apply it to the shared OkHttp client, the streaming data source factory and the DownloadManager request, and drop SubsonicClient's now-redundant header.
This commit is contained in:
austinried
2026-10-09 06:33:22 +00:00
parent ce9e3e89a7
commit bcd7bbf23a
6 changed files with 49 additions and 4 deletions
@@ -14,7 +14,7 @@ import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
class SubtracksApp : Application() {
private val http = OkHttpClient()
private val http = OkHttpClient.Builder().addInterceptor(UserAgent.interceptor()).build()
@OptIn(DelicateCoilApi::class)
override fun onCreate() {
@@ -0,0 +1,18 @@
package com.subtracks
import okhttp3.Interceptor
object UserAgent {
val value: String = "subtracks/android (${BuildConfig.VERSION_NAME})"
fun interceptor(): Interceptor =
Interceptor { chain ->
chain.proceed(
chain
.request()
.newBuilder()
.header("User-Agent", value)
.build(),
)
}
}
@@ -4,6 +4,7 @@ import android.app.DownloadManager
import android.content.Context
import android.os.Environment
import androidx.core.net.toUri
import com.subtracks.UserAgent
import com.subtracks.data.model.DownloadError
class SystemDownloadEngine(
@@ -16,6 +17,7 @@ class SystemDownloadEngine(
.setTitle(request.title)
.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_MUSIC, request.path)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN)
.addRequestHeader("User-Agent", UserAgent.value)
// Wi-Fi only unless the preference allows a metered network, and roaming counts as
// one of those; the platform otherwise permits it by default.
.setAllowedOverMetered(request.allowMetered)
@@ -20,7 +20,6 @@ class SubsonicClient(
private val password: String,
useTokenAuth: Boolean,
private val http: OkHttpClient,
private val userAgent: String = "subtracks/android",
private val onTokenAuthUnsupported: () -> Unit = {},
) {
@Volatile
@@ -76,7 +75,6 @@ class SubsonicClient(
Request
.Builder()
.url(uri(method, params))
.header("User-Agent", userAgent)
.build()
http.newCall(request).execute().use { response ->
if (!response.isSuccessful) {
@@ -29,6 +29,7 @@ import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import com.subtracks.MainActivity
import com.subtracks.R
import com.subtracks.UserAgent
import com.subtracks.data.repo.LibraryRepository
import com.subtracks.data.repo.SourceRepository
import com.subtracks.data.source.StarType
@@ -104,7 +105,9 @@ class PlaybackService : MediaSessionService() {
.setMediaSourceFactory(
DefaultMediaSourceFactory(this)
.setEnableClippingInMediaPeriod(true)
.setDataSourceFactory(mediaDataSourceFactory(this, OkHttpDataSource.Factory(streamingClient()))),
.setDataSourceFactory(
mediaDataSourceFactory(this, OkHttpDataSource.Factory(streamingClient()).setUserAgent(UserAgent.value)),
),
).build()
exoPlayer.addListener(
object : Player.Listener {
@@ -0,0 +1,24 @@
package com.subtracks
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.Assert.assertEquals
import org.junit.Test
class UserAgentTest {
@Test
fun addsTheSubtracksUserAgent() {
val server = MockWebServer()
server.enqueue(MockResponse())
server.start()
val client = OkHttpClient.Builder().addInterceptor(UserAgent.interceptor()).build()
client.newCall(Request.Builder().url(server.url("/")).build()).execute().close()
val header = server.takeRequest().getHeader("User-Agent")
server.shutdown()
assertEquals(UserAgent.value, header)
}
}