package v.blade.library.sources; import android.content.Context; import android.content.SharedPreferences; import android.os.Looper; import android.util.Log; import android.widget.Toast; import com.spotify.sdk.android.player.*; import kaaes.spotify.webapi.android.SpotifyApi; import kaaes.spotify.webapi.android.SpotifyError; import kaaes.spotify.webapi.android.SpotifyService; import kaaes.spotify.webapi.android.models.*; import retrofit.RetrofitError; import v.blade.R; import v.blade.library.Album; import v.blade.library.Playlist; import v.blade.library.*; import v.blade.player.PlayerMediaPlayer; import v.blade.ui.settings.SettingsActivity; import javax.net.ssl.HttpsURLConnection; import java.io.*; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import static v.blade.library.LibraryService.CACHE_SEPARATOR; public class Spotify extends Source { public final String SPOTIFY_CLIENT_ID = "2f95bc7168584e7aa67697418a684bae"; public final String SPOTIFY_REDIRECT_URI = "http://valou3433.fr/"; public String SPOTIFY_USER_TOKEN; public String SPOTIFY_REFRESH_TOKEN; public final SpotifyApi spotifyApi = new SpotifyApi(); public UserPrivate mePrivate; private File spotifyCacheFile; private File spotifyPlaylistsCache; private ArrayList<LibraryObject> spotifyCachedToLoadArt; private HashMap<LibraryObject, String> spotifyCachedToLoadArtUris; public SourcePlayer player = new SourcePlayer() { SpotifyPlayer spotifyPlayer; @Override public void init() { Config playerConfig = new Config(LibraryService.appContext, SPOTIFY_USER_TOKEN, SPOTIFY_CLIENT_ID); com.spotify.sdk.android.player.Spotify.getPlayer(playerConfig, LibraryService.appContext, new SpotifyPlayer.InitializationObserver() { @Override public void onInitialized(final SpotifyPlayer p) { spotifyPlayer = p; setListener(PlayerMediaPlayer.playerListener); spotifyPlayer.addConnectionStateCallback(new ConnectionStateCallback() { @Override public void onLoggedIn() {} @Override public void onLoggedOut() {} @Override public void onLoginFailed(com.spotify.sdk.android.player.Error error) { Toast.makeText(LibraryService.appContext, LibraryService.appContext.getString(R.string.player_login_error) + " Spotify", Toast.LENGTH_SHORT).show(); } @Override public void onTemporaryError() {} @Override public void onConnectionMessage(String s) {} }); } @Override public void onError(Throwable throwable) { Toast.makeText(LibraryService.appContext, LibraryService.appContext.getString(R.string.player_unknown_error) + " Spotify", Toast.LENGTH_SHORT).show(); } }); } @Override public void setListener(PlayerListener listener) { spotifyPlayer.addNotificationCallback(new Player.NotificationCallback() { @Override public void onPlaybackEvent(PlayerEvent playerEvent) { if(playerEvent.equals(PlayerEvent.kSpPlaybackNotifyAudioDeliveryDone)) { listener.onSongCompletion(); } } @Override public void onPlaybackError(com.spotify.sdk.android.player.Error error) { listener.onPlaybackError(player, error.name()); } }); } @Override public void play(PlayerCallback callback) { if(spotifyPlayer == null) {callback.onFailure(this); return;} spotifyPlayer.resume(new Player.OperationCallback() { @Override public void onSuccess() {callback.onSucess(player);} @Override public void onError(com.spotify.sdk.android.player.Error error) {callback.onFailure(player);} }); } @Override public void pause(PlayerCallback callback) { if(spotifyPlayer == null) {if(callback != null) callback.onFailure(player); return;} spotifyPlayer.pause(new Player.OperationCallback() { @Override public void onSuccess() {if(callback != null) callback.onSucess(player);} @Override public void onError(com.spotify.sdk.android.player.Error error) {if(callback != null) callback.onFailure(player);} }); } @Override public void playSong(Song song, PlayerCallback callback) { SongSources.SongSource spot = song.getSources().getSpotify(); if(spot == null) {if(callback != null) callback.onFailure(player); return;} if(spotifyPlayer == null) { if(callback != null) callback.onFailure(player); return; } spotifyPlayer.playUri(new Player.OperationCallback() { @Override public void onSuccess() {if(callback != null) callback.onSucess(player);} @Override public void onError(com.spotify.sdk.android.player.Error error) {if(callback != null) callback.onFailure(player);} }, "spotify:track:" + spot.getId(), 0, 0); } @Override public void seekTo(int msec) { if(spotifyPlayer != null) spotifyPlayer.seekToPosition(null, msec); } @Override public int getCurrentPosition() { return spotifyPlayer == null ? 0 : (spotifyPlayer.getPlaybackState() == null ? 0 : (int) spotifyPlayer.getPlaybackState().positionMs); } }; Spotify() { super(R.drawable.ic_spotify, R.drawable.ic_spotify_logo, "Spotify"); } @Override public SourcePlayer getPlayer() {return player;} @Override public String getUserName() {return mePrivate == null ? "" : (mePrivate.display_name == null ? mePrivate.id : mePrivate.display_name);} @Override public void initConfig(SharedPreferences accountsPrefs) { spotifyCacheFile = new File(LibraryService.appContext.getCacheDir().getAbsolutePath() + "/spotify.cached"); spotifyPlaylistsCache = new File(LibraryService.appContext.getCacheDir().getAbsolutePath() + "/spotifyPlaylists/"); if(!spotifyPlaylistsCache.exists()) spotifyPlaylistsCache.mkdir(); setPriority(accountsPrefs.getInt("spotify_prior", 0)); //setup spotify api if(SPOTIFY_USER_TOKEN == null) { SPOTIFY_USER_TOKEN = accountsPrefs.getString("spotify_token", null); SPOTIFY_REFRESH_TOKEN = accountsPrefs.getString("spotify_refresh_token", null); } if(SPOTIFY_USER_TOKEN != null) { spotifyApi.setAccessToken(SPOTIFY_USER_TOKEN); //check for token validity new Thread() { public void run() { Looper.prepare(); try { mePrivate = spotifyApi.getService().getMe(); } catch(RetrofitError e) { if(e.getResponse() != null && e.getResponse().getStatus() == 401) { Log.println(Log.INFO, "[BLADE-SPOTIFY]", "Actualizing token."); refreshSpotifyToken(); try { mePrivate = spotifyApi.getService().getMe(); } catch(RetrofitError e1) { setAvailable(false); Toast.makeText(LibraryService.appContext, "Could not connect to your Spotify account.", Toast.LENGTH_LONG).show(); return; } } } player.init(); } }.start(); setAvailable(true); } } @Override public void disconnect() { setAvailable(false); setPriority(0); SPOTIFY_USER_TOKEN = null; SPOTIFY_REFRESH_TOKEN = null; mePrivate = null; SharedPreferences accountsPrefs = LibraryService.appContext.getSharedPreferences(SettingsActivity.PREFERENCES_ACCOUNT_FILE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = accountsPrefs.edit(); editor.remove("spotify_token"); editor.remove("spotify_refresh_token"); editor.remove("spotify_prior"); editor.apply(); //remove cache spotifyCacheFile.delete(); if(spotifyPlaylistsCache.exists()) { for(File f : spotifyPlaylistsCache.listFiles()) f.delete(); spotifyPlaylistsCache.delete(); } //wait for resync } @Override public void registerCachedSongs() { if(!LibraryService.configured) return; spotifyCachedToLoadArt = new ArrayList<>(); spotifyCachedToLoadArtUris = new HashMap<>(); System.out.println("[BLADE-SPOTIFY] Registering cached songs..."); try { if(spotifyCacheFile.exists()) { //spotify library BufferedReader spr = new BufferedReader(new FileReader(spotifyCacheFile)); String tp[] = spr.readLine().split(CACHE_SEPARATOR); int cache_version = 0; if(tp[0].equals("Blade") && tp[1].equals("edalB") && tp[2].equals("SPCACHE")) { //We have cache file from Blade >= 1.5, retrieve version from file to know how to read it cache_version = Integer.parseInt(tp[3]); } else { //cache version is 1 : < Blade 1.5 cache_version = 1; //register first song Song song = LibraryService.registerSong(tp[2], tp[1], Integer.parseInt(tp[4]), 0, Long.parseLong(tp[5]), tp[0], new SongSources.SongSource(tp[6], SOURCE_SPOTIFY)); song.setFormat(tp[3]); if(!song.getAlbum().hasArt() && !song.getAlbum().getArtLoading()) { spotifyCachedToLoadArt.add(song.getAlbum()); song.getAlbum().setArtLoading(); } } while(spr.ready()) { tp = spr.readLine().split(CACHE_SEPARATOR); Song song = LibraryService.registerSong(tp[2], tp[1], Integer.parseInt(tp[4]), 0, Long.parseLong(tp[5]), tp[0], new SongSources.SongSource(tp[6], SOURCE_SPOTIFY)); song.setFormat(tp[3]); if(!song.getAlbum().hasArt() && !song.getAlbum().getArtLoading()) { //the image is supposed to be cached locally, so no need to provide URL //if user removed cache, this fails ; cache version 2 should take care of that spotifyCachedToLoadArt.add(song.getAlbum()); if(cache_version == 2 && tp.length > 7) spotifyCachedToLoadArtUris.put(song.getAlbum(), tp[7]); else spotifyCachedToLoadArtUris.put(song.getAlbum(), ""); song.getAlbum().setArtLoading(); } } spr.close(); //spotify playlists if(spotifyPlaylistsCache.exists()) { for(File f : spotifyPlaylistsCache.listFiles()) { ArrayList<Song> thisList = new ArrayList<>(); BufferedReader sppr = new BufferedReader(new FileReader(f)); String id = sppr.readLine(); boolean isMine = Boolean.parseBoolean(sppr.readLine()); String owner = null; String ownerID = null; if(!isMine) {owner = sppr.readLine(); ownerID = sppr.readLine();} boolean isCollab = Boolean.parseBoolean(sppr.readLine()); while(sppr.ready()) { tp = sppr.readLine().split(CACHE_SEPARATOR); Song song = LibraryService.SAVE_PLAYLISTS_TO_LIBRARY ? LibraryService.registerSong(tp[2], tp[1], Integer.parseInt(tp[4]), 0, Long.parseLong(tp[5]), tp[0], new SongSources.SongSource(tp[6], SOURCE_SPOTIFY)) : LibraryService.getSongHandle(tp[0], tp[1], tp[2], Long.parseLong(tp[5]), new SongSources.SongSource(tp[6], SOURCE_SPOTIFY), Integer.parseInt(tp[4]), 0); song.setFormat(tp[3]); thisList.add(song); if(!song.getAlbum().hasArt() && !song.getAlbum().getArtLoading()) { //the image is supposed to be cached locally, so no need to provide URL if(cache_version == 2 && tp.length > 7) spotifyCachedToLoadArtUris.put(song.getAlbum(), tp[7]); else spotifyCachedToLoadArtUris.put(song.getAlbum(), ""); spotifyCachedToLoadArt.add(song.getAlbum()); song.getAlbum().setArtLoading(); } } sppr.close(); Playlist p = new Playlist(f.getName(), thisList); if(!isMine) p.setOwner(owner, ownerID); if(isCollab) p.setCollaborative(); spotifyCachedToLoadArt.add(p); p.setArtLoading(); p.getSources().addSource(new SongSources.SongSource(id, SOURCE_SPOTIFY)); LibraryService.getPlaylists().add(p); } } } } catch(IOException e) { Log.println(Log.ERROR, "[BLADE-SPOTIFY]", "Cache restore : IOException"); e.printStackTrace(); } System.out.println("[BLADE-SPOTIFY] Cached songs registered."); } @Override public void loadCachedArts() { if(spotifyCachedToLoadArt == null) return; for(LibraryObject alb : spotifyCachedToLoadArt) { LibraryService.loadArt(alb, spotifyCachedToLoadArtUris.get(alb), false); } spotifyCachedToLoadArt = null; } @Override public void registerSongs() { if(!LibraryService.configured) return; if(SPOTIFY_USER_TOKEN == null) return; // list used for spotify cache ArrayList<Song> spotifySongs = new ArrayList<>(); ArrayList<Playlist> spotifyPlaylists = new ArrayList<>(); HashMap<Album, String> albumImagesUrls = new HashMap<>(); SpotifyService service = spotifyApi.getService(); try { if(mePrivate == null) mePrivate = service.getMe(); //requests HashMap<String, Object> params = new HashMap<>(); params.put("limit", 50); Pager<SavedTrack> userTracks = service.getMySavedTracks(params); //parse user tracks request response int count = userTracks.total; int offset = 0; while(true) { for (SavedTrack track : userTracks.items) { Track t = track.track; Song s = LibraryService.registerSong(t.artists.get(0).name, t.album.name, t.track_number, 0, t.duration_ms, t.name, new SongSources.SongSource(t.id, SOURCE_SPOTIFY)); spotifySongs.add(s); if(!s.getAlbum().hasArt()) { if(t.album.images != null && t.album.images.size() >= 1) { Image albumImage = t.album.images.get(0); LibraryService.loadArt(s.getAlbum(), albumImage.url, false); albumImagesUrls.put(s.getAlbum(), albumImage.url); } } } count -= 50; if(count <= 0) break; else { offset += 50; params.put("offset", offset); userTracks = service.getMySavedTracks(params); } } params.put("limit", 50); params.put("offset", 0); Pager<SavedAlbum> userAlbums = service.getMySavedAlbums(params); offset = 0; count = userAlbums.total; while(true) { //parse user albums request response for(SavedAlbum album : userAlbums.items) { Album savedAlbum = null; kaaes.spotify.webapi.android.models.Album alb = album.album; Pager<Track> tracks = service.getAlbumTracks(alb.id); for(Track t : tracks.items) { Song s = LibraryService.registerSong(t.artists.get(0).name, alb.name, t.track_number, 0, t.duration_ms, t.name, new SongSources.SongSource(t.id, SOURCE_SPOTIFY)); spotifySongs.add(s); if (savedAlbum == null) savedAlbum = s.getAlbum(); } if(!savedAlbum.hasArt()) { if(alb.images != null && alb.images.size() >= 1) { Image albumImage = alb.images.get(0); LibraryService.loadArt(savedAlbum, albumImage.url, false); albumImagesUrls.put(savedAlbum, albumImage.url); } } } count-=50; if(count <= 0) break; else { offset += 50; params.put("offset", offset); userAlbums = service.getMySavedAlbums(params); } } params.put("limit", 20); params.put("offset", 0); Pager<PlaylistSimple> userPlaylists = service.getMyPlaylists(); offset = 0; count = userPlaylists.total; while(true) { //parse user playlists request response for(PlaylistSimple playlistBase : userPlaylists.items) { ArrayList<Song> thisList = new ArrayList<>(); HashMap<String, Object> map = new HashMap<>(); int trackNbr = playlistBase.tracks.total; int poffset = 0; while(trackNbr > 0) { map.put("offset", poffset); Pager<PlaylistTrack> tracks = service.getPlaylistTracks(playlistBase.owner.id, playlistBase.id, map); for(PlaylistTrack pt : tracks.items) { Track t = pt.track; if(t == null) continue; Song s; if(LibraryService.SAVE_PLAYLISTS_TO_LIBRARY) s = LibraryService.registerSong(t.artists.get(0).name, t.album.name, t.track_number, 0, t.duration_ms, t.name, new SongSources.SongSource(t.id, SOURCE_SPOTIFY)); else s = LibraryService.getSongHandle(t.name, t.album.name, t.artists.get(0).name, t.duration_ms, new SongSources.SongSource(t.id, SOURCE_SPOTIFY), t.track_number, 0); //get albumart for this song if(!s.getAlbum().hasArt()) { if(t.album.images != null && t.album.images.size() >= 1) { Image albumImage = t.album.images.get(0); LibraryService.loadArt(s.getAlbum(), albumImage.url, false); albumImagesUrls.put(s.getAlbum(), albumImage.url); } } thisList.add(s); } poffset+=100; trackNbr-=100; } Playlist list = new Playlist(playlistBase.name, thisList); list.getSources().addSource(new SongSources.SongSource(playlistBase.id, SOURCE_SPOTIFY)); if(playlistBase.collaborative) list.setCollaborative(); if(playlistBase.owner != null) if(!playlistBase.owner.id.equals(mePrivate.id)) list.setOwner(playlistBase.owner.display_name == null ? playlistBase.owner.id : playlistBase.owner.display_name, playlistBase.owner.id); spotifyPlaylists.add(list); LibraryService.getPlaylists().add(list); if(LibraryService.currentCallback != null) LibraryService.currentCallback.onLibraryChange(); if(playlistBase.images != null && playlistBase.images.size() >= 1) LibraryService.loadArt(list, playlistBase.images.get(0).url, false); } count -= 20; if(count <= 0) break; else { offset += 20; params.put("offset", offset); userPlaylists = service.getMyPlaylists(params); } } // cache all spotifySongs and spotifyPlaylists try { //library songs BufferedWriter bw = new BufferedWriter(new FileWriter((spotifyCacheFile))); //cache version bw.write("Blade" + CACHE_SEPARATOR + "edalB" + CACHE_SEPARATOR + "SPCACHE" + CACHE_SEPARATOR + "2"); bw.newLine(); for(Song song : spotifySongs) { bw.write(song.getTitle() + CACHE_SEPARATOR + song.getAlbum().getName() + CACHE_SEPARATOR + song.getArtist().getName() + CACHE_SEPARATOR + song.getFormat() + CACHE_SEPARATOR + song.getTrackNumber() + CACHE_SEPARATOR + song.getDuration() + CACHE_SEPARATOR + song.getSources().getSpotify().getId() + CACHE_SEPARATOR + albumImagesUrls.get(song.getAlbum()) + CACHE_SEPARATOR); //todo : find a better way to cache album image urls : this is caching url once per song.... bw.newLine(); } bw.close(); //playlists if(spotifyPlaylistsCache.exists() && spotifyPlaylistsCache.listFiles() != null) for(File f : spotifyPlaylistsCache.listFiles()) f.delete(); for(Playlist p : spotifyPlaylists) { cachePlaylist(p); } } catch(IOException e) { Log.println(Log.ERROR, "[BLADE-SPOTIFY]", "Error while writing cache !"); } } catch (RetrofitError error) { error.printStackTrace(); if(error.getResponse() == null) return; if(error.getResponse().getStatus() == 401) { Log.println(Log.INFO, "[BLADE-SPOTIFY]", "Actualizing token."); refreshSpotifyToken(); registerSongs(); return; } System.err.println("ERROR BODY : " + error.getBody()); SpotifyError spotifyError = SpotifyError.fromRetrofitError(error); spotifyError.printStackTrace(); System.err.println("SPOTIFY ERROR DETAILS : " + spotifyError.getErrorDetails()); } } @Override public List<LibraryObject> query(String query) { ArrayList<LibraryObject> tr = new ArrayList<>(); HashMap<Album, String> urls = new HashMap<>(); try { if(isAvailable()) { //request from spotify TracksPager tracks = spotifyApi.getService().searchTracks(query); AlbumsPager albums = spotifyApi.getService().searchAlbums(query); //ArtistsPager artists = spotifyApi.getService().searchArtists(query); //handle returned data for(Track t : tracks.tracks.items) { Song song = LibraryService.getSongHandle(t.name, t.album.name, t.artists.get(0).name, t.duration_ms, new SongSources.SongSource(t.id, SOURCE_SPOTIFY), t.track_number, 0); tr.add(song); //System.out.println("[SART] Song : " + song.getName() + " - " + song.getAlbum() + " - " + song.getArtist() + " , hasArt = " + song.getAlbum().hasArt() + " art = " + song.getAlbum().getArtUri()); if(!song.getAlbum().hasArt()) { if(t.album.images.get(0) != null) urls.put(song.getAlbum(), t.album.images.get(0).url); //System.out.println("[SART] Album " + song.getAlbum() + " (artist = " + song.getArtist() + ") : img = " + t.album.images.get(0).url); } } for(kaaes.spotify.webapi.android.models.AlbumSimple a : albums.albums.items) { Album album = null; Pager<Track> albumTracks = spotifyApi.getService().getAlbumTracks(a.id); for(Track t : albumTracks.items) { Song currentSong = LibraryService.getSongHandle(t.name, a.name, t.artists.get(0).name, t.duration_ms, new SongSources.SongSource(t.id, SOURCE_SPOTIFY), t.track_number, 0); if(album == null) album = currentSong.getAlbum(); } if(!album.hasArt()) { if(a.images != null && a.images.size() >= 1) { Image albumImage = a.images.get(0); if(albumImage != null) urls.put(album, a.images.get(0).url); } } } } } catch(RetrofitError e) { if(e.getResponse() != null) { if(e.getResponse().getStatus() == 401) { refreshSpotifyToken(); return query(query); } } e.printStackTrace(); } new Thread() { public void run() { Looper.prepare(); for(Album a : urls.keySet()) { System.out.println("[SART] Loading albumArt for " + a.getName() + " - " + a.getArtist().getName() + " ; img = " + urls.get(a)); LibraryService.loadArt(a, urls.get(a), false); } } }.start(); return tr; } @Override public void addSongsToPlaylist(List<Song> songs, Playlist list, OperationCallback callback) { if(list.getSources().getSourceByPriority(0).getSource() != SOURCE_SPOTIFY) {callback.onFailure(); return;} if(!list.isMine() && !list.isCollaborative()) {callback.onFailure(); return;} new Thread() { public void run() { Looper.prepare(); HashMap<String, Object> parameters = new HashMap<>(); String sSongs = ""; for(Song s : songs) { SongSources.SongSource spot = s.getSources().getSpotify(); if(spot == null) { if(SOURCE_SPOTIFY.searchForSong(s)) spot = s.getSources().getSpotify(); else {songs.remove(s); continue;} } sSongs += ("spotify:track:" + spot.getId() + ","); } if(sSongs.length() == 0) {callback.onFailure(); return;} sSongs = sSongs.substring(0, sSongs.length()-1); parameters.put("uris", sSongs); try { spotifyApi.getService().addTracksToPlaylist((list.isCollaborative() ? (String) list.getOwnerID() : mePrivate.id), (String) list.getSources().getSpotify().getId(), parameters, new HashMap<>()); //add song to RAM list list.getContent().addAll(songs); //add song to cached list cachePlaylist(list); callback.onSucess(null); } catch(RetrofitError error) { if(error.getResponse() == null) {callback.onFailure(); return;} if(error.getResponse().getStatus() == 401) { refreshSpotifyToken(); addSongsToPlaylist(songs, list, callback); } else callback.onFailure(); } catch(Exception ex) { if(mePrivate == null) checkAndRefreshSpotifyToken(); callback.onFailure(); } } }.start(); } @Override public void removeSongFromPlaylist(Song song, Playlist list, OperationCallback callback) { if(list.getSources().getSourceByPriority(0).getSource() != SOURCE_SPOTIFY) {callback.onFailure(); return;} if(!list.isMine() && !list.isCollaborative()) {callback.onFailure(); return;} TracksToRemove tracksToRemove = new TracksToRemove(); tracksToRemove.tracks = new ArrayList<>(); TrackToRemove trackToRemove = new TrackToRemove(); trackToRemove.uri = "spotify:track:" + song.getSources().getSpotify().getId(); tracksToRemove.tracks.add(trackToRemove); new Thread() { public void run() { Looper.prepare(); try { spotifyApi.getService().removeTracksFromPlaylist((list.isCollaborative() ? (String) list.getOwnerID() : mePrivate.id), (String) list.getSources().getSpotify().getId(), tracksToRemove); //remove song from RAM list and notify change list.getContent().remove(song); if(LibraryService.currentCallback != null) LibraryService.currentCallback.onLibraryChange(); //remove song from cached list (by rewriting the whole list) cachePlaylist(list); callback.onSucess(null); } catch(RetrofitError error) { if(error.getResponse() == null) {callback.onFailure(); return;} if(error.getResponse().getStatus() == 401) { refreshSpotifyToken(); removeSongFromPlaylist(song, list, callback); } else callback.onFailure(); } } }.start(); } public void checkAndRefreshSpotifyToken() { //check for token validity try { mePrivate = spotifyApi.getService().getMe(); } catch(RetrofitError e) { if(e.getResponse() != null && e.getResponse().getStatus() == 401) { Log.println(Log.INFO, "[BLADE-SPOTIFY]", "Actualizing token."); refreshSpotifyToken(); mePrivate = spotifyApi.getService().getMe(); } } } private void refreshSpotifyToken() { try { URL apiUrl = new URL("https://accounts.spotify.com/api/token"); HttpsURLConnection urlConnection = (HttpsURLConnection) apiUrl.openConnection(); urlConnection.setDoInput(true); urlConnection.setDoOutput(true); urlConnection.setRequestMethod("POST"); //write POST parameters OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream()); BufferedWriter writer = new BufferedWriter (new OutputStreamWriter(out, "UTF-8")); writer.write("grant_type=refresh_token&"); writer.write("refresh_token=" + SPOTIFY_REFRESH_TOKEN + "&"); writer.write("client_id=" + SPOTIFY_CLIENT_ID + "&"); writer.write("client_secret=" + "3166d3b40ff74582b03cb23d6701c297"); writer.flush(); writer.close(); out.close(); urlConnection.connect(); System.out.println("[BLADE] [AUTH-REFRESH] Result : " + urlConnection.getResponseCode() + " " + urlConnection.getResponseMessage()); BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); String result = reader.readLine(); reader.close(); result = result.substring(1); result = result.substring(0, result.length()-1); String[] results = result.split(","); for(String param : results) { if(param.startsWith("\"access_token\":\"")) { param = param.replaceFirst("\"access_token\":\"", ""); param = param.replaceFirst("\"", ""); SPOTIFY_USER_TOKEN = param; spotifyApi.setAccessToken(SPOTIFY_USER_TOKEN); SharedPreferences pref = LibraryService.appContext.getSharedPreferences(SettingsActivity.PREFERENCES_ACCOUNT_FILE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = pref.edit(); editor.putString("spotify_token", SPOTIFY_USER_TOKEN); editor.commit(); } } } catch(Exception e) { e.printStackTrace(); } } @Override public boolean searchForSong(Song s) { if(s.getSources().getSpotify() != null) return true; try { HashMap<String, Object> args = new HashMap<>(); args.put("limit", 1); List<Track> t = Source.SOURCE_SPOTIFY.spotifyApi.getService().searchTracks(s.getTitle() + " album:" + s.getAlbum().getName() + " artist:" + s.getArtist().getName(), args).tracks.items; if(t != null && t.size() > 0 && t.get(0) != null) { SongSources.SongSource source = new SongSources.SongSource(t.get(0).id, Source.SOURCE_SPOTIFY); s.getSources().addSource(source); s.getArtist().getSources().addSource(source); s.getAlbum().getSources().addSource(source); return true; } } catch(RetrofitError e) {} //ignored return false; } private void cachePlaylist(Playlist p) { try { File thisPlaylist = new File(spotifyPlaylistsCache.getAbsolutePath() + "/" + p.getName()); thisPlaylist.createNewFile(); BufferedWriter pwriter = new BufferedWriter(new FileWriter(thisPlaylist)); pwriter.write((String) p.getSources().getSpotify().getId()); pwriter.newLine(); pwriter.write(String.valueOf(p.isMine())); pwriter.newLine(); if(!p.isMine()) { pwriter.write(p.getOwner()); pwriter.newLine(); pwriter.write((String) p.getOwnerID()); pwriter.newLine(); } pwriter.write(String.valueOf(p.isCollaborative())); pwriter.newLine(); for(Song song : p.getContent()) { pwriter.write(song.getTitle() + CACHE_SEPARATOR + song.getAlbum().getName() + CACHE_SEPARATOR + song.getArtist().getName() + CACHE_SEPARATOR + song.getFormat() + CACHE_SEPARATOR + song.getTrackNumber() + CACHE_SEPARATOR + song.getDuration() + CACHE_SEPARATOR + song.getSources().getSpotify().getId() + CACHE_SEPARATOR + "" + CACHE_SEPARATOR); //todo : find a way to cache album image url for playlists pwriter.newLine(); } pwriter.close(); } catch (IOException e) {e.printStackTrace();} } @Override public void addPlaylist(String name, OperationCallback callback, boolean isPublic, boolean isCollaborative) { HashMap<String, Object> params = new HashMap<>(); params.put("name", name); params.put("public", isPublic); params.put("collaborative", isCollaborative); //params.put("description", desc); //check connection if(mePrivate == null) { Toast.makeText(LibraryService.appContext, R.string.spotify_connection_error, Toast.LENGTH_SHORT).show(); return; } new Thread() { public void run() { try { kaaes.spotify.webapi.android.models.Playlist p = spotifyApi.getService().createPlaylist(mePrivate.id, params); //add playlist to RAM Playlist playlist = new Playlist(name, new ArrayList<>()); if(isCollaborative) playlist.setCollaborative(); playlist.getSources().addSource(new SongSources.SongSource(p.id, SOURCE_SPOTIFY)); LibraryService.getPlaylists().add(playlist); if(LibraryService.currentCallback != null) LibraryService.currentCallback.onLibraryChange(); //add playlist to cache cachePlaylist(playlist); callback.onSucess(playlist); } catch(RetrofitError error) { if(error.getResponse() == null) {callback.onFailure(); return;} if(error.getResponse().getStatus() == 401) { refreshSpotifyToken(); addPlaylist(name, callback, isPublic, isCollaborative); } else callback.onFailure(); } } }.start(); } @Override public void removePlaylist(Playlist list, OperationCallback callback) { if(list.getSources().getSourceByPriority(0).getSource() != SOURCE_SPOTIFY) {callback.onFailure(); return;} new Thread() { public void run() { try { spotifyApi.getService().unfollowPlaylist(list.isMine() ? mePrivate.id : (String) list.getOwnerID(), (String) list.getSources().getSpotify().getId()); //remove playlist from RAM LibraryService.getPlaylists().remove(list); if(LibraryService.currentCallback != null) LibraryService.currentCallback.onLibraryChange(); //remove playlist from cache File thisPlaylist = new File(spotifyPlaylistsCache.getAbsolutePath() + "/" + list.getName()); thisPlaylist.delete(); callback.onSucess(null); } catch(RetrofitError error) { if(error.getResponse() == null) {callback.onFailure(); return;} if(error.getResponse().getStatus() == 401) { refreshSpotifyToken(); removePlaylist(list, callback); } else callback.onFailure(); } } }.start(); } public void addSongToLibrary(Song song, OperationCallback callback) { new Thread() { public void run() { try { SongSources.SongSource spot = song.getSources().getSpotify(); if(spot == null) { if(!searchForSong(song)) {callback.onFailure(); return;} spot = song.getSources().getSpotify(); } spotifyApi.getService().addToMySavedTracks((String) spot.getId()); //add to library if(song.isHandled()) { //todo : find a better way (registersong is heavy) ; that is just lazyness LibraryService.registerSong(song.getArtist().getName(), song.getAlbum().getName(), song.getTrackNumber(), song.getYear(), song.getDuration(), song.getName(), spot); } //add to cache try { BufferedWriter writer = new BufferedWriter(new FileWriter(spotifyCacheFile, true)); writer.write(song.getTitle() + CACHE_SEPARATOR + song.getAlbum().getName() + CACHE_SEPARATOR + song.getArtist().getName() + CACHE_SEPARATOR + song.getFormat() + CACHE_SEPARATOR + song.getTrackNumber() + CACHE_SEPARATOR + song.getDuration() + CACHE_SEPARATOR + song.getSources().getSpotify().getId() + CACHE_SEPARATOR); writer.newLine(); writer.close(); } catch(IOException e) {} callback.onSucess(null); } catch(RetrofitError error) { if(error.getResponse() == null) {callback.onFailure(); return;} if(error.getResponse().getStatus() == 401) { refreshSpotifyToken(); addSongToLibrary(song, callback); } else callback.onFailure(); } } }.start(); } public void removeSongFromLibrary(Song song, OperationCallback callback) { new Thread() { public void run() { try { SongSources.SongSource spot = song.getSources().getSpotify(); if(spot == null) {callback.onFailure(); return;} if(!spot.getLibrary()) {callback.onFailure(); return;} spotifyApi.getService().removeFromMySavedTracks((String) spot.getId()); //remove from cache try { StringBuilder newContent = new StringBuilder(); BufferedReader reader = new BufferedReader(new FileReader(spotifyCacheFile)); String toCompare = song.getTitle() + CACHE_SEPARATOR + song.getAlbum().getName() + CACHE_SEPARATOR + song.getArtist().getName() + CACHE_SEPARATOR + song.getFormat() + CACHE_SEPARATOR + song.getTrackNumber() + CACHE_SEPARATOR + song.getDuration() + CACHE_SEPARATOR + song.getSources().getSpotify().getId() + CACHE_SEPARATOR + "\n"; while(reader.ready()) { String toAdd = (reader.readLine() + "\n"); if(toAdd.equals(toCompare)) toAdd = ""; newContent.append(toAdd); } reader.close(); BufferedWriter writer = new BufferedWriter(new FileWriter(spotifyCacheFile)); writer.write(newContent.toString()); writer.close(); } catch(IOException e) {} //remove from library LibraryService.unregisterSong(song, spot); callback.onSucess(null); } catch(RetrofitError error) { if(error.getResponse() == null) {callback.onFailure(); return;} if(error.getResponse().getStatus() == 401) { refreshSpotifyToken(); addSongToLibrary(song, callback); } else callback.onFailure(); } } }.start(); } public void addAlbumToLibrary(Album album, OperationCallback callback) { callback.onFailure(); } public void removeAlbumFromLibrary(Album album, OperationCallback callback) { callback.onFailure(); } public List<LibraryObject> getFeaturedContent() { ArrayList<LibraryObject> tr = new ArrayList<>(); for(PlaylistSimple playlistSimple : spotifyApi.getService().getFeaturedPlaylists().playlists.items) { ArrayList<Song> thisList = new ArrayList<>(); HashMap<String, Object> map = new HashMap<>(); int trackNbr = playlistSimple.tracks.total; int poffset = 0; while(trackNbr > 0) { map.put("offset", poffset); Pager<PlaylistTrack> tracks = spotifyApi.getService().getPlaylistTracks(playlistSimple.owner.id, playlistSimple.id, map); for(PlaylistTrack pt : tracks.items) { Track t = pt.track; if(t == null) continue; Song s = LibraryService.getSongHandle(t.name, t.album.name, t.artists.get(0).name, t.duration_ms, new SongSources.SongSource(t.id, SOURCE_SPOTIFY), t.track_number, 0); thisList.add(s); } poffset+=100; trackNbr-=100; } tr.add(new Playlist(playlistSimple.name, thisList)); } return tr; } }