controllers.Rdio.java Source code

Java tutorial

Introduction

Here is the source code for controllers.Rdio.java

Source

/*
 * Copyright (c) 2011 - 2016 by Edward J. Becker
 *
 * This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package controllers;

import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.json.Json;
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.YouTubeRequestInitializer;
import com.google.api.services.youtube.model.Video;
import com.google.api.services.youtube.model.VideoListResponse;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.io.IOException;

import java.net.URLEncoder;

import java.util.*;
import jobs.YoutubeJob;
import models.*;
import org.joda.time.DateTime;
import play.libs.WS;
import play.libs.WS.HttpResponse;
import play.libs.WS.WSRequest;
import play.mvc.*;
import play.Logger;
import play.Play;
import play.data.binding.NoBinding;
import utils.Auth;
import utils.MetricsReporter;
import utils.RdioHelper;

/**
 *
 * @author Edward Becker
 *
    
 */
public class Rdio extends Controller {

    public static String endpoint = "http://api.napster.com/v2.1/";
    public static String NAPSTER_TOKEN_ENDPOINT = "https://api.napster.com/oauth/access_token";

    public static Random generator = new Random();
    private static MyOAuthClient connector = null;

    // This object is used to make YouTube Data API requests. The last
    // argument is required, but since we don't need anything
    // initialized when the HttpRequest is initialized, we override
    // the interface and provide a no-op function.
    private static YouTube youtube = new YouTube.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY,
            new HttpRequestInitializer() {
                public void initialize(HttpRequest request) throws IOException {
                }
            }).setYouTubeRequestInitializer(
                    new YouTubeRequestInitializer(Play.configuration.getProperty("youtube.key")))
                    .setApplicationName("zMusicCafe").build();

    public static MyOAuthClient getConnector(User user) {

        if (connector == null) {
            // See if its in cache
            connector = user.authClient;
            if (connector == null) {
                connector = new MyOAuthClient(null, null, null, Play.configuration.getProperty("rdio.key"),
                        Play.configuration.getProperty("rdio.secret"));

                user.authClient = connector;
                user.save();

            }
        }
        return connector;
    }

    public static boolean setupTokens(User user, String code, int retryAttempt) {

        Logger.info("Setuptokens");
        if (retryAttempt > 1) {
            return false;
        }
        retryAttempt++;

        String url = NAPSTER_TOKEN_ENDPOINT;
        String charset = "UTF-8";

        WS.WSRequest wsRequest = WS.url(url);
        //Logger.info(url);

        if (user.rdioCreds != null) {
            //Logger.info("Secret: " + user.rdioCreds.secret);
        }

        if (code == null && (user.rdioCreds != null && user.rdioCreds.secret != null)) {
            Logger.info("Getting refresh token for " + user.firstName);
            wsRequest.parameters.put("code", code);
            wsRequest.parameters.put("grant_type", "refresh_token");
        } else {
            Logger.info("Getting auth code for " + user.firstName);
            if (code != null) {
                wsRequest.parameters.put("code", code);
            }
            wsRequest.parameters.put("grant_type", "authorization_code");
        }
        wsRequest.parameters.put("redirect_uri",
                Play.configuration.getProperty("application.baseUrl") + "redirectRDIO");
        wsRequest.parameters.put("client_id", Play.configuration.getProperty("rdio.key"));
        wsRequest.parameters.put("client_secret", Play.configuration.getProperty("rdio.secret"));
        if (user.rdioCreds != null && user.rdioCreds.secret != null) {
            wsRequest.parameters.put("refresh_token", user.rdioCreds.secret);
        }

        HttpResponse wsResponse = wsRequest.post();
        String responseString = wsResponse.getString(charset);
        Logger.info("Token response: " + responseString);

        if (responseString.contains("Authentication code not valid")) {
            Logger.error("No way to re-authenticate user. Authentication code not valid. Clearing tokens");
            user.performedNapsterCheck = false; // re-ask napster check
            user.rdioCreds = null;
            user.save();
            return false;
        } else if (responseString.contains("Refresh token not valid")) {
            Logger.error("No way to re-authenticate user. Refresh token not valid .clearing tokens");
            user.performedNapsterCheck = false; // re-ask napster check
            user.rdioCreds = null;
            user.save();
            return false;
        }

        //        if (responseString.contains("error")){
        //            Logger.error("FIX THIS CODE OR REMOVE IT " + responseString);
        //            // Probably rdio token needs refreshing. let's clear auth creds and retry
        ////            user.rdioCreds.secret = null;
        ////            user.save();
        ////            return setupTokens(user, code, retryAttempt);
        //        }
        RDIOTokenResponse tokenResponse = new RDIOTokenResponse();

        Gson gson = new Gson();

        // We know from experience that an error here usually indicates a bad rdio token

        try {
            tokenResponse = gson.fromJson(responseString, tokenResponse.getClass());
        } catch (Exception e) {
            Logger.error("Exception parsing token response: " + tokenResponse + " Exception:" + e.getMessage());
            // Lets clear auth completely
            user.rdioCreds.secret = null;
            user.save();
            return false;
        }
        //Logger.info("Token response:" + tokenResponse.access_token +"/" + tokenResponse.refresh_token);

        if (user.rdioCreds == null) {
            user.rdioCreds = new AuthCredentials();
        }
        user.rdioCreds.token = tokenResponse.access_token;
        user.rdioCreds.secret = tokenResponse.refresh_token;
        Logger.info("Token Expires IN:" + tokenResponse.expires_in);
        Date then = new Date(new Date().getTime() + (tokenResponse.expires_in * 1000));
        Logger.info("Then:" + then.toString());
        user.rdioCreds.expiryDate = then;
        user.rdioCreds.code = code;
        user.rdioCreds.save();
        Logger.info("Created new auth tokens for " + user.firstName + " :" + user.rdioCreds.getToken());
        user.save();
        return true;

    }

    @NoBinding
    public static Track searchExactMatch(User user, String trackName, String artistName, String albumName,
            Track track) {

        return searchExactMatchUpdateTrackRdio(user, trackName, artistName, albumName, track);

    }

    @NoBinding
    public static ArrayList<Track> searchTrack(User user, String trackName, AuthCredentials backupCredentials) {

        if (user.rdioCreds == null) {
            Logger.info("Attempting to searchTrack without Catalog API credentials rejected.");
            return null;
        }
        //Logger.info("Rdio.searchTrack:" + trackName);

        ArrayList trackResults = null;
        // try {

        String url = endpoint + "search";
        WSRequest sign;
        try {
            sign = getConnector(user).sign(user.rdioCreds == null ? backupCredentials : user.rdioCreds, WS.url(url),
                    "GET");
        } catch (Exception ex) {
            Logger.error(ex.toString());
            ex.printStackTrace();
            return null;
        }

        sign.setParameter("method", "search");
        sign.setParameter("apikey", Play.configuration.getProperty("rdio.secret"));
        sign.setParameter("type", "track");
        sign.setParameter("q", trackName);
        sign.setParameter("limit", 35);
        sign.setParameter("preference", "editorial");

        JsonElement searchResult = sign.get().getJson();

        if (searchResult.toString().contains("{\"code\":\"UnauthorizedError\",\"message\":\"Unauthorized\"}")) {
            Logger.error(searchResult.toString());
            Logger.error(user.firstName + " is unauthorized.. @TODO add logic to setup token");
            if (setupTokens(user, user.rdioCreds.code, 0)) {
                searchResult = sign.get().getJson();
            } else {
                Logger.error("Severe error searching track for user " + user.firstName);
                return null;
            }

        } else if (searchResult.toString().toLowerCase().contains("unauthorized")) {
            Logger.error("Picking up unauthorized with wrong message: " + searchResult.toString());
        }

        trackResults = new ArrayList();
        JsonObject rdioTrack;

        if (searchResult == null) {
            Logger.info("Null search object getting " + trackName);
        }
        JsonObject dataObj = searchResult.getAsJsonObject();
        JsonArray jArray = dataObj.get("data").getAsJsonArray();
        for (JsonElement rdioTrackElement : jArray) {
            rdioTrack = rdioTrackElement.getAsJsonObject();

            //Logger.info("type:" + rdioTrack.get("type").getAsString());
            Track track = Track.retrieveTrackByRadioId(rdioTrack.get("id").getAsString());

            if (track != null
                    && track.coverArt.equals("http://www.zmusiccafe.com/public/images/missing-album-art.png")) {
                //Logger.warn("Reparsing missing album image art for existing track");
                track.coverArt = "http://direct.rhapsody.com/imageserver/v2/albums/" + track.albumKey
                        + "/images/200x200.jpg";
                track.save();
            }
            ;
            if (track == null) {
                Logger.info("Finding Similar track with network");
                track = Track.find("byTitleAndAlbum_NameAndArtistAndNetwork", rdioTrack.get("name").getAsString(),
                        rdioTrack.get("albumName").getAsString(), rdioTrack.get("artistName").getAsString(), "rdio")
                        .first();
                if (track != null
                        && track.coverArt.equals("http://www.zmusiccafe.com/public/images/missing-album-art.png")) {
                    //Logger.warn("Reparsing missing album iamage art");
                    track.coverArt = "http://direct.rhapsody.com/imageserver/v2/albums/" + track.albumKey
                            + "/images/200x200.jpg";
                    track.save();
                }
                if (track == null) {
                    Logger.info("Finding similar track with no network");
                    track = Track.find("byTitleIlikeAndAlbum_NameIlikeAndArtistIlikeAndNetworkIsNull",
                            rdioTrack.get("name").getAsString(), rdioTrack.get("albumName").getAsString(),
                            rdioTrack.get("artistName").getAsString()).first();
                    if (track != null && track.coverArt
                            .equals("http://www.zmusiccafe.com/public/images/missing-album-art.png")) {
                        //Logger.warn("Reparsing missing album iamage art");
                        track.coverArt = "http://direct.rhapsody.com/imageserver/v2/albums/" + track.albumKey
                                + "/images/200x200.jpg";
                        track.save();
                    }
                }
                if (track == null) {
                    //Logger.info("Creating new track:" + rdioTrack.toString());
                    track = new Track();
                    track = RdioHelper.parseNewTrack(user, track, rdioTrack, false);
                    track.save();

                    //                        YoutubeJob ty = new YoutubeJob(track);
                    //                       ty.loadYoutubes();

                    //                        if (track.bestYoutubeMatch() == null || track.bestYoutubeMatch().getId() == null){
                    //                            Logger.info("No you tube match for :" + track.artist + ":" + track.title);
                    //                        } else {
                    //                            track.youtube_id = ty.bestYouTubeMatch().getId().getVideoId().toString();
                    //                            track.youtube_duration = getDuration(track.youtube_id);
                    //                        }
                } else {

                    // @TODO I don't think we need to do this, we have the track
                    Logger.error("Why reparsing track " + track.title); // @TODO what does this have to do with anything?
                    //RdioHelper.parseNewTrack(user, track, rdioTrack);
                }
            }

            // track.save();

            if (track.canStream) {
                trackResults.add(track);
            }

        }

        return trackResults;
    }

    @NoBinding
    public static Track searchExactMatchUpdateTrackRdio(User user, String trackName, String artistName,
            String albumName, Track updateTrack) {

        // Logger.info("Searching" + trackName);

        Track matchedTrack = updateTrack;
        Track exactMatch = null;

        if (updateTrack != null) {

            // Force entity reload
            updateTrack = Track.findById(updateTrack.id);

            // Logger.info("Last time we tried to search " + trackName + "/" + albumName + "/" + artistName +":" + updateTrack.lastNoMatchLookupDate);

            if (updateTrack.lastNoMatchLookupDate != null) {
                return updateTrack;
            }
            updateTrack.lastNoMatchLookupDate = new Date();
            updateTrack.save();

        }

        Logger.info("Searching catalog API for " + trackName + "/" + albumName + "/" + artistName);

        AuthCredentials rdioCreds = user.rdioCreds;

        if (rdioCreds == null) {
            rdioCreds = user.room.owner.rdioCreds;
        }

        if (rdioCreds == null) {
            Logger.info("Unable to search catalog to no catalog credentials");
            return null;
        }
        String url = endpoint + "search";
        WSRequest sign;
        try {
            sign = getConnector(user).sign(rdioCreds, WS.url(url), "POST");
        } catch (Exception ex) {
            Logger.error("Exception with endpoint" + ex.toString());
            Logger.error("Exception with endpoint message " + ex.getMessage());
            ex.printStackTrace();
            return null;
        }

        sign.setHeader("Content-type", "application/json");
        String query = trackName;
        if (artistName != null) {
            query = query + " " + artistName;
        }
        if (albumName != null) {
            query = query + " " + albumName;
        }

        /*
            
        @TODO we need to paginate these results
            
         */
        query = query + "&type=track";

        sign.setParameter("q", URLEncoder.encode(query));
        sign.setParameter("limit", "" + 1000);
        JsonElement searchResult;

        try {
            searchResult = sign.get().getJson();
        } catch (Exception e) {
            Logger.error(" Error getting json: " + e.getMessage());
            return null;
        }

        // @TODO this is completely retarted.  Let's look for real error codes instead.

        //        if (searchResult.toString().contains("Unauthorized")){
        //
        //            Logger.info(searchResult.toString());
        //            Logger.error(user.firstName + " is unauthorized.. attempting to setup tokens and trying again...");
        //
        //            setupTokens(null, 0);
        //            Logger.info("Resigning request");
        //            try {
        //                sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "GET");
        //            } catch (Exception ex) {
        //                Logger.error(ex.toString());
        //                ex.printStackTrace();
        //                return null;
        //            }
        //            try {
        //                searchResult = sign.get().getJson();
        //            } catch (Exception e){
        //                MetricsReporter.sendMetric("rhapsody.unauthorizederror2 1");
        //                Logger.info("Search Napster error:" + e.getMessage());
        //                return null;
        //            }
        //
        //            if (searchResult.toString().contains("Unauthorized")) {
        //                updateTrack.lastNoMatchLookupDate = null;
        //                updateTrack.save();
        //                Logger.info(searchResult.toString());
        //                Logger.error(user.firstName + " is still unauthorized.. giving up. @TODO send UI an event to request re-login.");
        //                MetricsReporter.sendMetric("rhapsody.unauthorizederror 1");
        //            }
        //        }

        JsonArray resultArray = null;

        //try {
        resultArray = searchResult.getAsJsonObject().getAsJsonArray("data");
        //        } catch (Exception e){
        //            Logger.info("Error retrieving result from Napster:" + e.getMessage());
        //            updateTrack.lastNoMatchLookupDate = null;
        //            updateTrack.save();
        //            Logger.info(searchResult.toString());
        //            return null;
        //        }

        Track track = updateTrack;

        //        if (track != null) {
        //            Logger.info("Attempting to update existing track: " + track.title + "/" + track.album_name + "/" + track.artist);
        //        }

        Logger.info("Catalog API returned " + resultArray.size() + " results");

        // Note that we will load all tracks, but only return the last one we found, so that they at least get seeded in the library
        for (JsonElement rdioTrackElement : resultArray) {
            JsonObject rdioTrack = rdioTrackElement.getAsJsonObject();
            if (rdioTrack.get("id").getAsString().startsWith("Tra")) { // Object's type

                if (track != null && track.rdio_id != null
                        && track.rdio_id.equals(rdioTrack.get("id").getAsString())) {
                    Logger.info("!!Found same track with id " + track.rdio_id + "... updating");
                    if (track.coverArt.equals("http://www.zmusiccafe.com/public/images/missing-album-art.png")
                            || track.coverArt == null) {
                        Logger.warn("Missing album art for " + track.title + ": Reparsing.");
                    }
                    RdioHelper.parseNewTrack(user, track, rdioTrack, true);
                    exactMatch = track;
                    continue;
                }

                //Logger.info("looking for closet match");
                String closeMatchTitle = rdioTrack.get("name").getAsString().replace("(Album Version)", "")
                        .replace("(Album Version)", "").replace("(Remastered)", "").replace("(Radio Edit)", "")
                        .trim();

                /**
                 * See if we need to load this track while we have it.
                 */
                Track loadTrack = Track.retrieveTrackByRadioId(rdioTrack.get("id").getAsString());

                if (loadTrack == null) {
                    loadTrack = new Track();
                    //Logger.info("searchExactMatchUpdateTrackRdio calling parseNewTrack");
                    RdioHelper.parseNewTrack(user, loadTrack, rdioTrack, true);
                }

                if (loadTrack.title.equals(closeMatchTitle) && loadTrack.artist
                        .equalsIgnoreCase(rdioTrack.get("artist").getAsJsonObject().get("name").getAsString())) {

                    if (exactMatch == null) {
                        Logger.info("Exact match" + loadTrack.title + "/" + loadTrack.artist);
                        exactMatch = loadTrack;
                    }
                } else {
                    matchedTrack = loadTrack;
                }
            }
        }
        if (exactMatch != null) {
            return exactMatch;
        } else {
            return matchedTrack;
        }
    }

    public static Album retrieveAlbumByRdioId(User user, String album_key) {

        //Logger.info("Retrieving album by ID:" + album_key);
        Album album = Album.find("byRdio_Key", album_key).first();

        if (album != null && album.tracksLoaded == true && album.albumTracks != null
                && album.albumTracks.size() > 1) {
            return album;
        }

        // Logger.info("Album not (fully) loaded, loading...");
        WSRequest sign = null;

        String url = endpoint + "/albums/" + album_key + "?include=tracks,images";

        boolean rerequest = false;

        try {
            sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "GET");
        } catch (Exception ex) {

            Logger.error("User " + user.firstName + " had error with credentials");
            Logger.error(ex.toString());
            ex.printStackTrace();
            rerequest = true;
        }

        if (rerequest) {
            setupTokens(user, null, 0);
            try {
                sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "GET");
            } catch (Exception ex) {
                Logger.error(ex.toString());
                ex.printStackTrace();
                return null;
            }
        }

        JsonElement json = sign.get().getJson();

        if (json.toString().equalsIgnoreCase("{\"code\":\"UnauthorizedError\",\"message\":\"Unauthorized\"}")) {
            json = reasssignCatalogAPIAccessToken(user, url, null);
        }

        if (json == null) {
            Logger.error("XZK:001 Null search result for " + user.firstName + " for album key" + album_key);
            return null;
        }

        JsonArray searchArray = json.getAsJsonObject().getAsJsonArray("albums");

        JsonObject searchResult;

        if (searchArray == null) {
            Logger.error("Null searchArray in Rdio.java");
            Logger.info(json.toString());
            return null;
        }
        // Logger.info(searchArray.toString());
        if (searchArray.size() < 1) {
            // @TODO Add flag to album to ignore in future
            Logger.error("Album " + album_key + " is missing from Napster catalog");
            return null;
        }
        searchResult = searchArray.get(0).getAsJsonObject();

        if (album == null) {
            Logger.info("No album at all, creating..0");
            album = new Album();
        }
        if (searchResult.get("name") == null) {
            Logger.error("Strange error:" + searchResult.toString());
            return null;
        }
        album.rdio_key = album_key;
        album.name = searchResult.get("name").getAsString();
        album.artist = searchResult.getAsJsonPrimitive("artistName").getAsString();
        String dateString = searchResult.getAsJsonPrimitive("released").getAsString();
        album.releaseDate = new DateTime(dateString).toDate();
        JsonObject linked = searchResult.getAsJsonObject("linked");
        JsonArray imagesArray = linked.getAsJsonArray("images");
        JsonElement imageElement = imagesArray.get(0);
        album.icon = imageElement.getAsJsonObject().get("url").getAsString();
        album.baseIcon = album.icon;
        album.bigIcon = album.icon;
        JsonArray tracks = linked.getAsJsonArray("tracks");
        Iterator<JsonElement> iterator = tracks.iterator();
        int trackNum = 1;
        album.albumTracks = new ArrayList<Track>();

        int tracksAdded = 0;
        while (iterator.hasNext()) {
            JsonObject trackObj = iterator.next().getAsJsonObject();
            Track track = Track.find("byRdio_Id", trackObj.get("id").getAsString()).first();

            if (track == null) {
                track = new Track();
                RdioHelper.parseNewTrack(user, track, trackObj, true);

            }

            // Make sure track is not already present
            boolean trackPresent = false;

            Iterator iter = album.albumTracks.iterator();
            while (iter.hasNext()) {
                Track albumTrack = (Track) iter.next();
                if (albumTrack.rdio_id == track.rdio_id) {
                    trackPresent = true;
                    break;
                }
            }

            if (trackPresent) {
                Logger.error("Trying to add a track to an album but it already exists in album!");
            } else {
                tracksAdded++;
                track.album = album;
                album.albumTracks.add(track);
            }
        }

        album.tracksLoaded = true;
        album.save();
        //Logger.info("Saved album " + album.rdio_key + " and added " + tracksAdded + " tracks.");
        return album;

    }

    private static JsonObject reasssignCatalogAPIAccessToken(User user, String url, JsonObject searchResult) {
        WSRequest sign;
        //Logger.info("!!!" + searchResult.toString());
        if (searchResult == null || searchResult.toString()
                .contains("{\"code\":\"UnauthorizedError\",\"message\":\"Unauthorized\"}")) {
            if (searchResult == null) {
                Logger.error(user.firstName + " is unauthorized.. reassigning tokens."); // Message: " + searchResult.toString());
            } else {
                Logger.error(user.firstName + " is unauthorized.. reassigning tokens. Message: "
                        + searchResult.toString());
            }
            MetricsReporter.sendMetric("rhapsody.unauthorizederror 1");

            // Let's setup tokens and try again
            setupTokens(user, null, 0);

            if (user.rdioCreds == null) {
                Logger.error("Severe error retrieving and even reassiging tokens for " + user.firstName);
                return null;
            }
            try {
                sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "GET");
            } catch (Exception ex) {
                Logger.error(ex.toString());
                ex.printStackTrace();
                return null;
            }
            searchResult = sign.get().getJson().getAsJsonObject();
            if (searchResult.toString().contains("Unauthorized")) {
                Logger.info(searchResult.toString());
                Logger.error(user.firstName + " is unauthorized");
                MetricsReporter.sendMetric("catalog.unauthorizederror 1");
                return null;
            }

        }
        return searchResult;
    }

    /*
     *
     *
     */
    //    public static List retrieveAlbumsForArtist(User user, String artist_key) {
    //        Logger.info("Rdio.retrieveAlbumsForArtist" + artist_key);
    //
    //
    //        // Now load discography
    //
    //        ArrayList<Album> albumResults = new ArrayList();
    //        WSRequest sign;
    //
    //        String url = endpoint + "artists/" + artist_key + "/albums" +"?apikey=" + Play.configuration.getProperty("rdio.key") + "&limit=200";
    //
    //        Logger.info(url);
    //
    //        try {
    //            sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "GET");
    //        } catch (Exception ex) {
    //            Logger.error(ex.toString());
    //            ex.printStackTrace();
    //            return albumResults;
    //        }
    //
    //        JsonElement searchResult = sign.get().getJson();
    //
    //        JsonArray discs = searchResult.getAsJsonArray();
    //        //Logger.info(discs.toString());
    //
    //
    //        Iterator<JsonElement> discsIterator = discs.iterator();
    //
    //        while (discsIterator.hasNext()){
    //            JsonElement elem = discsIterator.next();
    //            JsonObject jsonAlbum = elem.getAsJsonObject();
    //            String aKey = jsonAlbum.get("id").getAsString();
    //
    //            Logger.info("Getting album for key" + aKey);
    //            Album album = Album.find("byRdio_key", aKey).first();
    //
    //            if (album != null) {
    //                Logger.info("Found album " + album.rdio_key);
    //                Album foundAlbum = album;
    //
    //                AlbumRating albumRating = AlbumRating.find("byUserAndRdio_key", user, foundAlbum.rdio_key).first();
    //                foundAlbum.album_rating = albumRating;
    //                Logger.info("FF album " + foundAlbum.name);
    //
    //                if (album.albumTracks.size() < 1){
    //                    Logger.error("Missing album tracks for album!");
    //                    //continue;
    ////                    album = new Album();
    ////
    ////                    album.rdio_key = aKey;
    ////
    ////                    album.name = jsonAlbum.get("name").getAsString();
    ////
    ////                    album.artist = jsonAlbum.getAsJsonObject("artist").get("name").getAsString();
    ////                    if (jsonAlbum.get("duration") != null) {
    ////                        album.duration = jsonAlbum.get("duration").getAsInt();
    ////                    }
    ////                    album.albumTracks = new ArrayList();
    ////                    album.save();
    ////                    albumResults.add(album);
    //
    //                }
    //               //continue;
    //            } else {
    //                Logger.info("No matches for album, so creating loading album for artist " +artist_key);
    //                album = new Album();
    //
    //                album.rdio_key = aKey;
    //
    //                album.name = jsonAlbum.get("name").getAsString();
    //
    //                album.artist = jsonAlbum.getAsJsonObject("artist").get("name").getAsString();
    //                if (jsonAlbum.get("duration") != null) {
    //                    album.duration = jsonAlbum.get("duration").getAsInt();
    //                }
    //                album.albumTracks = new ArrayList();
    //
    //
    //            }
    //            // Grab album detail
    //            Logger.info("Have album " + album.name);
    //
    //            url = endpoint + "albums/" + album.rdio_key;
    //            try {
    //                sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "GET");
    //                sign.setParameter("apikey", Play.configuration.getProperty("rdio.key"));
    //                sign.setParameter("limit", "100");
    //            } catch (Exception ex) {
    //                Logger.error(ex.toString());
    //                ex.printStackTrace();
    //                return albumResults;
    //            }
    //
    //            elem = sign.get().getJson();
    //            JsonArray images = elem.getAsJsonObject().get("images").getAsJsonArray();
    //            album.icon = images.get(0).getAsJsonObject().get("url").getAsString();
    //            album.bigIcon = album.icon;
    //            album.baseIcon = album.icon;
    //            album.releaseDate = new Date(jsonAlbum.get("released").getAsLong());
    //            album.duration = new Integer(0);
    //
    //            Logger.info("Before album save");
    //            album.save();
    //            Logger.info("loading trtacks");
    //
    //            JsonArray tracks = elem.getAsJsonObject().get("tracks").getAsJsonArray();
    //            Iterator<JsonElement> trackIter = tracks.iterator();
    //            while (trackIter.hasNext()){
    //                 JsonObject trackObj = trackIter.next().getAsJsonObject();
    //
    //                String trackId = trackObj.get("id").getAsString();
    //
    //                List AXtracks = Track.find("byRdio_id", trackId).fetch();
    //
    //                Track track = null;
    //                if (AXtracks.size() > 0){
    //                    track = (Track) AXtracks.get(0);
    //                }
    //
    //                album.save();
    //                if (track == null){
    //                    track = new Track();
    //                    RdioHelper.parseNewTrack(user, track, trackObj);
    //                    track.save();
    //                    track.coverArt = album.icon;
    //                }
    //                boolean albumHasTrack = false;
    //                Iterator tkIter = album.albumTracks.iterator();
    //
    //                while (tkIter.hasNext()){
    //                    Track tkForID = (Track) tkIter.next();
    //                    Track tk = Track.findById(tkForID.id);
    //                    if (tk.title.equals(track.title)){
    //                        albumHasTrack = true;
    //                    }
    //
    //                }
    //                if (!albumHasTrack){
    //                    Logger.info("Adding track " + track.title + "to album " + album.name);
    //                    album.albumTracks.add(track);
    //                    track.save();
    //                    album.save();
    //                }
    //                album.duration += (int) track.duration;
    //
    //            }
    //
    //            AlbumRating albumRating = AlbumRating.find("byUserAndRdio_key", user, album.rdio_key).first();
    //
    //            if (albumRating == null) {
    //                Logger.info("Creating album rating");
    //                albumRating = new AlbumRating();
    //                albumRating.rdio_key = album.rdio_key;
    //                albumRating.user = user;
    //                albumRating.rating = -1L;
    //                albumRating.artist_name = album.artist;
    //                albumRating.album_name = album.name;
    //                albumRating.numOfRatingStars = LibraryItem.getStarCount(albumRating.rating);
    //                albumRating.save();
    //            }
    //
    //            Logger.info("Duration:" + album.duration);
    //            album.save();
    //
    //            Logger.info(albumResults.toString());
    //            Logger.info("Adding album results for " + album.name);
    //            albumResults.add(album);
    //        }
    //        return albumResults;
    //    }

    /*
     *
     *
     */
    public static List retrieveAlbumsForArtist(User user, String artist_key) {
        Logger.info("Rdio.retrieveAlbumsForArtist" + artist_key);

        // Now load discography

        ArrayList<Album> albumResults = new ArrayList();
        WSRequest sign;

        String url = endpoint + "artists/" + artist_key + "/albums" + "?apikey="
                + Play.configuration.getProperty("rdio.key") + "&limit=200";

        //Logger.info(url);

        try {
            sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "GET");
        } catch (Exception ex) {
            Logger.error(ex.toString());
            ex.printStackTrace();
            return albumResults;
        }

        JsonElement searchResult = sign.get().getJson();
        JsonElement albumsJsonElem = searchResult.getAsJsonObject().get("albums");
        JsonArray albumsArray = albumsJsonElem.getAsJsonArray();

        //Logger.info(searchResult.toString());

        JsonArray discs = albumsArray;

        //Logger.info(discs.toString());

        Iterator<JsonElement> iterator = discs.iterator();

        while (iterator.hasNext()) {
            JsonElement elem = iterator.next();
            JsonObject jsonAlbum = elem.getAsJsonObject();
            String aKey = jsonAlbum.get("id").getAsString();

            Album album = Album.find("byRdio_key", aKey).first();

            if (album != null) {
                //Logger.info("Found album " + album.rdio_key);
                Album foundAlbum = album;

                AlbumRating albumRating = AlbumRating.find("byUserAndRdio_key", user, foundAlbum.rdio_key).first();

                if (albumRating == null) {
                    albumRating = new AlbumRating();
                    albumRating.rdio_key = foundAlbum.rdio_key;
                    albumRating.user = user;
                    albumRating.rating = -1L;
                    albumRating.artist_name = foundAlbum.artist;
                    albumRating.album_name = foundAlbum.name;
                    albumRating.numOfRatingStars = LibraryItem.getStarCount(albumRating.rating);
                }

                foundAlbum.album_rating = albumRating;

                albumRating.save();
                albumResults.add(foundAlbum);
                //Logger.info(foundAlbum.name);
                continue;
            } else {
                // Logger.info("No matches for album, so loading album " +artist_key);
                album = new Album();
                album.albumTracks = new ArrayList();
            }

            album.rdio_key = aKey;

            //Logger.info(jsonAlbum.toString());
            album.name = jsonAlbum.get("name").getAsString();

            album.artist = jsonAlbum.getAsJsonPrimitive("artistName").getAsString();
            if (jsonAlbum.get("duration") != null) {
                album.duration = jsonAlbum.get("duration").getAsInt();
            }

            // Grab album detail

            url = endpoint + "albums/" + album.rdio_key + "?include=tracks,images";
            try {
                sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "GET");
                sign.setParameter("apikey", Play.configuration.getProperty("rdio.key"));
                sign.setParameter("limit", "100");
            } catch (Exception ex) {
                Logger.error(ex.toString());
                ex.printStackTrace();
                return albumResults;
            }

            //Logger.info(url + "&apiKey=");
            elem = sign.get().getJson();
            // Logger.info(elem.toString());
            JsonArray albums = elem.getAsJsonObject().getAsJsonArray("albums");

            JsonObject thisAlbum = albums.get(0).getAsJsonObject();
            JsonObject linked = thisAlbum.get("linked").getAsJsonObject();
            JsonArray images = linked.getAsJsonArray("images");
            // Logger.info(thisAlbum.toString());
            album.icon = images.get(0).getAsJsonObject().get("url").getAsString();
            album.bigIcon = album.icon;
            album.baseIcon = album.icon;
            String dateString = thisAlbum.getAsJsonPrimitive("released").getAsString();
            album.releaseDate = new DateTime(dateString).toDate();
            album.duration = new Integer(0);

            JsonArray tracks = linked.getAsJsonArray("tracks").getAsJsonArray();
            Iterator<JsonElement> trackIter = tracks.iterator();
            while (trackIter.hasNext()) {
                JsonObject trackObj = trackIter.next().getAsJsonObject();

                String trackId = trackObj.get("id").getAsString();

                List AXtracks = Track.find("byRdio_id", trackId).fetch();

                Track track = null;
                if (AXtracks.size() > 0) {
                    track = (Track) AXtracks.get(0);
                }

                if (track == null) {
                    track = new Track();
                    //                    track.rdio_id = trackObj.get("id").getAsString(); // the object key of the track
                    //                    track.save();

                    RdioHelper.parseNewTrack(user, track, trackObj, true);
                    track.coverArt = album.icon;

                }
                album.duration += (int) track.duration;

            }

            AlbumRating albumRating = AlbumRating.find("byUserAndRdio_key", user, album.rdio_key).first();

            if (albumRating == null) {
                albumRating = new AlbumRating();
                albumRating.rdio_key = album.rdio_key;
                albumRating.user = user;
                albumRating.rating = -1L;
                albumRating.artist_name = album.artist;
                albumRating.album_name = album.name;
                albumRating.numOfRatingStars = LibraryItem.getStarCount(albumRating.rating);
            }

            // Logger.info("Duration:" + album.duration);

            album = album.save();
            album.album_rating = albumRating;

            albumRating.save();

            albumResults.add(album);
        }
        return albumResults;
    }

    public static Track updateTrackFromRdio(User user, Track track) {

        String url = endpoint + "tracks/" + track.rdio_id + "?apikey=" + Play.configuration.getProperty("rdio.key")
                + "&limit=200";

        Logger.info("AX: Updating " + track.title + " from Rdio");

        if (track == null || track.rdio_id == null) {
            Logger.info("Rdio.UpdateTrackFromRdio has null track or id:  " + track);
            return track;
        }
        ArrayList trackResults = null;
        WSRequest sign;

        try {
            sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "GET");
        } catch (Exception ex) {
            Logger.error(ex.toString());
            ex.printStackTrace();
            return null;
        }

        sign.setParameter("method", "get");

        sign.setParameter("domain", Play.configuration.getProperty("domain"));
        sign.setHeader("Content-type", "application/json");

        JsonElement searchResult = null;
        searchResult = sign.get().getJson();

        //if (searchResult != null)
        //Logger.info(searchResult.toString());

        if (searchResult == null || searchResult.getAsJsonObject() == null) {
            Logger.error("Null searrch result" + track.rdio_id);
            try {
                // track.delete();
                // track.save();
                Logger.info("Deleted track");
                return null;
            } catch (Exception e) {
                Logger.info("" + e.getMessage());
            } finally {
                return null;
            }
        }
        com.google.gson.JsonObject asJsonObj = searchResult.getAsJsonObject();
        Logger.info("updateTrackFromRadio calling parseNewTrack");
        RdioHelper.parseNewTrack(user, track, asJsonObj, true);

        Logger.info("Updated " + track.title + "/" + track.album_name + "/" + track.artist + " from Rdio - CX011");

        track.lastUpdated = new Date();
        //        YoutubeJob ty = new YoutubeJob(track);
        //       ty.loadYoutubes();

        track.uniformSave();
        return track;

    }

    // @TODO we need to look for variations .. i.e. "The Smashing Pumpkins" vs. "Smashing Pumpkins"
    @NoBinding
    public static Artist searchArtist(User user, String artist_name) {
        Logger.info("Rdio.searchArtist");
        ArrayList trackResults = null;
        Artist artist = null;
        // try {

        //String url = endpoint + "search?apikey=" + Play.configuration.getProperty("rdio.key") + "&q=" + URLEncoder.encode(artist_name) + "&type=artist";
        String url = endpoint + "search";
        //Logger.info(url);
        WSRequest sign;
        try {
            sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "GET");
            sign.setParameter("type", "artist");
            sign.setParameter("q", URLEncoder.encode(artist_name));
            sign.setParameter("apikey", Play.configuration.getProperty("rdio.key"));
        } catch (Exception ex) {
            Logger.error(ex.toString());
            ex.printStackTrace();
            return null;
        }
        JsonElement searchResult = sign.get().getJson();
        //Logger.info(searchResult.toString());

        JsonArray results = searchResult.getAsJsonArray();

        JsonObject artistResult = null;

        for (JsonElement result : results) {
            JsonObject resObj = result.getAsJsonObject();
            //Logger.info(resObj.toString());

            if (resObj.get("name").getAsString().equals(artist_name)) {
                artistResult = resObj;
                Logger.info("Found artist " + artist_name + " with " + artistResult.get("id").getAsString());
                break;

            }
        }
        if (artistResult == null) {
            Logger.error("Trying to get artist result for non existent artist");
            return null;
        }

        Artist newArtist = new Artist();
        newArtist.rdio_key = artistResult.get("id").getAsString();
        newArtist.name = artistResult.get("name").getAsString();

        // Grab image
        url = endpoint + "artists/" + newArtist.rdio_key + "/images";
        try {
            sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "GET");
            sign.setParameter("type", "artist");
            sign.setParameter("q", URLEncoder.encode(artist_name));
            sign.setParameter("apikey", Play.configuration.getProperty("rdio.key"));
        } catch (Exception ex) {
            Logger.error(ex.toString());
            ex.printStackTrace();
            return null;
        }

        JsonElement imgResp = sign.get().getJson();
        JsonArray imgArray = imgResp.getAsJsonArray();
        JsonObject firstimage = imgArray.iterator().next().getAsJsonObject();
        newArtist.baseIcon = firstimage.get("url").getAsString();
        newArtist.icon = newArtist.baseIcon;

        return newArtist;

    }

    public static ArrayList<Track> suggestsearchTrack(User user, String trackName) {

        ArrayList trackResults = null;
        // try {

        String url = endpoint;
        WSRequest sign;
        try {
            sign = getConnector(user).sign(user.rdioCreds, WS.url(url), "POST");
        } catch (Exception ex) {
            Logger.info(ex.toString());
            ex.printStackTrace();
            return null;
        }

        sign.setParameter("method", "searchSuggestions");
        sign.setParameter("domain", Play.configuration.getProperty("domain"));
        sign.setParameter("types", "Track,Album,Artist");

        sign.setParameter("query", trackName);
        sign.setParameter("never_or", true);
        sign.setHeader("Content-type", "application/json");
        JsonElement searchResult = sign.post().getJson();
        //Logger.info(searchResult.toString());
        com.google.gson.JsonObject asJsonObj = searchResult.getAsJsonObject();
        // JsonElement rawResult = asJsonObj.get("result").getAsJsonObject();

        com.google.gson.JsonArray resultArray = asJsonObj.getAsJsonObject().get("result").getAsJsonArray();

        trackResults = new ArrayList();
        for (JsonElement rdioTrackElement : resultArray) {
            JsonObject rdioTrack = rdioTrackElement.getAsJsonObject();

            if (rdioTrack.get("type").getAsString().contains("t")) { // Object's type
                Track track = Track.retrieveTrackByRadioId(rdioTrack.get("key").getAsString());
                if (track == null) {
                    track = new Track();
                    RdioHelper.parseNewTrack(user, track, rdioTrack, true);
                    //                    YoutubeJob ty = new YoutubeJob(track);
                    //                   ty.loadYoutubes();

                }

                track.uniformSave();

                if (track.canStream) {
                    trackResults.add(track);
                }

            }
        }
        return trackResults;
    }
}