From ca2bfbf14b88b428b77d6c38c82aafa13452e620 Mon Sep 17 00:00:00 2001 From: Nite Date: Thu, 27 Aug 2020 10:27:25 +0200 Subject: [PATCH] Updated Subsonic API version handling --- .../api/subsonic/SubsonicAPIClientTest.kt | 2 +- ...VersionAwareJacksonConverterFactoryTest.kt | 54 ++++++++++++ .../interceptors/VersionInterceptorTest.kt | 56 +----------- .../api/subsonic/ApiNotSupportedException.kt | 11 ++- .../api/subsonic/SubsonicAPIClient.kt | 13 +-- .../api/subsonic/SubsonicAPIVersions.kt | 64 +++++++++----- .../VersionAwareJacksonConverterFactory.kt | 87 +++++++++++++++++++ .../interceptors/VersionInterceptor.kt | 57 +----------- .../api/subsonic/SubsonicAPIVersionsTest.kt | 4 +- .../moire/ultrasonic/util/BackgroundTask.java | 6 +- .../moire/ultrasonic/di/MusicServiceModule.kt | 4 +- ultrasonic/src/main/res/values-de/strings.xml | 1 + ultrasonic/src/main/res/values-es/strings.xml | 1 + ultrasonic/src/main/res/values-fr/strings.xml | 1 + ultrasonic/src/main/res/values-hu/strings.xml | 1 + ultrasonic/src/main/res/values-nl/strings.xml | 1 + ultrasonic/src/main/res/values-pl/strings.xml | 1 + .../src/main/res/values-pt-rBR/strings.xml | 1 + ultrasonic/src/main/res/values-pt/strings.xml | 1 + ultrasonic/src/main/res/values/strings.xml | 1 + 20 files changed, 221 insertions(+), 146 deletions(-) create mode 100644 core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/VersionAwareJacksonConverterFactoryTest.kt create mode 100644 core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/VersionAwareJacksonConverterFactory.kt diff --git a/core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt b/core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt index d82c34c2..8fcacecb 100644 --- a/core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt +++ b/core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt @@ -14,7 +14,7 @@ abstract class SubsonicAPIClientTest { protected lateinit var client: SubsonicAPIClient @Before - fun setUp() { + open fun setUp() { config = SubsonicClientConfiguration( mockWebServerRule.mockWebServer.url("/").toString(), USERNAME, diff --git a/core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/VersionAwareJacksonConverterFactoryTest.kt b/core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/VersionAwareJacksonConverterFactoryTest.kt new file mode 100644 index 00000000..0f313243 --- /dev/null +++ b/core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/VersionAwareJacksonConverterFactoryTest.kt @@ -0,0 +1,54 @@ +package org.moire.ultrasonic.api.subsonic + +import com.fasterxml.jackson.databind.ObjectMapper +import okhttp3.mockwebserver.MockResponse +import org.amshove.kluent.`should be` +import org.junit.Before +import org.junit.Test + +/** + * Integration test for [VersionAwareJacksonConverterFactory]. + */ +class VersionAwareJacksonConverterFactoryTest : SubsonicAPIClientTest() { + private val initialProtocolVersion = SubsonicAPIVersions.V1_1_0 + private var updatedProtocolVersion = SubsonicAPIVersions.V1_1_0 + + @Before + override fun setUp() { + config = SubsonicClientConfiguration( + mockWebServerRule.mockWebServer.url("/").toString(), + USERNAME, + PASSWORD, + initialProtocolVersion, + CLIENT_ID + ) + client = SubsonicAPIClient(config) + } + + @Test + fun `Should update version from response`() { + mockWebServerRule.enqueueResponse("ping_ok.json") + + client.api.ping().execute() + + client.protocolVersion.`should be`(SubsonicAPIVersions.V1_13_0) + } + + @Test + fun `Should update version from response with utf-8 bom`() { + mockWebServerRule.enqueueResponse("ping_ok_utf8_bom.json") + + client.api.ping().execute() + + client.protocolVersion.`should be`(SubsonicAPIVersions.V1_16_0) + } + + @Test + fun `Should not update version if response json doesn't contain version`() { + mockWebServerRule.enqueueResponse("non_subsonic_response.json") + + client.api.stream("1234").execute() + + client.protocolVersion.`should be`(initialProtocolVersion) + } +} \ No newline at end of file diff --git a/core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptorTest.kt b/core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptorTest.kt index 227eb833..9885a455 100644 --- a/core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptorTest.kt +++ b/core/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptorTest.kt @@ -14,12 +14,9 @@ import org.moire.ultrasonic.api.subsonic.enqueueResponse */ class VersionInterceptorTest : BaseInterceptorTest() { private val initialProtocolVersion = SubsonicAPIVersions.V1_1_0 - private var updatedProtocolVersion = SubsonicAPIVersions.V1_1_0 override val interceptor: Interceptor by lazy(NONE) { - VersionInterceptor(initialProtocolVersion) { - updatedProtocolVersion = it - } + VersionInterceptor(initialProtocolVersion) } @Test @@ -33,55 +30,4 @@ class VersionInterceptorTest : BaseInterceptorTest() { requestLine `should contain` "v=${initialProtocolVersion.restApiVersion}" } - - @Test - fun `Should update version from response`() { - mockWebServerRule.enqueueResponse("ping_ok.json") - - client.newCall(createRequest {}).execute() - - (interceptor as VersionInterceptor) - .protocolVersion `should equal` SubsonicAPIVersions.V1_13_0 - } - - @Test - fun `Should update version from response with utf-8 bom`() { - mockWebServerRule.enqueueResponse("ping_ok_utf8_bom.json") - - client.newCall(createRequest {}).execute() - - (interceptor as VersionInterceptor) - .protocolVersion `should equal` SubsonicAPIVersions.V1_16_0 - } - - @Test - fun `Should not update version if response json doesn't contain version`() { - mockWebServerRule.enqueueResponse("non_subsonic_response.json") - - client.newCall(createRequest {}).execute() - - (interceptor as VersionInterceptor).protocolVersion `should equal` initialProtocolVersion - } - - @Test - fun `Should not update version on non-json response`() { - mockWebServerRule.mockWebServer.enqueue( - MockResponse() - .setBody("asdqwnekjnqwkjen") - .setHeader("Content-Type", "application/octet-stream") - ) - - client.newCall(createRequest {}).execute() - - (interceptor as VersionInterceptor).protocolVersion `should equal` initialProtocolVersion - } - - @Test - fun `Should notify notifier on version change`() { - mockWebServerRule.enqueueResponse("ping_ok.json") - - client.newCall(createRequest {}).execute() - - updatedProtocolVersion `should equal` SubsonicAPIVersions.V1_13_0 - } } diff --git a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/ApiNotSupportedException.kt b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/ApiNotSupportedException.kt index a7191a3a..d0f591ca 100644 --- a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/ApiNotSupportedException.kt +++ b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/ApiNotSupportedException.kt @@ -6,6 +6,11 @@ import java.io.IOException * Special [IOException] to indicate that called api is not yet supported * by current server api version. */ -class ApiNotSupportedException( - serverApiVersion: SubsonicAPIVersions -) : IOException("Server api $serverApiVersion does not support this call") +class ApiNotSupportedException : IOException { + val serverApiVersion: String + constructor( + apiVersion: SubsonicAPIVersions + ) : super("Server api $apiVersion does not support this call") { + serverApiVersion = apiVersion.restApiVersion + } +} diff --git a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt index d9ef55f2..0d7e097e 100644 --- a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt +++ b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt @@ -21,7 +21,6 @@ import org.moire.ultrasonic.api.subsonic.response.StreamResponse import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse import retrofit2.Response import retrofit2.Retrofit -import retrofit2.converter.jackson.JacksonConverterFactory private const val READ_TIMEOUT = 60_000L @@ -39,9 +38,7 @@ class SubsonicAPIClient( config: SubsonicClientConfiguration, baseOkClient: OkHttpClient = OkHttpClient.Builder().build() ) { - private val versionInterceptor = VersionInterceptor(config.minimalProtocolVersion) { - protocolVersion = it - } + private val versionInterceptor = VersionInterceptor(config.minimalProtocolVersion) private val proxyPasswordInterceptor = ProxyPasswordInterceptor( config.minimalProtocolVersion, @@ -58,6 +55,7 @@ class SubsonicAPIClient( field = value proxyPasswordInterceptor.apiVersion = field wrappedApi.currentApiVersion = field + versionInterceptor.protocolVersion = field } private val okHttpClient = baseOkClient.newBuilder() @@ -87,7 +85,12 @@ class SubsonicAPIClient( private val retrofit = Retrofit.Builder() .baseUrl("${config.baseUrl}/rest/") .client(okHttpClient) - .addConverterFactory(JacksonConverterFactory.create(jacksonMapper)) + .addConverterFactory( + VersionAwareJacksonConverterFactory.create( + { protocolVersion = it }, + jacksonMapper + ) + ) .build() private val wrappedApi = ApiVersionCheckWrapper( diff --git a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersions.kt b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersions.kt index 9b55160f..5e4a6146 100644 --- a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersions.kt +++ b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersions.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.JsonDeserializer import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import java.lang.NumberFormatException /** * Subsonic REST API versions. @@ -31,28 +32,45 @@ enum class SubsonicAPIVersions(val subsonicVersions: String, val restApiVersion: companion object { @JvmStatic @Throws(IllegalArgumentException::class) - fun fromApiVersion(apiVersion: String): SubsonicAPIVersions { - when (apiVersion) { - "1.1.0" -> return V1_1_0 - "1.1.1" -> return V1_1_1 - "1.2.0" -> return V1_2_0 - "1.3.0" -> return V1_3_0 - "1.4.0" -> return V1_4_0 - "1.5.0" -> return V1_5_0 - "1.6.0" -> return V1_6_0 - "1.7.0" -> return V1_7_0 - "1.8.0" -> return V1_8_0 - "1.9.0" -> return V1_9_0 - "1.10.2" -> return V1_10_2 - "1.10.5" -> return V1_10_2 // Non standard version of Madsonic Server 5.1 - "1.11.0" -> return V1_11_0 - "1.12.0" -> return V1_12_0 - "1.13.0" -> return V1_13_0 - "1.14.0" -> return V1_14_0 - "1.15.0" -> return V1_15_0 - "1.16.0" -> return V1_16_0 - "1.16.1" -> return V1_16_0 // Fast and dirty fix to Subsonic 6.1.4 - else -> throw IllegalArgumentException("Unknown api version $apiVersion") + fun getClosestKnownClientApiVersion(apiVersion: String): SubsonicAPIVersions { + val versionComponents = apiVersion.split(".") + if (versionComponents.size < 2) + throw IllegalArgumentException("Unknown api version $apiVersion") + + try { + val majorVersion = versionComponents[0].toInt() + val minorVersion = versionComponents[1].toInt() + val patchVersion = if (versionComponents.size > 2) versionComponents[2].toInt() + else 0 + + when (majorVersion) { + 1 -> when { + minorVersion < 1 -> + throw IllegalArgumentException("Unknown api version $apiVersion") + minorVersion < 2 && patchVersion < 1 -> return V1_1_0 + minorVersion < 2 -> return V1_1_1 + minorVersion < 3 -> return V1_2_0 + minorVersion < 4 -> return V1_3_0 + minorVersion < 5 -> return V1_4_0 + minorVersion < 6 -> return V1_5_0 + minorVersion < 7 -> return V1_6_0 + minorVersion < 8 -> return V1_7_0 + minorVersion < 9 -> return V1_8_0 + minorVersion < 10 -> return V1_9_0 + minorVersion < 11 -> return V1_10_2 + minorVersion < 12 -> return V1_11_0 + minorVersion < 13 -> return V1_12_0 + minorVersion < 14 -> return V1_13_0 + minorVersion < 15 -> return V1_14_0 + minorVersion < 16 -> return V1_15_0 + else -> return V1_16_0 + } + // Subsonic API specifies that the client's and the server's major API version + // must be the same + else -> throw IllegalArgumentException("Unknown api version $apiVersion") + } + } catch (exception: NumberFormatException) { + throw IllegalArgumentException("Malformed api version $apiVersion") } } @@ -64,7 +82,7 @@ enum class SubsonicAPIVersions(val subsonicVersions: String, val restApiVersion: if (p.currentName != "version") { throw JsonParseException(p, "Not valid token for API version!") } - return fromApiVersion(p.text) + return getClosestKnownClientApiVersion(p.text) } } } diff --git a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/VersionAwareJacksonConverterFactory.kt b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/VersionAwareJacksonConverterFactory.kt new file mode 100644 index 00000000..347e6864 --- /dev/null +++ b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/VersionAwareJacksonConverterFactory.kt @@ -0,0 +1,87 @@ +package org.moire.ultrasonic.api.subsonic + +import com.fasterxml.jackson.databind.JavaType +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.ObjectReader +import java.lang.reflect.Type +import okhttp3.RequestBody +import okhttp3.ResponseBody +import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse +import retrofit2.Converter +import retrofit2.Retrofit +import retrofit2.converter.jackson.JacksonConverterFactory + +/** + * Retrofit Converter Factory which uses Jackson for conversion and maintains the + * version of the Subsonic API. + * @param notifier: callback function to call when the Subsonic API version changes + */ +class VersionAwareJacksonConverterFactory( + private val notifier: (SubsonicAPIVersions) -> Unit = {} +) : Converter.Factory() { + + constructor( + notifier: (SubsonicAPIVersions) -> Unit = {}, + mapper: ObjectMapper + ) : this(notifier) { + this.mapper = mapper + jacksonConverterFactory = JacksonConverterFactory.create(mapper) + } + + private var mapper: ObjectMapper? = null + private var jacksonConverterFactory: JacksonConverterFactory? = null + + override fun responseBodyConverter( + type: Type, + annotations: Array, + retrofit: Retrofit + ): Converter? { + val javaType: JavaType = mapper!!.typeFactory.constructType(type) + val reader: ObjectReader? = mapper!!.readerFor(javaType) + return VersionAwareResponseBodyConverter(notifier, reader!!) + } + + override fun requestBodyConverter( + type: Type, + parameterAnnotations: Array, + methodAnnotations: Array, + retrofit: Retrofit + ): Converter<*, RequestBody>? { + return jacksonConverterFactory?.requestBodyConverter( + type, parameterAnnotations, methodAnnotations, retrofit + ) + } + + companion object { + @JvmOverloads // Guarding public API nullability. + fun create( + notifier: (SubsonicAPIVersions) -> Unit = {}, + mapper: ObjectMapper? = ObjectMapper() + ): VersionAwareJacksonConverterFactory { + if (mapper == null) throw NullPointerException("mapper == null") + return VersionAwareJacksonConverterFactory(notifier, mapper) + } + } + + class VersionAwareResponseBodyConverter ( + private val notifier: (SubsonicAPIVersions) -> Unit = {}, + private val adapter: ObjectReader + ) : Converter { + override fun convert(value: ResponseBody): T { + value.use { + // The response stream contains the version of the API for parsing the stream + // to an object. Currently the parsing is independent from the version as new + // versions only contain extra optional fields. + val response: T = adapter.readValue(value.charStream()) + if (response is SubsonicResponse) { + try { + notifier(response.version) + } catch (e: IllegalArgumentException) { + // no-op + } + } + return response + } + } + } +} diff --git a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptor.kt b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptor.kt index 864aaf58..8cffdda8 100644 --- a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptor.kt +++ b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptor.kt @@ -1,29 +1,16 @@ package org.moire.ultrasonic.api.subsonic.interceptors -import com.fasterxml.jackson.core.JsonFactory -import com.fasterxml.jackson.core.JsonParseException -import com.fasterxml.jackson.core.JsonToken -import java.io.IOException import okhttp3.Interceptor import okhttp3.Interceptor.Chain -import okhttp3.Response import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions -private const val DEFAULT_PEEK_BYTE_COUNT = 1000L - /** - * Special [Interceptor] that adds client supported version to request and tries to update it - * from server response. - * - * Optionally [notifier] will be invoked on version change. - * + * Special [Interceptor] that adds client supported version to request * @author Yahor Berdnikau */ internal class VersionInterceptor( - internal var protocolVersion: SubsonicAPIVersions, - private val notifier: (SubsonicAPIVersions) -> Unit = {} + internal var protocolVersion: SubsonicAPIVersions ) : Interceptor { - private val jsonFactory = JsonFactory() override fun intercept(chain: Chain): okhttp3.Response { val originalRequest = chain.request() @@ -38,44 +25,6 @@ internal class VersionInterceptor( ) .build() - val response = chain.proceed(newRequest) - if (response.isSuccessful) { - val isJson = response.body()?.contentType()?.subtype()?.equals("json", true) ?: false - if (isJson) { - tryUpdateProtocolVersion(response) - } - } - - return response - } - - private fun tryUpdateProtocolVersion(response: Response) { - val content = response.peekBody(DEFAULT_PEEK_BYTE_COUNT).byteStream() - - try { - val jsonReader = jsonFactory.createParser(content) - jsonReader.nextToken() - if (jsonReader.currentToken == JsonToken.START_OBJECT) { - while ( - jsonReader.currentName != "version" && - jsonReader.currentToken != null - ) { - jsonReader.nextToken() - } - val versionStr = jsonReader.nextTextValue() - if (versionStr != null) { - try { - protocolVersion = SubsonicAPIVersions.fromApiVersion(versionStr) - notifier(protocolVersion) - } catch (e: IllegalArgumentException) { - // no-op - } - } - } - } catch (io: IOException) { - // no-op - } catch (parse: JsonParseException) { - // no-op - } + return chain.proceed(newRequest) } } diff --git a/core/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersionsTest.kt b/core/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersionsTest.kt index d6159ff9..0e2ed81f 100644 --- a/core/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersionsTest.kt +++ b/core/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersionsTest.kt @@ -18,11 +18,11 @@ class SubsonicAPIVersionsTest(private val apiVersion: SubsonicAPIVersions) { @Test fun `Should proper convert api version to enum`() { - SubsonicAPIVersions.fromApiVersion(apiVersion.restApiVersion) `should equal` apiVersion + SubsonicAPIVersions.getClosestKnownClientApiVersion(apiVersion.restApiVersion) `should equal` apiVersion } @Test(expected = IllegalArgumentException::class) fun `Should throw IllegalArgumentException for unknown api version`() { - SubsonicAPIVersions.fromApiVersion(apiVersion.restApiVersion.substring(0, 2)) + SubsonicAPIVersions.getClosestKnownClientApiVersion(apiVersion.restApiVersion.substring(0, 2)) } } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java index 444ebb95..bba57c3f 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java @@ -23,6 +23,7 @@ import android.os.Handler; import android.util.Log; import com.fasterxml.jackson.core.JsonParseException; import org.moire.ultrasonic.R; +import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException; import org.moire.ultrasonic.service.SubsonicRESTException; import org.moire.ultrasonic.subsonic.RestErrorMapper; @@ -86,7 +87,10 @@ public abstract class BackgroundTask implements ProgressListener } else { return activity.getResources().getString(R.string.background_task_ssl_error); } - } else if (error instanceof IOException) { + } else if (error instanceof ApiNotSupportedException) { + return activity.getResources().getString(R.string.background_task_unsupported_api, + ((ApiNotSupportedException) error).getServerApiVersion()); + } else if (error instanceof IOException) { return activity.getResources().getString(R.string.background_task_network_error); } else if (error instanceof SubsonicRESTException) { return RestErrorMapper.getLocalizedErrorMessage((SubsonicRESTException) error, activity); 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 193e34d9..69f38a86 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt @@ -78,7 +78,7 @@ val musicServiceModule = module(MUSIC_SERVICE_CONTEXT) { baseUrl = "http://localhost", username = "", password = "", - minimalProtocolVersion = SubsonicAPIVersions.fromApiVersion( + minimalProtocolVersion = SubsonicAPIVersions.getClosestKnownClientApiVersion( Constants.REST_PROTOCOL_VERSION ), clientID = Constants.REST_CLIENT_ID, @@ -91,7 +91,7 @@ val musicServiceModule = module(MUSIC_SERVICE_CONTEXT) { baseUrl = serverUrl, username = username, password = password, - minimalProtocolVersion = SubsonicAPIVersions.fromApiVersion( + minimalProtocolVersion = SubsonicAPIVersions.getClosestKnownClientApiVersion( Constants.REST_PROTOCOL_VERSION ), clientID = Constants.REST_CLIENT_ID, diff --git a/ultrasonic/src/main/res/values-de/strings.xml b/ultrasonic/src/main/res/values-de/strings.xml index b6005fdb..246fbae3 100644 --- a/ultrasonic/src/main/res/values-de/strings.xml +++ b/ultrasonic/src/main/res/values-de/strings.xml @@ -3,6 +3,7 @@ Lade… Ein Netzwerkfehler ist aufgetreten. Bitte die Serveradresse prüfen oder später noch einmal versuchen. + Server api v%1$s does not support this function. Dieses Programm benötigt eine Netzwerkverbindung. Bitte das WLAN oder Mobilfunk einschalten. Ressource nicht gefunden. Bitte die Serveradresse überprüfen. Antwort nicht verstanden. Bitte die Serveradresse überprüfen. diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index 425cd725..b2b2ab6b 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -3,6 +3,7 @@ Cargando… Se ha producido un error de red. Por favor comprueba la dirección del servidor o reinténtalo mas tarde. + Server api v%1$s does not support this function. Este programa requiere acceso a la red. Por favor enciende la Wi-Fi o la red móvil. Recurso no encontrado. Por favor comprueba la dirección del servidor. No se entiende la respuesta. Por favor comprueba la dirección del servidor. diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index 8b41078b..b96db050 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -3,6 +3,7 @@ Chargement… Une erreur réseau est survenue. Veuillez vérifier l\'adresse du serveur ou réessayer plus tard. + Server api v%1$s does not support this function. Cette application requiert un accès au réseau. Veuillez activer le Wi-Fi ou le réseau mobile. Ressources introuvables. Veuillez vérifier l\'adresse du serveur. Réponse incorrecte. Veuillez vérifier l\'adresse du serveur. diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index 57af374b..2059fdab 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -3,6 +3,7 @@ Betöltés… Hálózati hiba történt! Kérjük, ellenőrizze a kiszolgáló címét vagy próbálja később! + A v%1$s verziójú Szerver api nem támogatja ezt a funkciót. Az alkalmazás hálózati hozzáférést igényel. Kérjük, kapcsolja be a Wi-Fi-t vagy a mobilhálózatot! Az erőforrás nem található! Kérjük, ellenőrizze a kiszolgáló címét! Értelmezhetetlen válasz! Kérjük, ellenőrizze a kiszolgáló címét! diff --git a/ultrasonic/src/main/res/values-nl/strings.xml b/ultrasonic/src/main/res/values-nl/strings.xml index ba1727ee..bc3f5a1d 100644 --- a/ultrasonic/src/main/res/values-nl/strings.xml +++ b/ultrasonic/src/main/res/values-nl/strings.xml @@ -3,6 +3,7 @@ Bezig met laden… Er is een netwerkfout opgetreden. Controleer het serveradres of probeer het later opnieuw. + Server api v%1$s does not support this function. Deze app vereist netwerktoegang. Schakel Wi-Fi of mobiel internet in. Bron niet gevonden. Controleer het serveradres. Het antwoord werd niet begrepen. Controleer het serveradres. diff --git a/ultrasonic/src/main/res/values-pl/strings.xml b/ultrasonic/src/main/res/values-pl/strings.xml index 7341257f..66f46d2d 100644 --- a/ultrasonic/src/main/res/values-pl/strings.xml +++ b/ultrasonic/src/main/res/values-pl/strings.xml @@ -3,6 +3,7 @@ Ładowanie… Wystąpił błąd sieci. Proszę sprawdzić adres serwera i spróbować później. + Server api v%1$s does not support this function. Ta aplikacja wymaga dostępu do sieci. Proszę włączyć wi-fi lub dane komórkowe. Nie znaleziono zasobów. Proszę sprawdzić adres serwera. Brak prawidłowej odpowiedzi. Proszę sprawdzić adres serwera. diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index a64b1b05..2ab07c9f 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -3,6 +3,7 @@ Carregando… Ocorreu um erro de rede. Verifique o endereço do servidor ou tente mais tarde. + Server api v%1$s does not support this function. Este aplicativo requer acesso à rede. Ligue o Wi-Fi ou a rede de dados. Recurso não encontrado. Verifique o endereço do servidor. Não entendi a resposta. Verifique o endereço do servidor. diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index 1103e0a8..8e53355d 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -3,6 +3,7 @@ Carregando… Ocorreu um erro de rede. Verifique o endereço do servidor ou tente mais tarde. + Server api v%1$s does not support this function. Este aplicativo requer acesso à rede. Ligue o Wi-Fi ou a rede de dados. Recurso não encontrado. Verifique o endereço do servidor. Não entendi a resposta. Verifique o endereço do servidor. diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index 3569891b..75c418cf 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -3,6 +3,7 @@ Loading… A network error occurred. Please check the server address or try again later. + Server api v%1$s does not support this function. This program requires network access. Please turn on Wi-Fi or mobile network. Resource not found. Please check the server address. Didn\'t understand the reply. Please check the server address.