diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/GetStreamUrlTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/GetStreamUrlTest.kt index 063a5cf7..2b828a1a 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/GetStreamUrlTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/GetStreamUrlTest.kt @@ -21,8 +21,14 @@ class GetStreamUrlTest { @Before fun setUp() { - client = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), - USERNAME, PASSWORD, V1_6_0, CLIENT_ID) + val config = SubsonicClientConfiguration( + mockWebServerRule.mockWebServer.url("/").toString(), + USERNAME, + PASSWORD, + V1_6_0, + CLIENT_ID + ) + client = SubsonicAPIClient(config) val baseExpectedUrl = mockWebServerRule.mockWebServer.url("").toString() expectedUrl = "$baseExpectedUrl/rest/stream.view?id=$id&u=$USERNAME" + "&c=$CLIENT_ID&f=json&v=${V1_6_0.restApiVersion}&p=enc:${PASSWORD.toHexBytes()}" diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt index 6d4dfffd..d82c34c2 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt @@ -10,11 +10,18 @@ import org.moire.ultrasonic.api.subsonic.rules.MockWebServerRule abstract class SubsonicAPIClientTest { @JvmField @Rule val mockWebServerRule = MockWebServerRule() + protected lateinit var config: SubsonicClientConfiguration protected lateinit var client: SubsonicAPIClient @Before fun setUp() { - client = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), - USERNAME, PASSWORD, CLIENT_VERSION, CLIENT_ID) + config = SubsonicClientConfiguration( + mockWebServerRule.mockWebServer.url("/").toString(), + USERNAME, + PASSWORD, + CLIENT_VERSION, + CLIENT_ID + ) + client = SubsonicAPIClient(config) } } diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiPasswordTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiPasswordTest.kt index 1a10e6cb..536cb8a7 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiPasswordTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiPasswordTest.kt @@ -10,8 +10,9 @@ import org.junit.Test class SubsonicApiPasswordTest : SubsonicAPIClientTest() { @Test fun `Should pass PasswordMD5Interceptor in query params for api version 1 13 0`() { - val clientV12 = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), - USERNAME, PASSWORD, SubsonicAPIVersions.V1_14_0, CLIENT_ID) + val clientV12 = SubsonicAPIClient( + config.copy(minimalProtocolVersion = SubsonicAPIVersions.V1_14_0) + ) mockWebServerRule.enqueueResponse("ping_ok.json") clientV12.api.ping().execute() @@ -25,8 +26,9 @@ class SubsonicApiPasswordTest : SubsonicAPIClientTest() { @Test fun `Should pass PasswordHexInterceptor in query params for api version 1 12 0`() { - val clientV11 = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), - USERNAME, PASSWORD, SubsonicAPIVersions.V1_12_0, CLIENT_ID) + val clientV11 = SubsonicAPIClient( + config.copy(minimalProtocolVersion = SubsonicAPIVersions.V1_12_0) + ) mockWebServerRule.enqueueResponse("ping_ok.json") clientV11.api.ping().execute() diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSSLTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSSLTest.kt index fb43dbc1..89f8f21d 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSSLTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSSLTest.kt @@ -90,7 +90,15 @@ class SubsonicApiSSLTest { assertResponseSuccessful(response) } - private fun createSubsonicClient(allowSelfSignedCertificate: Boolean) = SubsonicAPIClient( - "https://$HOST:$PORT/", USERNAME, PASSWORD, CLIENT_VERSION, CLIENT_ID, - allowSelfSignedCertificate = allowSelfSignedCertificate) + private fun createSubsonicClient(allowSelfSignedCertificate: Boolean): SubsonicAPIClient { + val config = SubsonicClientConfiguration( + "https://$HOST:$PORT/", + USERNAME, + PASSWORD, + CLIENT_VERSION, + CLIENT_ID, + allowSelfSignedCertificate = allowSelfSignedCertificate + ) + return SubsonicAPIClient(config) + } } diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt index 577e55d2..6f407576 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt @@ -36,29 +36,23 @@ private const val READ_TIMEOUT = 60_000L * @author Yahor Berdnikau */ class SubsonicAPIClient( - baseUrl: String, - username: String, - password: String, - minimalProtocolVersion: SubsonicAPIVersions, - clientID: String, - allowSelfSignedCertificate: Boolean = false, - enableLdapUserSupport: Boolean = false, - debug: Boolean = false + config: SubsonicClientConfiguration ) { - private val versionInterceptor = VersionInterceptor(minimalProtocolVersion) { + private val versionInterceptor = VersionInterceptor(config.minimalProtocolVersion) { protocolVersion = it } private val proxyPasswordInterceptor = ProxyPasswordInterceptor( - minimalProtocolVersion, - PasswordHexInterceptor(password), - PasswordMD5Interceptor(password), - enableLdapUserSupport) + config.minimalProtocolVersion, + PasswordHexInterceptor(config.password), + PasswordMD5Interceptor(config.password), + config.enableLdapUserSupport + ) /** * Get currently used protocol version. */ - var protocolVersion = minimalProtocolVersion + var protocolVersion = config.minimalProtocolVersion private set(value) { field = value proxyPasswordInterceptor.apiVersion = field @@ -67,13 +61,13 @@ class SubsonicAPIClient( private val okHttpClient = OkHttpClient.Builder() .readTimeout(READ_TIMEOUT, MILLISECONDS) - .apply { if (allowSelfSignedCertificate) allowSelfSignedCertificates() } + .apply { if (config.allowSelfSignedCertificate) allowSelfSignedCertificates() } .addInterceptor { chain -> // Adds default request params val originalRequest = chain.request() val newUrl = originalRequest.url().newBuilder() - .addQueryParameter("u", username) - .addQueryParameter("c", clientID) + .addQueryParameter("u", config.username) + .addQueryParameter("c", config.clientID) .addQueryParameter("f", "json") .build() chain.proceed(originalRequest.newBuilder().url(newUrl).build()) @@ -81,7 +75,7 @@ class SubsonicAPIClient( .addInterceptor(versionInterceptor) .addInterceptor(proxyPasswordInterceptor) .addInterceptor(RangeHeaderInterceptor()) - .apply { if (debug) addLogging() } + .apply { if (config.debug) addLogging() } .build() private val jacksonMapper = ObjectMapper() @@ -90,14 +84,15 @@ class SubsonicAPIClient( .registerModule(KotlinModule()) private val retrofit = Retrofit.Builder() - .baseUrl("$baseUrl/rest/") + .baseUrl("${config.baseUrl}/rest/") .client(okHttpClient) .addConverterFactory(JacksonConverterFactory.create(jacksonMapper)) .build() private val wrappedApi = ApiVersionCheckWrapper( retrofit.create(SubsonicAPIDefinition::class.java), - minimalProtocolVersion) + config.minimalProtocolVersion + ) val api: SubsonicAPIDefinition get() = wrappedApi diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicClientConfiguration.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicClientConfiguration.kt new file mode 100644 index 00000000..732efe7a --- /dev/null +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicClientConfiguration.kt @@ -0,0 +1,15 @@ +package org.moire.ultrasonic.api.subsonic + +/** + * Provides configuration for [SubsonicAPIClient]. + */ +data class SubsonicClientConfiguration( + val baseUrl: String, + val username: String, + val password: String, + val minimalProtocolVersion: SubsonicAPIVersions, + val clientID: String, + val allowSelfSignedCertificate: Boolean = false, + val enableLdapUserSupport: Boolean = false, + val debug: Boolean = false +) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt index 57fa1607..e0c110d1 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt @@ -7,6 +7,7 @@ import org.koin.dsl.module.applicationContext import org.moire.ultrasonic.BuildConfig import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions +import org.moire.ultrasonic.api.subsonic.SubsonicClientConfiguration import org.moire.ultrasonic.cache.PermanentFileStorage import org.moire.ultrasonic.service.CachedMusicService import org.moire.ultrasonic.service.MusicService @@ -68,7 +69,7 @@ fun musicServiceModule(sp: SharedPreferences) = applicationContext { password == null ) { Log.i(LOG_TAG, "Server credentials is not available") - return@bean SubsonicAPIClient( + return@bean SubsonicClientConfiguration( baseUrl = "http://localhost", username = "", password = "", @@ -81,7 +82,7 @@ fun musicServiceModule(sp: SharedPreferences) = applicationContext { debug = BuildConfig.DEBUG ) } else { - return@bean SubsonicAPIClient( + return@bean SubsonicClientConfiguration( baseUrl = serverUrl, username = username, password = password, @@ -96,6 +97,8 @@ fun musicServiceModule(sp: SharedPreferences) = applicationContext { } } + bean { return@bean SubsonicAPIClient(get()) } + bean(name = ONLINE_MUSIC_SERVICE) { return@bean CachedMusicService(RESTMusicService(get(), get())) }