Skip to content

Latest commit

History

261 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Android developers
You can use this library in your Android app. However, you should have a look at the Android Wrapper project built by kaaes as it's made specifically for Android. See also Spotify's Android SDK.

Spotify Web API Java Build Status

This is a Java wrapper/client for the Spotify Web API.

It includes helper functions to do:

Music metadata

  • Albums, artists, and tracks
  • Albums for a specific artist
  • Top tracks for a specific artist
  • Artists similar to a specific artist

Profiles

  • User's emails, product type, follower count, display name, image

Search

  • Albums, artists, and tracks

Playlists

  • Get a user's playlists
  • Get a specific playlist
  • Create playlists
  • Change playlist details
  • Add tracks to a playlist

Your Music library

  • Get tracks that are in the signed in user's Your Music library
  • Add/remove tracks to/from the user's Your Music library
  • Check if a track is in the user's Your Music library

Browse

  • Get new releases
  • Get featured playlists

Authentication

Some methods require authentication, which can be done using these flows:

Even though authentication isn't always necessary, it always gives benefits such as an increased rate limit.

Install

As of version 1.4.5 the artifact is available through Maven Central. (All artifacts are available at https://oss.sonatype.org/#nexus-search;quick~spotify-web-api-java.)

Get the latest version:

Maven users

<dependency>
<groupId>se.michaelthelin.spotify</groupId>
<artifactId>spotify-web-api-java</artifactId>
<version>1.5.0</version>
</dependency>

Gradle users

This project uses net.sf.json-lib for marshalling, which may cause your Android build to fail if you're using Android Studio 1.0 (relevant Stack Overflow post). It may therefore necessary to make an exclusion of common-beanutils when setting up this library as a dependency.

compile('se.michaelthelin.spotify:spotify-web-api-java:1.5.0') {
exclude group: "commons-beanutils", module: "commons-beanutils"
}
compile 'commons-beanutils:commons-beanutils:20030211.134440'

Thanks a lot digitaldesaster for coming up with the workaround.

Usage

Asynchronous, using Guava's future API.

// Create an API instance. The default instance connects to https://api.spotify.com/.Apiapi = Api.DEFAULT_API; // Create a request object for the type of request you want to makeAlbumRequestrequest = api.getAlbum("7e0ij2fpWaxOEHv5fUYZjd").build();
// Retrieve a future for an albumSettableFuture<Album> albumFuture = request.getAsync();
// Create callbacks in case of success or failureFutures.addCallback(albumFuture, newFutureCallback<Album>() {
// Print the genres of the album call is successfulpublicvoidonSuccess(Albumalbum) {
List<String> genres = album.getGenres(); for (Stringgenre : genres) {
System.out.println(genre);
}
}
// In case of failurepublicvoidonFailure(Throwablethrown) {
System.out.println("Could not get albums.");
}
});

Synchronous

// Create an API instance. The default instance connects to https://api.spotify.com/.Apiapi = Api.DEFAULT_API; // Create a request object for the type of request you want to makeAlbumRequestrequest = api.getAlbum("7e0ij2fpWaxOEHv5fUYZjd").build();
// Retrieve an albumtry {
Albumalbum = request.get();
// Print the genres of the albumList<String> genres = album.getGenres(); for (Stringgenre : genres) {
System.out.println(genre);
};
} catch (Exceptione) {
System.out.println("Could not get albums.");
}

Authorization

Client ID, client secret and redirect URI are set on the API object when it's created, and will be used by functions that need them, e.g. when retrieving or refreshing an access token. You can retrieve these credentials by creating an application here.

Apiapi = Api.builder()
.clientId("<your_client_id>")
.clientSecret("<your_client_secret>")
.redirectURI("<your_redirect_uri>")
.build();

Access token and refresh token can also be set using the Api builder.

Apiapi = Api.builder()
.accessToken("<your_access_token>")
.refreshToken("<your_refresh_token>")
.build();

There are two ways of retrieving access tokens. The first one is the Client Credentials flow, which is useful when a specific user's permission isn't needed, for example when simply looking up albums or searching for tracks. The second one is the Authorization Code Grant, which requires a user to sign in and authorize the application.

Client Credentials flow

Use the Client Credentials flow when the requests doesn't require permission from a specific user. This flow doesn't return an refresh token. However, there are still benefits with using an access token when making requests as it gives the application a higher rate limit.

The example below implements the asynchronous version of the Client Credentials flow.

finalStringclientId = "<insert client id>";
finalStringclientSecret = "<insert client secret>";
finalApiapi = Api.builder()
.clientId(clientId)
.clientSecret(clientSecret)
.build();
/* Create a request object. */finalClientCredentialsGrantRequestrequest = api.clientCredentialsGrant().build();
/* Use the request object to make the request, either asynchronously (getAsync) or synchronously (get) */finalSettableFuture<ClientCredentials> responseFuture = request.getAsync();
/* Add callbacks to handle success and failure */Futures.addCallback(responseFuture, newFutureCallback<ClientCredentials>() {
@OverridepublicvoidonSuccess(ClientCredentialsclientCredentials) {
/* The tokens were retrieved successfully! */System.out.println("Successfully retrieved an access token! " + clientCredentials.getAccessToken());
System.out.println("The access token expires in " + clientCredentials.getExpiresIn() + " seconds");
/* Set access token on the Api object so that it's used going forward */api.setAccessToken(clientCredentials.getAccessToken());
/* Please note that this flow does not return a refresh token. * That's only for the Authorization code flow */
}
@OverridepublicvoidonFailure(Throwablethrowable) {
/* An error occurred while getting the access token. This is probably caused by the client id or * client secret is invalid. */
}
});

When the access token has been set on the Api object, it will be used automatically when making requests. This wrapper leaves it to the implementer to handle expiration time. If a token expires, the access token on the Api object either needs to be set again, or set to null so that it's not used when making requests.

Authorization Code Grant

Using the Authorization Code Grant to retrieve an access token is necessary if the requests are bound to a specific user, e.g. the retrieving information about the current user (.getMe()). Using this flow returns a refresh token, which can be used to renew the access token before it expires.

Retrieving an Authorization code

The Authorization Code Grant flow requires a code, which is returned to the redirectURI when the user has authorized the application. This wrapper provides a helper function to create the URL which the user needs to go to in order to authorize the application.

finalStringclientId = "<your_client_id>";
finalStringclientSecret = "<your_client_secret>";
finalStringredirectURI = "<your_redirect_uri>";
finalApiapi = Api.builder()
.clientId(clientId)
.clientSecret(clientSecret)
.redirectURI(redirectURI)
.build();
/* Set the necessary scopes that the application will need from the user */finalList<String> scopes = Arrays.asList("user-read-private", "user-read-email");
/* Set a state. This is used to prevent cross site request forgeries. */finalStringstate = "someExpectedStateString";
StringauthorizeURL = api.createAuthorizeURL(scopes, state);
/* Continue by sending the user to the authorizeURL, which will look something like https://accounts.spotify.com:443/authorize?client_id=5fe01282e44241328a84e7c5cc169165&response_type=code&redirect_uri=https://example.com/callback&scope=user-read-private%20user-read-email&state=some-state-of-my-choice */
Retrieving the access token

When the code has been retrieved from the user, it can be used to get an access token as well as a refresh token.

/* Application details necessary to get an access token */finalStringcode = "<insert code>";
/* Make a token request. Asynchronous requests are made with the .getAsync method and synchronous requests * are made with the .get method. This holds for all type of requests. */finalSettableFuture<AuthorizationCodeCredentials> authorizationCodeCredentialsFuture = api.authorizationCodeGrant(code).build().getAsync();
/* Add callbacks to handle success and failure */Futures.addCallback(authorizationCodeCredentialsFuture, newFutureCallback<AuthorizationCodeCredentials>() {
@OverridepublicvoidonSuccess(AuthorizationCodeCredentialsauthorizationCodeCredentials) {
/* The tokens were retrieved successfully! */System.out.println("Successfully retrieved an access token! " + authorizationCodeCredentials.getAccessToken());
System.out.println("The access token expires in " + authorizationCodeCredentials.getExpiresIn() + " seconds");
System.out.println("Luckily, I can refresh it using this refresh token! " + authorizationCodeCredentials.getRefreshToken());
/* Set the access token and refresh token so that they are used whenever needed */api.setAccessToken(authorizationCodeCredentials.getAccessToken());
api.setRefreshToken(authorizationCodeCredentials.getRefreshToken());
}
@OverridepublicvoidonFailure(Throwablethrowable) {
/* Let's say that the client id is invalid, or the code has been used more than once, * the request will fail. Why it fails is written in the throwable's message. */
}
});

More examples

This section includes example requests for every helper function. Please note that the requests in the examples doesn't necessarily use all possible options when making the request. Since the requests use the builder pattern, optional parameters can be appended using the builder. Required parameters are parameters on the api method (e.g. searchTracks).

The examples below use the synchronous version of the request (.get). In order to make asynchronous request, simply use the request's .getAsync() method instead.

Please refer to the documentation linked in the headline of each example to read about potential authorization requirements.

Searching tracks, artists and albums

finalTrackSearchRequestrequest = api.searchTracks("Mr. Brightside").market("US").build();
try {
finalPage<Track> trackSearchResult = request.get();
System.out.println("I got " + trackSearchResult.getTotal() + " results!");
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalArtistSearchRequestrequest = api.searchArtists("tania bowra").market("SE").limit(10).build();
try {
finalPage<Artist> artistSearchResult = request.get();
finalList<Artist> artists = artistSearchResult.getItems();
System.out.println("I've found " + artistSearchResult.getTotal() + " artists!");
for (Artistartist : artists) {
System.out.println(artist.getName());
}
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalAlbumSearchRequestrequest = api.searchAlbums("black album").offset(0).limit(3).build();
try {
finalPage<SimpleAlbum> albumSearchResult = request.get();
System.out.println("Printing results..");
for (SimpleAlbumalbum : albumSearchResult.getItems)) {
System.out.println(album.getName());
}
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}

Lookup of albums, tracks, artists, playlists and users

finalStringalbumId = "0sNOF9WDwhWunNAHPD3Baj";
finalAlbumRequestrequest = api.getAlbum(albumId).build();
try {
finalAlbumalbum = request.get();
System.out.println("Retrieved album " + album.getName());
System.out.println("Its popularity is " + album.getPopularity()); } catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalTrackRequestrequest = api.getTrack("0eGsygTp906u18L0Oimnem").build();
try {
finalTracktrack = request.get();
System.out.println("Retrieved track " + track.getName());
System.out.println("Its popularity is " + track.getPopularity());
if (track.isExplicit()) {
System.out.println("This track is explicit!");
} else {
System.out.println("It's OK, this track isn't explicit.");
}
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalArtistrequest = api.getArtist("0LcJLqbBmaGUft1e9Mm8HV").build();
try {
finalArtistartist = request.get();
System.out.println("This artist's name is " + artist.getName());
// Print URLs to the artist's imagesfinalList<Image> images = artist.getImages();
for (Imageimage : images) {
System.out.println(image.getUrl());
}
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalPlaylistRequestrequest = api.getPlaylist("thelinmichael", "3ktAYNcRHpazJ9qecm3ptn").build();
try {
finalPlaylistplaylist = request.get();
System.out.println("Retrieved playlist " + playlist.getName());
System.out.println(playlist.getDescription());
System.out.println("It contains " + playlist.getTracks().getTotal() + " tracks");
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalUserRequestrequest = api.getUser("wizzler").build();
try {
finalUseruser = request.get();
System.out.println("This user's Spotify URI is " + user.getUri()); } catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}

Lookup of of several albums, tracks, and artists

finalAlbumsRequest = api.getAlbums("41MnTivkwTO3UUJ8DrqEJJ", "0ntmUPwjfE9iGGM9qHglCm").get();
try {
finalList<Album> albums = request.get();
finalAlbumfirstAlbum = albums.get(0);
System.out.println(firstAlbum.getName());
System.out.println("The artists on this albums are");
for (SimpleArtistartist : firstAlbum.getArtists()) {
System.out.println(artist.getName());
}
finalAlbumsecondAlbum = albums.get(1);
System.out.println(secondAlbum.getName());
System.out.println("The artists on this albums are");
for (SimpleArtistartist : secondAlbum.getArtists()) {
System.out.println(artist.getName());
}
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalTracksRequestrequest = api.getTracks("0eGsygTp906u18L0Oimnem", "1lDWb6b6ieDQ2xT7ewTC3G").build();
try {
finalList<Track> tracks = request.get();
for (Tracktrack : tracks) {
System.out.println(track.getName());
}
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalArtistsRequestrequest = api.getArtists("0oSGxfWSnnOXhD2fKuz2Gy", "3dBVyJ7JuOMt4GE9607Qin").build();
try {
finalList<Artist> artists = request.get();
for (Artistartist : artists) {
System.out.println(artist.getName() + " has popularity " + artist.getPopularity());
}
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}

Other lookup methods

finalRelatedArtistsRequestrequest = api.getArtistRelatedArtists("0qeei9KQnptjwb8MgkqEoy").build();
try {
finalList<Artist> artists = request.get();
if (numbers.isEmpty()) {
System.out.println("Didn't find any similar artists!");
} else {
System.out.println("The related artists are:");
for (Artistartist : artists) {
System.out.println(artist.getName());
}
}
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}

The attributes that are loaded onto the User object depends on the permissions given by the user to the application.

finalCurrentUserRequestrequest = api.getMe().build();
try {
finalUseruser = request.get();
System.out.println("Display name: " + user.getDisplayName());
System.out.println("Email: " + user.getEmail());
System.out.println("Images:");
for (Imageimage : user.getImages()) {
System.out.println(image.getUrl());
}
System.out.println("This account is a " + user.getProduct() + " account");
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalPlaylistTracksRequestrequest = api.getPlaylistTracks("thelinmichael", "3ktAYNcRHpazJ9qecm3ptn").build();
try {
finalPage<PlaylistTrack> page = request.get();
finalList<PlaylistTrack> playlistTracks = page.getItems();
for (PlaylistTrackplaylistTrack : playlistTracks) {
System.out.println(playlistTrack.getTrack().getName());
}
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalUserPlaylistsRequestrequest = api.getPlaylistsForUser("thelinmichael").build();
try {
finalPage<SimplePlaylist> playlistsPage = request.get();
for (SimplePlaylistplaylist : playlistsPage.getItems()) {
System.out.println(playlist.getName());
}
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}

Playlist manipulation

finalList<String> tracksToAdd = Arrays.asList("spotify:track:4BYGxv4rxSNcTgT3DsFB9o","spotify:track:0BG2iE6McPhmAEKIhfqy1X");
// Index starts at 0finalintinsertIndex = 3;
finalAddTrackToPlaylistRequestrequest = api.addTracksToPlaylist("thelinmichael", "5ieJqeLJjjI8iJWaxeBLuK", tracksToAdd)
.position(insertIndex)
.build();
try {
request.get();
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalPlaylistCreationRequestrequest = api.createPlaylist("thelinmichael", "title")
.publicAccess(true)
.build();
try {
finalPlaylistplaylist = request.get();
System.out.println("You just created this playlist!");
System.out.println("Its title is " + playlist.getName());
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
finalApiapi = Api.builder().accessToken(accessToken).build();
ChangePlaylistDetailsRequestrequest = api
.changePlaylistDetails("thelinmichael", "3ktAYNcRHpazJ9qecm3ptn")
.publicAccess(true)
.name("Testing playlist name change")
.build();
try {
Stringresponse = request.get();
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}

Your Music library

Apiapi = Api.builder().accessToken(accessToken).build();
GetMySavedTracksRequestrequest = api.getMySavedTracks()
.limit(5)
.offset(1)
.build();
try {
Page<LibraryTrack> libraryTracks = request.get();
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
Apiapi = Api.builder().accessToken(accessToken).build();
ContainsMySavedTracksRequestrequest = api.containsMySavedTracks(
Arrays.asList("0udZHhCi7p1YzMlvI4fXoK", "1e1VmyiAuPyM4SHhySP1oU"))
.build();
try {
List<Boolean> containedTracks = request.get();
System.out.println("0udZHhCi7p1YzMlvI4fXoK is in the library: " + containedTracks.get(0));
System.out.println("1e1VmyiAuPyM4SHhySP1oU is in the library: " + containedTracks.get(1));
} catch (Exceptione) {
System.out.println("Something went wrong!" + e.getMessage());
}
Apiapi = Api.builder().accessToken(accessToken).build();
List<String> tracksToAdd = Arrays.asList("4BYGxv4rxSNcTgT3DsFB9o", "0BG2iE6McPhmAEKIhfqy1X");
AddToMySavedTracksRequestrequest = api.addToMySavedTracks(tracksToAdd).build();
try {
// Add tracks synchronouslyrequest.get();
System.out.println("Added tracks to the user's Your Music library!');} catch (Exception e) { System.out.println("Somethingwentwrong!" + e.getMessage());
}
Apiapi = Api.builder().accessToken(accessToken).build();
List<String> tracksToRemove = Arrays.asList("4BYGxv4rxSNcTgT3DsFB9o", "0BG2iE6McPhmAEKIhfqy1X");
RemoveFromMySavedTracksRequestrequest = api.removeFromMySavedTracks(tracksToAdd).build();
try {
// Remove tracks synchronouslyrequest.get();
System.out.println("Removed tracks from the user's Your Music library!');} catch (Exception e) { System.out.println("Somethingwentwrong! " + e.getMessage());
}

Browse

Apiapi = Api.builder().accessToken(accessToken).build();
// Create a request to get five new releases in SwedenfinalNewReleasesRequestrequest = api.getNewReleases()
.limit(5)
.offset(0)
.country("SE")
.build();
try {
NewReleasesnewReleases = request.get();
Page<SimpleAlbum> albums = newReleases.getAlbums();
// Print the name of the albumsSystem.out.println(albums.get(0).getName());
System.out.println(albums.get(1).getName());
System.out.println(albums.get(2).getName());
} catch(Exceptione) {
System.out.println("Something went wrong! " + e.getMessage());
}
Apiapi = Api.builder().accessToken(accessToken).build();
Calendarcalendar = Calendar.getInstance();
calendar.set(2014, 9, 23, 9, 0, 0);
Datetimestamp = calendar.getTime();
finalFeaturedPlaylistsRequestrequest = api.getFeaturedPlaylists()
.limit(1)
.offset(1)
.country("SE")
.timestamp(timestamp)
.build();
try {
FeaturedPlaylistsfeaturedPlaylists = request.get();
System.out.println("Message for this set of playlists: " + featuredPlaylists.getMessage());
} catch (Exceptione) {
System.out.println("Something went wrong! " + e.getMessage());
}

Development

Any additions to this project will be happily received. Make sure that your code is covered by a test and send a pull request! Please work on a branch that isn't master as that'll make it easier for me to merge pull requests in case there has been commits made between the time you forked and the time you send the pull request.

Requirements: Java, Maven.

Build

mvn clean install

Tests

There's a known issue in that tests cannot be run from an IDE as it doesn't pick up the mocked response files.

mvn clean test

Change log

1.5.0
  • Change response type for Add Tracks to Playlist to SnapshotResult since it now includes a snapshot_id. Code using Add Tracks to Playlist will break with this change.
1.4.21
1.4.20
1.4.19
1.4.18
1.4.17
1.4.16
  • bradnussbaum bumped Guava version and sorted out test dependencies. Cheers!
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
  • Add images to SimplePlaylist object. (Currently only returned from the Get User's Playlists endpoint)
1.4.9
  • Add available_markets to SimpleTrack object.
  • Add market parameter to Search requests.
1.4.8
  • Enable limit and offset parameters when retrieving a playlist's tracks.
1.4.7
  • Get a user's starred tracks. (Thanks felx)
1.4.5
  • Distribute via Maven Central.
  • Use query parameter to add tracks.
1.4.1
  • Add available_markets to SimpleAlbum
  • Add show dialog parameter to authorization URL
1.4.0
  • Enable setting limit and offset when requesting a user's playlists.
1.3
1.2

About

A Java wrapper for the new Spotify Web API.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages