/*
* Copyright (C) 2014 Fastboot Mobile, LLC.
*
* This program 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.
*
* This program 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 this program;
* if not, see <http://www.gnu.org/licenses>.
*/
package com.fastbootmobile.encore.api.musicbrainz;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.fastbootmobile.encore.api.common.JsonGet;
import com.fastbootmobile.encore.api.common.Pair;
import com.fastbootmobile.encore.api.common.RateLimitException;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
/**
* MusicBrainz API Client
*/
public class MusicBrainzClient {
private static final String TAG = "MusicBrainzClient";
private static final String MAIN_EP = "http://musicbrains.org/ws/2";
private static final String COVER_EP = "http://coverartarchive.org/release/";
private static final Map<Pair<String, String>, AlbumInfo[]> mAlbumInfoCache
= new HashMap<>();
private static final Map<String, String> mAlbumArtCache = new HashMap<>();
/**
* Retrieves the album information from MusicBrainz. This method is synchronous and must be
* called from a Thread!
* @param artist The name of the artist. Must be filled.
* @param album The name of the album. May be empty.
* @return An {@link com.fastbootmobile.encore.api.musicbrainz.AlbumInfo} filled with the information
* from musicbrainz, or null in case of error
*/
public static AlbumInfo[] getAlbum(String artist, String album) throws RateLimitException {
if (mAlbumInfoCache.containsKey(Pair.create(artist, album))) {
return mAlbumInfoCache.get(Pair.create(artist, album));
}
if (artist == null && album == null) {
return null;
}
try {
String query = "";
if (artist != null) {
query += URLEncoder.encode("artist:\"" + artist + "\"", "UTF-8");
}
if (album != null && !album.isEmpty()) {
query += URLEncoder.encode(" AND release:\"" + album + "\"", "UTF-8");
}
JSONObject object = JsonGet.getObject(MAIN_EP + "/release/", "fmt=json&query=" + query, true);
if (object.has("releases")) {
JSONArray releases = object.getJSONArray("releases");
final int releasesCount = releases.length();
if (releasesCount > 0) {
AlbumInfo[] infoArray = new AlbumInfo[releasesCount];
for (int i = 0; i < releasesCount; i++) {
AlbumInfo info = new AlbumInfo();
JSONObject release = releases.getJSONObject(i);
info.id = release.getString("id");
try {
info.track_count = release.getInt("track-count");
} catch (JSONException e) {
// No track count info, too bad
info.track_count = 0;
}
infoArray[i] = info;
}
mAlbumInfoCache.put(Pair.create(artist, album), infoArray);
return infoArray;
}
} else if (object.has("error")) {
Log.w(TAG, "Rate limited by the API, will retry later");
throw new RateLimitException();
}
// AlbumArtCache will retry with something else if needed
mAlbumInfoCache.put(Pair.create(artist, album), null);
return null;
} catch (IOException e) {
Log.e(TAG, "Unable to get album info (rate limit?)", e);
throw new RateLimitException();
} catch (JSONException e) {
// May happen due to an API error, e.g. error 502
Log.e(TAG, "JSON error while parsing album info", e);
throw new RateLimitException();
}
}
/**
* Returns the URL to an image representing the album art of the provided album ID. This album
* id must be retrieved with {@link #getAlbum(String, String)}
* @param albumId The album ID
* @return An album art URL, or null if none found
*/
public static String getAlbumArtUrl(String albumId) throws RateLimitException {
if (mAlbumArtCache.containsKey(albumId)) {
return mAlbumArtCache.get(albumId);
}
try {
JSONObject object = JsonGet.getObject(COVER_EP + albumId, "", true);
// We take the very first art here, no matter if it's front or back. Eventually some
// day, we might filter only front art.
JSONArray images = object.getJSONArray("images");
JSONObject image = images.getJSONObject(0);
String output = image.getJSONObject("thumbnails").getString("large");
mAlbumArtCache.put(albumId, output);
return output;
} catch (IOException e) {
mAlbumArtCache.put(albumId, null);
return null;
} catch (JSONException e) {
mAlbumArtCache.put(albumId, null);
return null;
}
}
}