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 new file mode 100644 index 00000000..5fc17035 --- /dev/null +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/GetStreamUrlTest.kt @@ -0,0 +1,47 @@ +package org.moire.ultrasonic.api.subsonic + +import okhttp3.mockwebserver.MockResponse +import org.amshove.kluent.`should equal to` +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions.V1_6_0 +import org.moire.ultrasonic.api.subsonic.rules.MockWebServerRule + +/** + * Integration test for [SubsonicAPIClient.getStreamUrl] method. + */ +class GetStreamUrlTest { + @JvmField @Rule val mockWebServerRule = MockWebServerRule() + + val id = "boom" + private lateinit var client: SubsonicAPIClient + private lateinit var expectedUrl: String + + @Before + fun setUp() { + client = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), + USERNAME, PASSWORD, V1_6_0, CLIENT_ID) + val baseExpectedUrl = mockWebServerRule.mockWebServer.url("").toString() + expectedUrl = "$baseExpectedUrl/rest/stream.view?id=$id&u=$USERNAME" + + "&p=${client.passwordHex}&v=${V1_6_0.restApiVersion}&c=$CLIENT_ID&f=json" + } + + @Test + fun `Should return valid stream url`() { + mockWebServerRule.enqueueResponse("ping_ok.json") + + val streamUrl = client.getStreamUrl(id) + + streamUrl `should equal to` expectedUrl + } + + @Test + fun `Should still return stream url if connection failed`() { + mockWebServerRule.mockWebServer.enqueue(MockResponse().setResponseCode(500)) + + val streamUrl = client.getStreamUrl(id) + + streamUrl `should equal to` expectedUrl + } +} 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 988a165b..8e088d9a 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 @@ -131,6 +131,22 @@ class SubsonicAPIClient(baseUrl: String, } } + /** + * Get stream url. + * + * Calling this method do actual connection to the backend, though not downloading all content. + * + * Consider do not use this method, but [stream] call. + */ + fun getStreamUrl(id: String): String { + val request = api.stream(id).execute() + val url = request.raw().request().url().toString() + if (request.isSuccessful) { + request.body().close() + } + return url + } + private val salt: String by lazy { val secureRandom = SecureRandom() BigInteger(130, secureRandom).toString(32) @@ -145,7 +161,7 @@ class SubsonicAPIClient(baseUrl: String, } } - private val passwordHex: String by lazy { + internal val passwordHex: String by lazy { "enc:${password.toHexBytes()}" } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java index f9ed973d..f6fe612e 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java @@ -104,6 +104,8 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import kotlin.Pair; import retrofit2.Response; @@ -739,12 +741,30 @@ public class RESTMusicService implements MusicService { } @Override - public String getVideoUrl(Context context, String id, boolean useFlash) throws Exception { + public String getVideoUrl(final Context context, + final String id, + final boolean useFlash) throws Exception { // This method should not exists as video should be loaded using stream method // Previous method implementation uses assumption that video will be available // by videoPlayer.view?id=&maxBitRate=500&autoplay=true, but this url is not // official Subsonic API call. - throw new UnsupportedOperationException("This method is not longer supported"); + if (id == null) { + throw new IllegalArgumentException("Id is null"); + } + final String[] expectedResult = new String[1]; + expectedResult[0] = null; + final CountDownLatch latch = new CountDownLatch(1); + + new Thread(new Runnable() { + @Override + public void run() { + expectedResult[0] = subsonicAPIClient.getStreamUrl(id) + "&format=raw"; + latch.countDown(); + } + }, "Get-Video-Url").start(); + + latch.await(3, TimeUnit.SECONDS); + return expectedResult[0]; } @Override