Merge pull request #49 from ultrasonic/add-get-album-list
Add get album list
This commit is contained in:
commit
0a22f7bcc7
|
@ -0,0 +1,109 @@
|
|||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType.BY_GENRE
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration tests for [SubsonicAPIDefinition] for getAlbumList call.
|
||||
*/
|
||||
class SubsonicApiGetAlbumListRequestTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getAlbumList(BY_GENRE).execute()
|
||||
}
|
||||
|
||||
response.albumList `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_album_list_ok.json")
|
||||
|
||||
val response = client.api.getAlbumList(BY_GENRE).execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().albumList) {
|
||||
size `should equal to` 2
|
||||
this[1] `should equal` MusicDirectoryChild(id = 9997, parent = 9996, isDir = true,
|
||||
title = "Endless Forms Most Beautiful", album = "Endless Forms Most Beautiful",
|
||||
artist = "Nightwish", year = 2015, genre = "Symphonic Metal",
|
||||
coverArt = "9997", playCount = 11,
|
||||
created = parseDate("2017-09-02T16:22:49.000Z"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass type in request params`() {
|
||||
val listType = AlbumListType.HIGHEST
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "type=${listType.typeName}") {
|
||||
client.api.getAlbumList(type = listType).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass size in request params`() {
|
||||
val size = 45
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "size=$size") {
|
||||
client.api.getAlbumList(type = BY_GENRE, size = size).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass offset in request params`() {
|
||||
val offset = 3
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "offset=$offset") {
|
||||
client.api.getAlbumList(type = BY_GENRE, offset = offset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass from year in request params`() {
|
||||
val fromYear = 2001
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "fromYear=$fromYear") {
|
||||
client.api.getAlbumList(type = BY_GENRE, fromYear = fromYear).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass to year in request params`() {
|
||||
val toYear = 2017
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "toYear=$toYear") {
|
||||
client.api.getAlbumList(type = BY_GENRE, toYear = toYear).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass genre in request params`() {
|
||||
val genre = "Rock"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "genre=$genre") {
|
||||
client.api.getAlbumList(type = BY_GENRE, genre = genre).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass music folder id in request params`() {
|
||||
val folderId = 545L
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "musicFolderId=$folderId") {
|
||||
client.api.getAlbumList(type = BY_GENRE, musicFolderId = folderId).execute()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"albumList" : {
|
||||
"album" : [ {
|
||||
"id" : "10020",
|
||||
"parent" : "490",
|
||||
"isDir" : true,
|
||||
"title" : "Fury",
|
||||
"album" : "Fury",
|
||||
"artist" : "Sick Puppies",
|
||||
"year" : 2016,
|
||||
"genre" : "Alternative Rock",
|
||||
"coverArt" : "10020",
|
||||
"playCount" : 13,
|
||||
"created" : "2017-09-02T17:34:51.000Z"
|
||||
}, {
|
||||
"id" : "9997",
|
||||
"parent" : "9996",
|
||||
"isDir" : true,
|
||||
"title" : "Endless Forms Most Beautiful",
|
||||
"album" : "Endless Forms Most Beautiful",
|
||||
"artist" : "Nightwish",
|
||||
"year" : 2015,
|
||||
"genre" : "Symphonic Metal",
|
||||
"coverArt" : "9997",
|
||||
"playCount" : 11,
|
||||
"created" : "2017-09-02T16:22:49.000Z"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,6 +54,7 @@ class SubsonicAPIClient(baseUrl: String,
|
|||
|
||||
private val jacksonMapper = ObjectMapper()
|
||||
.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true)
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.registerModule(KotlinModule())
|
||||
|
||||
private val retrofit = Retrofit.Builder()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
|
||||
import org.moire.ultrasonic.api.subsonic.response.GetAlbumListResponse
|
||||
import org.moire.ultrasonic.api.subsonic.response.GetAlbumResponse
|
||||
import org.moire.ultrasonic.api.subsonic.response.GetArtistResponse
|
||||
import org.moire.ultrasonic.api.subsonic.response.GetArtistsResponse
|
||||
|
@ -123,4 +125,13 @@ interface SubsonicAPIDefinition {
|
|||
fun scrobble(@Query("id") id: String,
|
||||
@Query("time") time: Long? = null,
|
||||
@Query("submission") submission: Boolean? = null): Call<SubsonicResponse>
|
||||
|
||||
@GET("getAlbumList.view")
|
||||
fun getAlbumList(@Query("type") type: AlbumListType,
|
||||
@Query("size") size: Int? = null,
|
||||
@Query("offset") offset: Int? = null,
|
||||
@Query("fromYear") fromYear: Int? = null,
|
||||
@Query("toYear") toYear: Int? = null,
|
||||
@Query("genre") genre: String? = null,
|
||||
@Query("musicFolderId") musicFolderId: Long? = null): Call<GetAlbumListResponse>
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package org.moire.ultrasonic.api.subsonic.models
|
||||
|
||||
/**
|
||||
* Type of album list used in [org.moire.ultrasonic.api.subsonic.SubsonicAPIDefinition.getAlbumList]
|
||||
* calls.
|
||||
*
|
||||
* @author Yahor Berdnikau
|
||||
*/
|
||||
enum class AlbumListType(val typeName: String) {
|
||||
RANDOM("random"),
|
||||
NEWEST("newest"),
|
||||
HIGHEST("highest"),
|
||||
FREQUENT("frequent"),
|
||||
RECENT("recent"),
|
||||
SORTED_BY_NAME("alphabeticalByName"),
|
||||
SORTED_BY_ARTIST("alphabeticalByArtist"),
|
||||
STARRED("starred"),
|
||||
BY_YEAR("byYear"),
|
||||
BY_GENRE("byGenre");
|
||||
|
||||
override fun toString(): String {
|
||||
return typeName
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun fromName(typeName: String): AlbumListType = when (typeName) {
|
||||
in RANDOM.typeName -> RANDOM
|
||||
in NEWEST.typeName -> NEWEST
|
||||
in HIGHEST.typeName -> HIGHEST
|
||||
in FREQUENT.typeName -> FREQUENT
|
||||
in RECENT.typeName -> RECENT
|
||||
in SORTED_BY_NAME.typeName -> SORTED_BY_NAME
|
||||
in SORTED_BY_ARTIST.typeName -> SORTED_BY_ARTIST
|
||||
in STARRED.typeName -> STARRED
|
||||
in BY_YEAR.typeName -> BY_YEAR
|
||||
in BY_GENRE.typeName -> BY_GENRE
|
||||
else -> throw IllegalArgumentException("Unknown type: $typeName")
|
||||
}
|
||||
|
||||
private operator fun String.contains(other: String) = this.equals(other, true)
|
||||
}
|
||||
}
|
|
@ -32,4 +32,6 @@ data class MusicDirectoryChild(val id: Long = -1L,
|
|||
val channelId: Long = -1,
|
||||
val description: String = "",
|
||||
val status: String = "",
|
||||
val publishDate: Calendar? = null)
|
||||
val publishDate: Calendar? = null,
|
||||
val userRating: Int? = null,
|
||||
val averageRating: Float? = null)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package org.moire.ultrasonic.api.subsonic.response
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicError
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
class GetAlbumListResponse(status: Status,
|
||||
version: SubsonicAPIVersions,
|
||||
error: SubsonicError?)
|
||||
: SubsonicResponse(status, version, error) {
|
||||
@JsonProperty("albumList") private val albumWrapper = AlbumWrapper()
|
||||
|
||||
val albumList: List<MusicDirectoryChild>
|
||||
get() = albumWrapper.albumList
|
||||
}
|
||||
|
||||
private class AlbumWrapper(
|
||||
@JsonProperty("album") val albumList: List<MusicDirectoryChild> = emptyList())
|
|
@ -0,0 +1,41 @@
|
|||
package org.moire.ultrasonic.api.subsonic.models
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should throw`
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Unit test for [AlbumListType] class.
|
||||
*/
|
||||
class AlbumListTypeTest {
|
||||
@Test
|
||||
fun `Should create type from string ignoring case`() {
|
||||
val type = AlbumListType.SORTED_BY_NAME.typeName.toLowerCase()
|
||||
|
||||
val albumListType = AlbumListType.fromName(type)
|
||||
|
||||
albumListType `should equal` AlbumListType.SORTED_BY_NAME
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should throw IllegalArgumentException for unknown type`() {
|
||||
val failCall = {
|
||||
AlbumListType.fromName("some-not-existing-type")
|
||||
}
|
||||
|
||||
failCall `should throw` IllegalArgumentException::class
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should convert type string to corresponding AlbumListType`() {
|
||||
AlbumListType.values().forEach {
|
||||
AlbumListType.fromName(it.typeName) `should equal` it
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should return type name for toString call`() {
|
||||
AlbumListType.STARRED.typeName `should equal to` AlbumListType.STARRED.toString()
|
||||
}
|
||||
}
|
|
@ -56,7 +56,9 @@ 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.models.AlbumListType;
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild;
|
||||
import org.moire.ultrasonic.api.subsonic.response.GetAlbumListResponse;
|
||||
import org.moire.ultrasonic.api.subsonic.response.GetAlbumResponse;
|
||||
import org.moire.ultrasonic.api.subsonic.response.GetArtistResponse;
|
||||
import org.moire.ultrasonic.api.subsonic.response.GetArtistsResponse;
|
||||
|
@ -653,21 +655,28 @@ public class RESTMusicService implements MusicService
|
|||
checkResponseSuccessful(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception
|
||||
{
|
||||
checkServerVersion(context, "1.2", "Album list not supported.");
|
||||
@Override
|
||||
public MusicDirectory getAlbumList(String type,
|
||||
int size,
|
||||
int offset,
|
||||
Context context,
|
||||
ProgressListener progressListener) throws Exception {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("Type is null!");
|
||||
}
|
||||
|
||||
Reader reader = getReader(context, progressListener, "getAlbumList", null, asList("type", "size", "offset"), Arrays.<Object>asList(type, size, offset));
|
||||
try
|
||||
{
|
||||
return new AlbumListParser(context).parse(reader, progressListener, false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Util.close(reader);
|
||||
}
|
||||
}
|
||||
updateProgressListener(progressListener, R.string.parser_reading);
|
||||
Response<GetAlbumListResponse> response = subsonicAPIClient.getApi()
|
||||
.getAlbumList(AlbumListType.fromName(type), size, offset, null,
|
||||
null, null, null).execute();
|
||||
checkResponseSuccessful(response);
|
||||
|
||||
List<MusicDirectory.Entry> childList = APIMusicDirectoryConverter
|
||||
.toDomainEntityList(response.body().getAlbumList());
|
||||
MusicDirectory result = new MusicDirectory();
|
||||
result.addAll(childList);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MusicDirectory getAlbumList2(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception
|
||||
|
|
|
@ -48,6 +48,8 @@ fun MusicDirectoryChild.toDomainEntity(): MusicDirectory.Entry = MusicDirectory.
|
|||
}
|
||||
}
|
||||
|
||||
fun List<MusicDirectoryChild>.toDomainEntityList() = this.map { it.toDomainEntity() }
|
||||
|
||||
fun APIMusicDirectory.toDomainEntity(): MusicDirectory = MusicDirectory().apply {
|
||||
name = this@toDomainEntity.name
|
||||
addAll(this@toDomainEntity.childList.map { it.toDomainEntity() })
|
||||
|
|
|
@ -71,7 +71,7 @@ class APIMusicDirectoryConverterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `Should convert MusicDirectoryChild podact entity`() {
|
||||
fun `Should convert MusicDirectoryChild podcast entity`() {
|
||||
val entity = MusicDirectoryChild(id = 584, streamId = 394,
|
||||
artist = "some-artist", publishDate = Calendar.getInstance())
|
||||
|
||||
|
@ -82,4 +82,16 @@ class APIMusicDirectoryConverterTest {
|
|||
artist `should equal to` dateFormat.format(entity.publishDate?.time)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should convert list of MusicDirectoryChild to domain entity list`() {
|
||||
val entitiesList = listOf(MusicDirectoryChild(id = 45), MusicDirectoryChild(id = 34))
|
||||
|
||||
val domainList = entitiesList.toDomainEntityList()
|
||||
|
||||
domainList.size `should equal to` entitiesList.size
|
||||
domainList.forEachIndexed { index, entry ->
|
||||
entry `should equal` entitiesList[index].toDomainEntity()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue