mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-18 02:02:23 +03:00
Add stream call.
Also introduced helper method in SubsonicApiClient that handles error cases. Signed-off-by: Yahor Berdnikau <egorr.berd@gmail.com>
This commit is contained in:
parent
c8c8766b55
commit
ba412721ac
@ -46,7 +46,7 @@ fun parseDate(dateAsString: String): Calendar {
|
||||
return result
|
||||
}
|
||||
|
||||
fun <T: SubsonicResponse> checkErrorCallParsed(mockWebServerRule: MockWebServerRule,
|
||||
fun <T: SubsonicResponse> checkErrorCallParsed(mockWebServerRule : MockWebServerRule,
|
||||
apiRequest: () -> Response<T>): T {
|
||||
mockWebServerRule.enqueueResponse("generic_error_response.json")
|
||||
|
||||
|
@ -19,7 +19,7 @@ class SubsonicApiGetCoverArtTest : SubsonicAPIClientTest() {
|
||||
|
||||
with(response) {
|
||||
stream `should be` null
|
||||
requestErrorCode `should be` null
|
||||
responseHttpCode `should equal to` 200
|
||||
apiError `should equal` SubsonicError.GENERIC
|
||||
}
|
||||
}
|
||||
@ -33,7 +33,7 @@ class SubsonicApiGetCoverArtTest : SubsonicAPIClientTest() {
|
||||
|
||||
with(response) {
|
||||
stream `should be` null
|
||||
requestErrorCode `should equal` 404
|
||||
responseHttpCode `should equal` 404
|
||||
apiError `should be` null
|
||||
}
|
||||
}
|
||||
@ -46,7 +46,7 @@ class SubsonicApiGetCoverArtTest : SubsonicAPIClientTest() {
|
||||
val response = client.getCoverArt("some-id")
|
||||
|
||||
with(response) {
|
||||
requestErrorCode `should be` null
|
||||
responseHttpCode `should equal to` 200
|
||||
apiError `should be` null
|
||||
stream `should not be` null
|
||||
val expectedContent = mockWebServerRule.loadJsonResponse("ping_ok.json")
|
||||
|
@ -0,0 +1,125 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for [SubsonicAPIDefinition.stream] call.
|
||||
*/
|
||||
class SubsonicApiStreamTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle api error response`() {
|
||||
mockWebServerRule.enqueueResponse("generic_error_response.json")
|
||||
|
||||
val response = client.stream("some-id")
|
||||
|
||||
with(response) {
|
||||
stream `should be` null
|
||||
responseHttpCode `should equal to` 200
|
||||
apiError `should equal` SubsonicError.GENERIC
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle server error`() {
|
||||
val httpErrorCode = 404
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse().setResponseCode(httpErrorCode))
|
||||
|
||||
val response = client.stream("some-id")
|
||||
|
||||
with(response) {
|
||||
stream `should be` null
|
||||
responseHttpCode `should equal to` httpErrorCode
|
||||
apiError `should be` null
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should return successfull call stream`() {
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse()
|
||||
.setBody(mockWebServerRule.loadJsonResponse("ping_ok.json")))
|
||||
|
||||
val response = client.stream("some-id")
|
||||
|
||||
with(response) {
|
||||
responseHttpCode `should equal to` 200
|
||||
apiError `should be` null
|
||||
stream `should not be` null
|
||||
val expectedContent = mockWebServerRule.loadJsonResponse("ping_ok.json")
|
||||
stream!!.bufferedReader().readText() `should equal to` expectedContent
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id as parameter`() {
|
||||
val id = "asdo123"
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json", id) {
|
||||
client.api.stream(id = id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass max bit rate as param`() {
|
||||
val maxBitRate = 360
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"maxBitRate=$maxBitRate") {
|
||||
client.api.stream("some-id", maxBitRate = maxBitRate).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass format as param`() {
|
||||
val format = "aac"
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"format=$format") {
|
||||
client.api.stream("some-id", format = format).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass time offset as param`() {
|
||||
val timeOffset = 155
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"timeOffset=$timeOffset") {
|
||||
client.api.stream("some-id", timeOffset = timeOffset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass video size as param`() {
|
||||
val videoSize = "44144"
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"size=$videoSize") {
|
||||
client.api.stream("some-id", videoSize = videoSize).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass estimate content length as param`() {
|
||||
val estimateContentLength = true
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"estimateContentLength=$estimateContentLength") {
|
||||
client.api.stream("some-id", estimateContentLength = estimateContentLength).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass converted as param`() {
|
||||
val converted = false
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"converted=$converted") {
|
||||
client.api.stream("some-id", converted = converted).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -83,6 +83,20 @@ class SubsonicAPIClient(baseUrl: String,
|
||||
api.getCoverArt(id, size).execute()
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient method to get media stream from api using item [id] and optional [maxBitrate].
|
||||
*
|
||||
* Optionally also you can provide [offset] that stream should start from.
|
||||
*
|
||||
* It detects the response `Content-Type` and tries to parse subsonic error if there is one.
|
||||
*
|
||||
* Prefer this method over [SubsonicAPIDefinition.stream] as this handles error cases.
|
||||
*/
|
||||
fun stream(id: String, maxBitrate: Int? = null, offset: Long? = null): StreamResponse =
|
||||
handleStreamResponse {
|
||||
api.stream(id, maxBitrate, offset = offset).execute()
|
||||
}
|
||||
|
||||
private inline fun handleStreamResponse(apiCall: () -> Response<ResponseBody>): StreamResponse {
|
||||
val response = apiCall()
|
||||
return if (response.isSuccessful) {
|
||||
@ -92,12 +106,13 @@ class SubsonicAPIClient(baseUrl: String,
|
||||
contentType.type().equals("application", true) &&
|
||||
contentType.subtype().equals("json", true)) {
|
||||
val error = jacksonMapper.readValue<SubsonicResponse>(responseBody.byteStream())
|
||||
StreamResponse(apiError = error.error)
|
||||
StreamResponse(apiError = error.error, responseHttpCode = response.code())
|
||||
} else {
|
||||
StreamResponse(stream = responseBody.byteStream())
|
||||
StreamResponse(stream = responseBody.byteStream(),
|
||||
responseHttpCode = response.code())
|
||||
}
|
||||
} else {
|
||||
StreamResponse(requestErrorCode = response.code())
|
||||
StreamResponse(responseHttpCode = response.code())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ import org.moire.ultrasonic.api.subsonic.response.SearchTwoResponse
|
||||
import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Query
|
||||
import retrofit2.http.Streaming
|
||||
|
||||
@ -167,4 +168,15 @@ interface SubsonicAPIDefinition {
|
||||
@GET("getCoverArt.view")
|
||||
fun getCoverArt(@Query("id") id: String,
|
||||
@Query("size") size: Long? = null): Call<ResponseBody>
|
||||
|
||||
@Streaming
|
||||
@GET("stream.view")
|
||||
fun stream(@Query("id") id: String,
|
||||
@Query("maxBitRate") maxBitRate: Int? = null,
|
||||
@Query("format") format: String? = null,
|
||||
@Query("timeOffset") timeOffset: Int? = null,
|
||||
@Query("size") videoSize: String? = null,
|
||||
@Query("estimateContentLength") estimateContentLength: Boolean? = null,
|
||||
@Query("converted") converted: Boolean? = null,
|
||||
@Header("Range") offset: Long? = null): Call<ResponseBody>
|
||||
}
|
||||
|
@ -5,15 +5,15 @@ import java.io.InputStream
|
||||
|
||||
/**
|
||||
* Special response that contains either [stream] of data from api, or [apiError],
|
||||
* or [requestErrorCode].
|
||||
* or [responseHttpCode].
|
||||
*
|
||||
* [requestErrorCode] will be only if there problem on http level.
|
||||
* [responseHttpCode] will be there always.
|
||||
*/
|
||||
class StreamResponse(val stream: InputStream? = null,
|
||||
val apiError: SubsonicError? = null,
|
||||
val requestErrorCode: Int? = null) {
|
||||
val responseHttpCode: Int) {
|
||||
/**
|
||||
* Check if this response has error.
|
||||
*/
|
||||
fun hasError(): Boolean = apiError != null || requestErrorCode != null
|
||||
fun hasError(): Boolean = apiError != null || responseHttpCode !in 200..300
|
||||
}
|
||||
|
@ -10,16 +10,26 @@ import org.moire.ultrasonic.api.subsonic.SubsonicError.GENERIC
|
||||
class StreamResponseTest {
|
||||
@Test
|
||||
fun `Should have error if subsonic error is not null`() {
|
||||
StreamResponse(apiError = GENERIC).hasError() `should equal to` true
|
||||
StreamResponse(apiError = GENERIC, responseHttpCode = 200).hasError() `should equal to` true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should have error if http error is not null`() {
|
||||
StreamResponse(requestErrorCode = 500).hasError() `should equal to` true
|
||||
fun `Should have error if http error is greater then 300`() {
|
||||
StreamResponse(responseHttpCode = 301).hasError() `should equal to` true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should not have error if subsonic error and http error is null`() {
|
||||
StreamResponse().hasError() `should equal to` false
|
||||
fun `Should have error of http error code is lower then 200`() {
|
||||
StreamResponse(responseHttpCode = 199).hasError() `should equal to` true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should not have error if http code is 200`() {
|
||||
StreamResponse(responseHttpCode = 200).hasError() `should equal to` false
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should not have error if http code is 300`() {
|
||||
StreamResponse(responseHttpCode = 300).hasError() `should equal to` false
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user