private MediaMetadata buildFromJSON(JSONObject json, String basePath) throws JSONException {
    String title = json.getString(JSON_TITLE);
    String album = json.getString(JSON_ALBUM);
    String artist = json.getString(JSON_ARTIST);
    String genre = json.getString(JSON_GENRE);
    String source = json.getString(JSON_SOURCE);
    String iconUrl = json.getString(JSON_IMAGE);
    int trackNumber = json.getInt(JSON_TRACK_NUMBER);
    int totalTrackCount = json.getInt(JSON_TOTAL_TRACK_COUNT);
    int duration = json.getInt(JSON_DURATION) * 1000; // ms

    LogHelper.d(TAG, "Found music track: ", json);

    // Media is stored relative to JSON file
    if (!source.startsWith("http")) {
      source = basePath + source;
    }
    if (!iconUrl.startsWith("http")) {
      iconUrl = basePath + iconUrl;
    }
    // Since we don't have a unique ID in the server, we fake one using the hashcode of
    // the music source. In a real world app, this could come from the server.
    String id = String.valueOf(source.hashCode());

    // Adding the music source to the MediaMetadata (and consequently using it in the
    // mediaSession.setMetadata) is not a good idea for a real world music app, because
    // the session metadata can be accessed by notification listeners. This is done in this
    // sample for convenience only.
    return new MediaMetadata.Builder()
        .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, id)
        .putString(CUSTOM_METADATA_TRACK_SOURCE, source)
        .putString(MediaMetadata.METADATA_KEY_ALBUM, album)
        .putString(MediaMetadata.METADATA_KEY_ARTIST, artist)
        .putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
        .putString(MediaMetadata.METADATA_KEY_GENRE, genre)
        .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, iconUrl)
        .putString(MediaMetadata.METADATA_KEY_TITLE, title)
        .putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, trackNumber)
        .putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, totalTrackCount)
        .build();
  }
  /**
   * Get the list of music tracks from a server and caches the track information for future
   * reference, keying tracks by musicId and grouping by genre.
   */
  public void retrieveMediaAsync(final Callback callback) {
    LogHelper.d(TAG, "retrieveMediaAsync called");
    if (mCurrentState == State.INITIALIZED) {
      // Nothing to do, execute callback immediately
      callback.onMusicCatalogReady(true);
      return;
    }

    // Asynchronously load the music catalog in a separate thread
    new AsyncTask<Void, Void, State>() {
      @Override
      protected State doInBackground(Void... params) {
        retrieveMedia();
        return mCurrentState;
      }

      @Override
      protected void onPostExecute(State current) {
        if (callback != null) {
          callback.onMusicCatalogReady(current == State.INITIALIZED);
        }
      }
    }.execute();
  }