Skip to content
This repository has been archived by the owner on Jan 13, 2021. It is now read-only.

Commit

Permalink
Merge pull request #20 from FelixGail/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
FelixGail authored Dec 23, 2017
2 parents da1c322 + 23e7a3d commit 6152ab2
Show file tree
Hide file tree
Showing 33 changed files with 740 additions and 471 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Unofficial GPlayMusic API in JAVA
[![CircleCI](https://img.shields.io/circleci/project/github/FelixGail/gplaymusic/master.svg)](https://circleci.com/gh/FelixGail/gplaymusic/tree/master)
[![GitHub release](https://img.shields.io/github/release/FelixGail/gplaymusic.svg)](https://github.com/FelixGail/gplaymusic/releases)

This library poses as a client for the [GooglePlay](https://play.google.com/music/) app.
It can search for songs/artists/albums, modify and create playlists and stations and even
download tracks. For most activities a valid subscription to _GooglePlay All Access_ is needed.
download tracks. For most activities an active subscription to _GooglePlay All Access_ is needed.

**This project is neither supported nor endorsed by Google.**

Expand All @@ -13,12 +15,14 @@ Installation
---------------

#### Maven:
To use this library, simply add the following lines to your pom.xml:
To use this library, simply add the following lines to your pom.xml:<br><br>
[![Sonatype Nexus (Releases)](https://img.shields.io/nexus/r/https/oss.sonatype.org/com.github.felixgail/gplaymusic.svg?label=Latest%20Stable%20Version)](https://mvnrepository.com/artifact/com.github.felixgail/gplaymusic)<br>
[![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/https/oss.sonatype.org/com.github.felixgail/gplaymusic.svg?label=Latest%20Snapshot%20Version)](https://oss.sonatype.org/content/repositories/snapshots/com/github/felixgail/gplaymusic/)
```xml
<dependency>
<groupId>com.github.felixgail</groupId>
<artifactId>gplaymusic</artifactId>
<version>0.2.0</version>
<version>0.3.0</version>
</dependency>
```

Expand Down
32 changes: 11 additions & 21 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.felixgail</groupId>
<artifactId>gplaymusic</artifactId>
<version>0.2.0</version>
<version>0.3.0</version>
<packaging>jar</packaging>
<name>${project.groupId}:${project.artifactId}</name>
<url>https://github.com/felixgail/gplaymusic</url>
Expand Down Expand Up @@ -51,22 +51,11 @@
</distributionManagement>

<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>com.github.bjoernpetersen</groupId>
<artifactId>gpsoauth</artifactId>
<version>0.3.2</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
Expand All @@ -77,12 +66,6 @@
<artifactId>converter-gson</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
Expand All @@ -94,16 +77,23 @@
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>3.1.3</version>
<groupId>io.gsonfire</groupId>
<artifactId>gson-fire</artifactId>
<version>1.8.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>1.16</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
172 changes: 67 additions & 105 deletions src/main/java/com/github/felixgail/gplaymusic/api/GPlayMusic.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,34 @@

import com.github.felixgail.gplaymusic.exceptions.InitializationException;
import com.github.felixgail.gplaymusic.exceptions.NetworkException;
import com.github.felixgail.gplaymusic.model.Album;
import com.github.felixgail.gplaymusic.model.Artist;
import com.github.felixgail.gplaymusic.model.Config;
import com.github.felixgail.gplaymusic.model.DeviceInfo;
import com.github.felixgail.gplaymusic.model.Genre;
import com.github.felixgail.gplaymusic.model.PagingHandler;
import com.github.felixgail.gplaymusic.model.Playlist;
import com.github.felixgail.gplaymusic.model.PlaylistEntry;
import com.github.felixgail.gplaymusic.model.Model;
import com.github.felixgail.gplaymusic.model.PodcastSeries;
import com.github.felixgail.gplaymusic.model.Station;
import com.github.felixgail.gplaymusic.model.Track;
import com.github.felixgail.gplaymusic.model.enums.ResultType;
import com.github.felixgail.gplaymusic.model.listennow.ListenNowItem;
import com.github.felixgail.gplaymusic.model.listennow.ListenNowSituation;
import com.github.felixgail.gplaymusic.model.listennow.ListenNowStation;
import com.github.felixgail.gplaymusic.model.requests.PagingRequest;
import com.github.felixgail.gplaymusic.model.requests.SearchTypes;
import com.github.felixgail.gplaymusic.model.requests.TimeZoneOffset;
import com.github.felixgail.gplaymusic.model.requests.mutations.MutationFactory;
import com.github.felixgail.gplaymusic.model.requests.mutations.Mutator;
import com.github.felixgail.gplaymusic.model.responses.ListResult;
import com.github.felixgail.gplaymusic.model.responses.Result;
import com.github.felixgail.gplaymusic.model.responses.SearchResponse;
import com.github.felixgail.gplaymusic.util.TokenProvider;
import com.github.felixgail.gplaymusic.util.deserializer.ColorDeserializer;
import com.github.felixgail.gplaymusic.util.deserializer.ConfigDeserializer;
import com.github.felixgail.gplaymusic.util.deserializer.ListenNowStationDeserializer;
import com.github.felixgail.gplaymusic.util.deserializer.ModelPostProcessor;
import com.github.felixgail.gplaymusic.util.deserializer.ResultDeserializer;
import com.github.felixgail.gplaymusic.util.interceptor.ErrorInterceptor;
import com.github.felixgail.gplaymusic.util.interceptor.LoggingInterceptor;
import com.github.felixgail.gplaymusic.util.interceptor.RequestInterceptor;
import com.github.felixgail.gplaymusic.util.language.Language;
import com.google.gson.GsonBuilder;
import io.gsonfire.GsonFireBuilder;
import okhttp3.CipherSuite;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
Expand All @@ -44,8 +41,6 @@
import javax.validation.constraints.NotNull;
import java.awt.Color;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
Expand All @@ -56,25 +51,23 @@
* Use the {@link GPlayMusic.Builder} to create a new instance.
*/
public final class GPlayMusic {
private static GPlayMusic instance;
private GPlayService service;
private Config config;
private RequestInterceptor interceptor;
private GenreApi genreApi;
private PlaylistApi playlistApi;
private PlaylistEntryApi playlistEntryApi;
private StationApi stationApi;
private TrackApi trackApi;

private GPlayMusic(GPlayService service, RequestInterceptor interceptor) {
this.service = service;
this.interceptor = interceptor;
instance = this;
}

/**
* @return Returns the last initiated api instance or a {@link InitializationException} if none was initialized.
*/
public static GPlayMusic getApiInstance() {
if (instance == null) {
throw new InitializationException(Language.get("api.NotInitialized"));
}
return instance;
this.genreApi = new GenreApi(this);
this.playlistEntryApi = new PlaylistEntryApi(this);
this.playlistApi = new PlaylistApi(this, playlistEntryApi);
this.stationApi = new StationApi(this);
this.trackApi = new TrackApi(this);
}

public Config getConfig() {
Expand All @@ -85,6 +78,25 @@ private void setConfig(Config config) {
this.config = config;
}

public Album getAlbum(String albumID, boolean includeTracks) throws IOException {
return getService().getAlbum(albumID, includeTracks).execute().body();
}

/**
* Fetches for an artist by {@code artistID}.
*
* @param artistID {@link Artist#getArtistId()} of the artist searched for.
* @param includeAlbums whether albums of the artist shall be included in the response.
* @param numTopTracks response includes up to provided number of most heard songs in response
* @param numRelArtist response includes up to provided number of similar artist in response
* @return An executable call which returns an artist on execution.
*/
public Artist getArtist(String artistID, boolean includeAlbums, int numTopTracks, int numRelArtist)
throws IOException {
return getService().getArtist(artistID, includeAlbums, numTopTracks, numRelArtist)
.execute().body();
}

/**
* This method will return the service used to make calls to google play, and therefore allows for
* low level and asynchronous calls. Be sure to check the response for error codes.
Expand Down Expand Up @@ -120,17 +132,6 @@ public SearchResponse search(String query, SearchTypes types)
return search(query, 50, types);
}

/**
* Provides convenience by wrapping the {@link #search(String, int, SearchTypes)} method and limiting
* the content types to Tracks only.
*
* @return Returns a list of tracks returned by the google play service.
*/
public List<Track> searchTracks(String query, int maxResults)
throws IOException {
return search(query, maxResults, new SearchTypes(ResultType.TRACK)).getTracks();
}

/**
* Fetches a list of all devices connected with the current google play account.
*
Expand All @@ -153,71 +154,7 @@ public ListResult<DeviceInfo> getRegisteredDevices()
public List<Track> getPromotedTracks()
throws IOException {
return getService().getPromotedTracks().execute().body().toList();
}

/**
* Deletes playlist entries. They can be from multiple playlists.
*
* @param entries playlist entries to be deleted
*/
public void deletePlaylistEntries(PlaylistEntry... entries)
throws IOException {
deletePlaylistEntries(Arrays.asList(entries));
}

/**
* Deletes playlist entries. They can be from multiple playlists.
*
* @param entries list of playlist entries to be deleted
*/
public void deletePlaylistEntries(Collection<PlaylistEntry> entries) throws IOException {
Mutator mutator = new Mutator();
entries.forEach(e -> mutator.addMutation(MutationFactory.getDeletePlaylistEntryMutation(e)));
service.makeBatchCall(PlaylistEntry.BATCH_URL, mutator);
Playlist.getCache().remove(entries);
}

/**
* deletes playlists and all contained {@link PlaylistEntry}
*
* @param playlists playlists to be deleted
*/
public void deletePlaylists(Playlist... playlists)
throws IOException {
Mutator mutator = new Mutator();
for (Playlist playlist : playlists) {
mutator.addMutation(MutationFactory.getDeletePlaylistMutation(playlist));
}
service.makeBatchCall(Playlist.BATCH_URL, mutator);
}

public void deleteStations(Station... stations)
throws IOException {
Mutator mutator = new Mutator();
for (Station station : stations) {
mutator.addMutation(MutationFactory.getDeleteStationMutation(station));
}
service.makeBatchCall(Station.BATCH_URL, mutator);
}

public List<Station> listStations()
throws IOException {
return new PagingHandler<Station>() {
@Override
public ListResult<Station> getChunk(String nextPageToken) throws IOException {
return service.listStations(new PagingRequest(nextPageToken, -1)).execute().body();
}
}.getAll();
}

public List<Playlist> listPlaylists()
throws IOException {
return new PagingHandler<Playlist>() {
@Override
public ListResult<Playlist> getChunk(String nextPageToken) throws IOException {
return service.listPlaylists(new PagingRequest(nextPageToken, -1)).execute().body();
}
}.getAll();
//TODO: set api
}

public List<PodcastSeries> listPodcastSeries(Genre genre)
Expand Down Expand Up @@ -249,6 +186,26 @@ public ListenNowSituation getListenNowSituation(int offsetInSeconds) throws IOEx
return service.getListenNowSituation(new TimeZoneOffset(String.valueOf(offsetInSeconds))).execute().body();
}

public GenreApi getGenreApi() {
return genreApi;
}

public PlaylistApi getPlaylistApi() {
return playlistApi;
}

public PlaylistEntryApi getPlaylistEntryApi() {
return playlistEntryApi;
}

public StationApi getStationApi() {
return stationApi;
}

public TrackApi getTrackApi() {
return trackApi;
}

/**
* Changes the token used to authenticate the client.
*
Expand Down Expand Up @@ -377,11 +334,6 @@ public GPlayMusic build() {
if (this.authToken == null) {
throw new InitializationException(Language.get("api.init.EmptyToken"));
}
GsonBuilder gsonBuilder = new GsonBuilder()
.registerTypeAdapter(Result.class, new ResultDeserializer())
.registerTypeAdapter(Config.class, new ConfigDeserializer())
.registerTypeAdapter(ListenNowStation.class, new ListenNowStationDeserializer())
.registerTypeAdapter(Color.class, new ColorDeserializer());

if (this.httpClientBuilder == null) {
this.httpClientBuilder = getDefaultHttpBuilder();
Expand All @@ -399,14 +351,24 @@ public GPlayMusic build() {

OkHttpClient httpClient = this.httpClientBuilder.build();

ModelPostProcessor postProcessor = new ModelPostProcessor();
GsonBuilder builder = new GsonFireBuilder()
.registerPostProcessor(Model.class, postProcessor)
.createGsonBuilder()
.registerTypeAdapter(Result.class, new ResultDeserializer())
.registerTypeAdapter(Config.class, new ConfigDeserializer())
.registerTypeAdapter(ListenNowStation.class, new ListenNowStationDeserializer())
.registerTypeAdapter(Color.class, new ColorDeserializer());

Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://mclients.googleapis.com/")
.addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()))
.addConverterFactory(GsonConverterFactory.create(builder.create()))
.client(httpClient)
.build();

GPlayMusic gPlay = new GPlayMusic(retrofit.create(GPlayService.class), parameterInterceptor);
retrofit2.Response<Config> configResponse = null;
postProcessor.setApi(gPlay);
retrofit2.Response<Config> configResponse;
configResponse = gPlay.getService().config(this.locale).execute();
if (!configResponse.isSuccessful()) {
throw new InitializationException(Language.get("network.GenericError"), NetworkException.parse(configResponse.raw()));
Expand Down
Loading

0 comments on commit 6152ab2

Please sign in to comment.