diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetArtistTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetArtistTest.kt new file mode 100644 index 00000000..b21b53d6 --- /dev/null +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetArtistTest.kt @@ -0,0 +1,54 @@ +package org.moire.ultrasonic.api.subsonic + +import org.amshove.kluent.`should contain` +import org.amshove.kluent.`should equal to` +import org.amshove.kluent.`should equal` +import org.junit.Test +import org.moire.ultrasonic.api.subsonic.models.Album + +/** + * Integration test for [SubsonicAPIClient] for getArtist call. + */ +class SubsonicApiGetArtistTest : SubsonicAPIClientTest() { + @Test + fun `Should parse error call`() { + checkErrorCallParsed(mockWebServerRule, { + client.api.getArtist(101L).execute() + }) + } + + @Test + fun `Should pass id param in request`() { + val id = 929L + mockWebServerRule.enqueueResponse("get_artist_ok.json") + client.api.getArtist(id).execute() + + val request = mockWebServerRule.mockWebServer.takeRequest() + + request.requestLine `should contain` "id=$id" + } + + @Test + fun `Should parse ok response`() { + mockWebServerRule.enqueueResponse("get_artist_ok.json") + + val response = client.api.getArtist(100L).execute() + + assertResponseSuccessful(response) + with(response.body().artist) { + id `should equal to` 362L + name `should equal to` "AC/DC" + coverArt `should equal to` "ar-362" + albumCount `should equal to` 2 + albumsList.size `should equal to` 2 + albumsList[0] `should equal` Album(id = 618L, name = "Black Ice", artist = "AC/DC", + artistId = 362L, coverArt = "al-618", songCount = 15, duration = 3331, + created = parseDate("2016-10-23T15:31:22.000Z"), + year = 2008, genre = "Hard Rock") + albumsList[1] `should equal` Album(id = 617L, name = "Rock or Bust", artist = "AC/DC", + artistId = 362L, coverArt = "al-617", songCount = 11, duration = 2095, + created = parseDate("2016-10-23T15:31:23.000Z"), + year = 2014, genre = "Hard Rock") + } + } +} diff --git a/subsonic-api/src/integrationTest/resources/get_artist_ok.json b/subsonic-api/src/integrationTest/resources/get_artist_ok.json new file mode 100644 index 00000000..4ad4007c --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/get_artist_ok.json @@ -0,0 +1,35 @@ +{ + "subsonic-response" : { + "status" : "ok", + "version" : "1.15.0", + "artist" : { + "id" : "362", + "name" : "AC/DC", + "coverArt" : "ar-362", + "albumCount" : 2, + "album" : [ { + "id" : "618", + "name" : "Black Ice", + "artist" : "AC/DC", + "artistId" : "362", + "coverArt" : "al-618", + "songCount" : 15, + "duration" : 3331, + "created" : "2016-10-23T15:31:22.000Z", + "year" : 2008, + "genre" : "Hard Rock" + }, { + "id" : "617", + "name" : "Rock or Bust", + "artist" : "AC/DC", + "artistId" : "362", + "coverArt" : "al-617", + "songCount" : 11, + "duration" : 2095, + "created" : "2016-10-23T15:31:23.000Z", + "year" : 2014, + "genre" : "Hard Rock" + } ] + } + } +} \ No newline at end of file diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIDefinition.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIDefinition.kt index 95e85307..9de5cab4 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIDefinition.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIDefinition.kt @@ -1,5 +1,6 @@ package org.moire.ultrasonic.api.subsonic +import org.moire.ultrasonic.api.subsonic.response.GetArtistResponse import org.moire.ultrasonic.api.subsonic.response.GetArtistsResponse import org.moire.ultrasonic.api.subsonic.response.GetIndexesResponse import org.moire.ultrasonic.api.subsonic.response.GetMusicDirectoryResponse @@ -44,4 +45,7 @@ interface SubsonicAPIDefinition { fun unstar(@Query("id") id: Long? = null, @Query("albumId") albumId: Long? = null, @Query("artistId") artistId: Long? = null): Call + + @GET("getArtist.view") + fun getArtist(@Query("id") id: Long): Call } diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/Album.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/Album.kt new file mode 100644 index 00000000..3471cb60 --- /dev/null +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/Album.kt @@ -0,0 +1,15 @@ +package org.moire.ultrasonic.api.subsonic.models + +import java.util.Calendar + +data class Album( + val id: Long = -1L, + val name: String = "", + val coverArt: String = "", + val artist: String = "", + val artistId: Long = -1L, + val songCount: Int = 0, + val duration: Int = 0, + val created: Calendar? = null, + val year: Int = 0, + val genre: String = "") diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/Artist.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/Artist.kt index f97b9c21..cbbca431 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/Artist.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/Artist.kt @@ -1,9 +1,12 @@ package org.moire.ultrasonic.api.subsonic.models +import com.fasterxml.jackson.annotation.JsonProperty import java.util.Calendar data class Artist(val id: Long = -1, val name: String = "", val coverArt: String = "", val albumCount: Int = 0, - val starred: Calendar? = null) + val starred: Calendar? = null, + @JsonProperty("album") + val albumsList: List = emptyList()) diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/GetArtistResponse.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/GetArtistResponse.kt new file mode 100644 index 00000000..45b4e1f2 --- /dev/null +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/GetArtistResponse.kt @@ -0,0 +1,11 @@ +package org.moire.ultrasonic.api.subsonic.response + +import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions +import org.moire.ultrasonic.api.subsonic.SubsonicError +import org.moire.ultrasonic.api.subsonic.models.Artist + +class GetArtistResponse(status: Status, + version: SubsonicAPIVersions, + error: SubsonicError?, + val artist: Artist = Artist()) + : SubsonicResponse(status, version, error) 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 03e8d482..d86542fe 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java @@ -56,6 +56,7 @@ import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; import org.moire.ultrasonic.R; import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient; +import org.moire.ultrasonic.api.subsonic.response.GetArtistResponse; import org.moire.ultrasonic.api.subsonic.response.GetArtistsResponse; import org.moire.ultrasonic.api.subsonic.response.GetIndexesResponse; import org.moire.ultrasonic.api.subsonic.response.GetMusicDirectoryResponse; @@ -369,21 +370,23 @@ public class RESTMusicService implements MusicService return APIConverter.toDomainEntity(response.body().getMusicDirectory()); } - @Override - public MusicDirectory getArtist(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception - { - checkServerVersion(context, "1.8", "Artist by ID3 tag not supported."); + @Override + public MusicDirectory getArtist(String id, + String name, + boolean refresh, + Context context, + ProgressListener progressListener) throws Exception { + if (id == null) { + throw new IllegalArgumentException("Id can't be null!"); + } - Reader reader = getReader(context, progressListener, "getArtist", null, "id", id); - try - { - return new MusicDirectoryParser(context).parse(name, reader, progressListener, false); - } - finally - { - Util.close(reader); - } - } + updateProgressListener(progressListener, R.string.parser_reading); + Response response = subsonicAPIClient.getApi() + .getArtist(Long.valueOf(id)).execute(); + checkResponseSuccessful(response); + + return APIConverter.toMusicDirectoryDomainEntity(response.body().getArtist()); + } @Override public MusicDirectory getAlbum(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/SubsonicAPIConverter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/SubsonicAPIConverter.kt index 4afba002..d93b5272 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/SubsonicAPIConverter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/SubsonicAPIConverter.kt @@ -2,6 +2,7 @@ @file:JvmName("APIConverter") package org.moire.ultrasonic.data +import org.moire.ultrasonic.api.subsonic.models.Album import org.moire.ultrasonic.api.subsonic.models.Index import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild import org.moire.ultrasonic.domain.Artist @@ -30,6 +31,24 @@ fun APIArtist.toDomainEntity(): Artist = Artist().apply { name = this@toDomainEntity.name } +fun APIArtist.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().apply { + name = this@toMusicDirectoryDomainEntity.name + addAll(this@toMusicDirectoryDomainEntity.albumsList.map { it.toDomainEntity() }) +} + +fun Album.toDomainEntity(): MusicDirectory.Entry = MusicDirectory.Entry().apply { + id = this@toDomainEntity.id.toString() + title = this@toDomainEntity.name + coverArt = this@toDomainEntity.coverArt + artist = this@toDomainEntity.artist + artistId = this@toDomainEntity.artistId.toString() + songCount = this@toDomainEntity.songCount.toLong() + duration = this@toDomainEntity.duration + created = this@toDomainEntity.created?.time + year = this@toDomainEntity.year + genre = this@toDomainEntity.genre +} + fun MusicDirectoryChild.toDomainEntity(): MusicDirectory.Entry = MusicDirectory.Entry().apply { id = this@toDomainEntity.id.toString() parent = this@toDomainEntity.parent.toString() diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIConverterTest.kt index da2f2fc1..1a7f9465 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIConverterTest.kt @@ -5,6 +5,7 @@ package org.moire.ultrasonic.data import org.amshove.kluent.`should equal to` import org.amshove.kluent.`should equal` import org.junit.Test +import org.moire.ultrasonic.api.subsonic.models.Album import org.moire.ultrasonic.api.subsonic.models.Artist import org.moire.ultrasonic.api.subsonic.models.Index import org.moire.ultrasonic.api.subsonic.models.Indexes @@ -18,6 +19,7 @@ import java.util.Calendar * * @author Yahor Berdnikau */ +@Suppress("TooManyFunctions") class APIConverterTest { @Test fun `Should convert MusicFolder entity`() { @@ -137,6 +139,43 @@ class APIConverterTest { } } + @Test + fun `Should convert Artist entity to domain MusicDirectory entity`() { + val entity = Artist(id = 101L, name = "artist-name", coverArt = "some-art", albumCount = 10, + albumsList = listOf(Album(id = 562L, name = "some-name", coverArt = "zzz", + artist = "artist-name", artistId = 256L, songCount = 10, duration = 345, + created = Calendar.getInstance(), year = 2011, genre = "Math Rock"))) + + val convertedEntity = entity.toMusicDirectoryDomainEntity() + + with(convertedEntity) { + name `should equal to` entity.name + children `should equal` entity.albumsList.map { it.toDomainEntity() }.toMutableList() + } + } + + @Test + fun `Should convert Album to domain entity`() { + val entity = Album(id = 387L, name = "some-name", coverArt = "asdas", artist = "some-artist", + artistId = 390L, songCount = 12, duration = 841, created = Calendar.getInstance(), + year = 2017, genre = "some-genre") + + val convertedEntity = entity.toDomainEntity() + + with(convertedEntity) { + id `should equal to` entity.id.toString() + title `should equal to` entity.name + coverArt `should equal to` entity.coverArt + artist `should equal to` entity.artist + artistId `should equal to` entity.artistId.toString() + songCount `should equal to` entity.songCount.toLong() + duration `should equal to` entity.duration + created `should equal` entity.created?.time + year `should equal to` entity.year + genre `should equal to` entity.genre + } + } + private fun createMusicFolder(id: Long = 0, name: String = ""): MusicFolder = MusicFolder(id, name)