diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt index 71f58c48..1ff93800 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt @@ -164,17 +164,18 @@ class SubsonicAPIClientTest { lastModified `should equal` 1491069027523 ignoredArticles `should equal` "The El La Los Las Le Les" shortcutList `should equal` listOf( - Artist(889L, "podcasts", null), - Artist(890L, "audiobooks", null) + Artist(id = 889L, name = "podcasts"), + Artist(id = 890L, name = "audiobooks") ) indexList `should equal` mutableListOf( Index("A", listOf( - Artist(50L, "Ace Of Base", parseDate("2017-04-02T20:16:29.815Z")), - Artist(379L, "A Perfect Circle", null) + Artist(id = 50L, name = "Ace Of Base", + starred = parseDate("2017-04-02T20:16:29.815Z")), + Artist(id = 379L, name = "A Perfect Circle") )), Index("H", listOf( - Artist(299, "Haddaway", null), - Artist(297, "Halestorm", null) + Artist(id = 299, name = "Haddaway"), + Artist(id = 297, name = "Halestorm") )) ) } @@ -271,6 +272,55 @@ class SubsonicAPIClientTest { } } + @Test + fun `Should parse get artists error response`() { + val response = checkErrorCallParsed { client.api.getArtists(null).execute() } + + response.indexes `should not be` null + with(response.indexes) { + lastModified `should equal to` 0 + ignoredArticles `should equal to` "" + indexList.size `should equal to` 0 + shortcutList.size `should equal to` 0 + } + } + + @Test + fun `Should parse get artists ok reponse`() { + enqueueResponse("get_artists_ok.json") + + val response = client.api.getArtists(null).execute() + + assertResponseSuccessful(response) + with(response.body().indexes) { + lastModified `should equal to` 0L + ignoredArticles `should equal to` "The El La Los Las Le Les" + shortcutList `should equal` emptyList() + indexList.size `should equal to` 2 + indexList `should equal` listOf( + Index(name = "A", artists = listOf( + Artist(id = 362L, name = "AC/DC", coverArt = "ar-362", albumCount = 2), + Artist(id = 254L, name = "Acceptance", coverArt = "ar-254", albumCount = 1) + )), + Index(name = "T", artists = listOf( + Artist(id = 516L, name = "Tangerine Dream", coverArt = "ar-516", albumCount = 1), + Artist(id = 242L, name = "Taproot", coverArt = "ar-242", albumCount = 2) + )) + ) + } + } + + @Test + fun `Should pass param on query for get artists call`() { + enqueueResponse("get_artists_ok.json") + val musicFolderId = 101L + client.api.getArtists(musicFolderId).execute() + + val request = mockWebServerRule.mockWebServer.takeRequest() + + request.requestLine `should contain` "musicFolderId=$musicFolderId" + } + private fun enqueueResponse(resourceName: String) { mockWebServerRule.mockWebServer.enqueue(MockResponse() .setBody(loadJsonResponse(resourceName))) diff --git a/subsonic-api/src/integrationTest/resources/get_artists_ok.json b/subsonic-api/src/integrationTest/resources/get_artists_ok.json new file mode 100644 index 00000000..6adb74bc --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/get_artists_ok.json @@ -0,0 +1,43 @@ +{ + "subsonic-response": { + "status": "ok", + "version": "1.15.0", + "artists": { + "ignoredArticles": "The El La Los Las Le Les", + "index": [ + { + "name": "A", + "artist": [ + { + "id": "362", + "name": "AC/DC", + "coverArt": "ar-362", + "albumCount": 2 + }, + { + "id": "254", + "name": "Acceptance", + "coverArt": "ar-254", + "albumCount": 1 + } + ] + }, { + "name": "T", + "artist": [ + { + "id": "516", + "name": "Tangerine Dream", + "coverArt": "ar-516", + "albumCount": 1 + }, + { + "id": "242", + "name": "Taproot", + "coverArt": "ar-242", + "albumCount": 2 + } + ] + } ] + } + } +} \ 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 59d06f6c..fb6c3789 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.GetArtistsResponse import org.moire.ultrasonic.api.subsonic.response.GetIndexesResponse import org.moire.ultrasonic.api.subsonic.response.GetMusicDirectoryResponse import org.moire.ultrasonic.api.subsonic.response.LicenseResponse @@ -30,4 +31,7 @@ interface SubsonicAPIDefinition { @GET("getMusicDirectory.view") fun getMusicDirectory(@Query("id") id: Long): Call + + @GET("getArtists.view") + fun getArtists(@Query("musicFolderId") musicFolderId: Long?): Call } 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 f0388e09..f97b9c21 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 @@ -4,4 +4,6 @@ import java.util.Calendar data class Artist(val id: Long = -1, val name: String = "", - val starred: Calendar?) + val coverArt: String = "", + val albumCount: Int = 0, + val starred: Calendar? = null) diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/GetArtistsResponse.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/GetArtistsResponse.kt new file mode 100644 index 00000000..76337483 --- /dev/null +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/GetArtistsResponse.kt @@ -0,0 +1,12 @@ +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.Indexes + +class GetArtistsResponse(status: Status, + version: SubsonicAPIVersions, + error: SubsonicError?, + @JsonProperty("artists") val indexes: Indexes = Indexes()) : + 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 daa4826f..4d31685d 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.GetArtistsResponse; import org.moire.ultrasonic.api.subsonic.response.GetIndexesResponse; import org.moire.ultrasonic.api.subsonic.response.LicenseResponse; import org.moire.ultrasonic.api.subsonic.response.MusicFoldersResponse; @@ -81,7 +82,6 @@ import org.moire.ultrasonic.service.parser.BookmarkParser; import org.moire.ultrasonic.service.parser.ChatMessageParser; import org.moire.ultrasonic.service.parser.ErrorParser; import org.moire.ultrasonic.service.parser.GenreParser; -import org.moire.ultrasonic.service.parser.IndexesParser; import org.moire.ultrasonic.service.parser.JukeboxStatusParser; import org.moire.ultrasonic.service.parser.LyricsParser; import org.moire.ultrasonic.service.parser.MusicDirectoryParser; @@ -234,6 +234,21 @@ public class RESTMusicService implements MusicService return musicFolders; } + private static List readCachedMusicFolders(Context context) { + String filename = getCachedMusicFoldersFilename(context); + return FileUtil.deserialize(context, filename); + } + + private static void writeCachedMusicFolders(Context context, List musicFolders) { + String filename = getCachedMusicFoldersFilename(context); + FileUtil.serialize(context, new ArrayList<>(musicFolders), filename); + } + + private static String getCachedMusicFoldersFilename(Context context) { + String s = Util.getRestUrl(context, null); + return String.format(Locale.US, "musicFolders-%d.ser", Math.abs(s.hashCode())); + } + @Override public Indexes getIndexes(String musicFolderId, boolean refresh, @@ -269,68 +284,38 @@ public class RESTMusicService implements MusicService return String.format(Locale.US, "indexes-%d.ser", Math.abs(s.hashCode())); } - @Override - public Indexes getArtists(boolean refresh, Context context, ProgressListener progressListener) throws Exception - { - checkServerVersion(context, "1.8", "Artists by ID3 tag not supported."); + @Override + public Indexes getArtists(boolean refresh, + Context context, + ProgressListener progressListener) throws Exception { + Indexes cachedArtists = readCachedArtists(context); + if (cachedArtists != null && + !refresh) { + return cachedArtists; + } - Indexes cachedArtists = readCachedArtists(context); - if (cachedArtists != null && !refresh) - { - return cachedArtists; - } + Response response = subsonicAPIClient.getApi().getArtists(null).execute(); + checkResponseSuccessful(response); - Reader reader = getReader(context, progressListener, "getArtists", null); - try - { - Indexes indexes = new IndexesParser(context).parse(reader, progressListener); - if (indexes != null) - { - writeCachedArtists(context, indexes); - return indexes; - } + Indexes indexes = APIConverter.toDomainEntity(response.body().getIndexes()); + writeCachedArtists(context, indexes); + return indexes; + } - return cachedArtists != null ? cachedArtists : new Indexes(0, null, new ArrayList(), new ArrayList()); - } - finally - { - Util.close(reader); - } - } - - private static Indexes readCachedArtists(Context context) - { - String filename = getCachedArtistsFilename(context); - return FileUtil.deserialize(context, filename); - } - - private static void writeCachedArtists(Context context, Indexes artists) - { - String filename = getCachedArtistsFilename(context); - FileUtil.serialize(context, artists, filename); - } - - private static String getCachedArtistsFilename(Context context) - { - String s = Util.getRestUrl(context, null); - return String.format("indexes-%d.ser", Math.abs(s.hashCode())); - } - - private static List readCachedMusicFolders(Context context) { - String filename = getCachedMusicFoldersFilename(context); + private static Indexes readCachedArtists(Context context) { + String filename = getCachedArtistsFilename(context); return FileUtil.deserialize(context, filename); } - private static void writeCachedMusicFolders(Context context, List musicFolders) { - String filename = getCachedMusicFoldersFilename(context); - FileUtil.serialize(context, new ArrayList<>(musicFolders), filename); + private static void writeCachedArtists(Context context, Indexes artists) { + String filename = getCachedArtistsFilename(context); + FileUtil.serialize(context, artists, filename); } - private static String getCachedMusicFoldersFilename(Context context) - { - String s = Util.getRestUrl(context, null); - return String.format("musicFolders-%d.ser", Math.abs(s.hashCode())); - } + private static String getCachedArtistsFilename(Context context) { + String s = Util.getRestUrl(context, null); + return String.format(Locale.US, "indexes-%d.ser", Math.abs(s.hashCode())); + } @Override public void star(String id, String albumId, String artistId, Context context, ProgressListener progressListener) throws Exception diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/IndexesParser.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/IndexesParser.java deleted file mode 100644 index faa39bd6..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/IndexesParser.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ -package org.moire.ultrasonic.service.parser; - -import android.content.Context; -import android.util.Log; - -import org.moire.ultrasonic.R; -import org.moire.ultrasonic.domain.Artist; -import org.moire.ultrasonic.domain.Indexes; -import org.moire.ultrasonic.util.ProgressListener; - -import org.xmlpull.v1.XmlPullParser; - -import java.io.Reader; -import java.util.ArrayList; -import java.util.List; - -/** - * @author Sindre Mehus - */ -public class IndexesParser extends AbstractParser -{ - private static final String TAG = IndexesParser.class.getSimpleName(); - private Context context; - - public IndexesParser(Context context) - { - super(context); - this.context = context; - } - - public Indexes parse(Reader reader, ProgressListener progressListener) throws Exception - { - - long t0 = System.currentTimeMillis(); - updateProgress(progressListener, R.string.parser_reading); - init(reader); - - List artists = new ArrayList(); - List shortcuts = new ArrayList(); - Long lastModified = null; - String ignoredArticles = null; - int eventType; - String index = "#"; - boolean changed = false; - - do - { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) - { - String name = getElementName(); - if ("indexes".equals(name) || "artists".equals(name)) - { - changed = true; - lastModified = getLong("lastModified"); - ignoredArticles = get("ignoredArticles"); - } - else if ("index".equals(name)) - { - index = get("name"); - } - else if ("artist".equals(name)) - { - Artist artist = new Artist(); - artist.setId(get("id")); - artist.setName(get("name")); - artist.setCoverArt(get("coverArt")); - artist.setAlbumCount(getLong("albumCount")); - artist.setIndex(index); - artists.add(artist); - - if (artists.size() % 10 == 0) - { - String msg = this.context.getResources().getString(R.string.parser_artist_count, artists.size()); - updateProgress(progressListener, msg); - } - } - else if ("shortcut".equals(name)) - { - Artist shortcut = new Artist(); - shortcut.setId(get("id")); - shortcut.setName(get("name")); - shortcut.setIndex("*"); - shortcuts.add(shortcut); - } - else if ("error".equals(name)) - { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - - if (!changed) - { - return null; - } - - long t1 = System.currentTimeMillis(); - Log.d(TAG, "Got " + artists.size() + " artist(s) in " + (t1 - t0) + "ms."); - - String msg = this.context.getResources().getString(R.string.parser_artist_count, artists.size()); - updateProgress(progressListener, msg); - - return new Indexes(lastModified == null ? 0L : lastModified, ignoredArticles, shortcuts, artists); - } -} \ No newline at end of file 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 50ee5da8..e07feb8f 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIConverterTest.kt @@ -82,7 +82,7 @@ class APIConverterTest { MusicFolder(id, name) private fun createArtist(id: Long = -1, name: String = "", starred: Calendar? = null): Artist - = Artist(id, name, starred) + = Artist(id = id, name = name, starred = starred) private fun createIndex(name: String = "", artistList: List = emptyList()): Index = Index(name, artistList)