Skip to content

Commit

Permalink
Added endpoint for artist info (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
entriphy committed Apr 20, 2020
1 parent b1bc4d0 commit d4c9f41
Show file tree
Hide file tree
Showing 10 changed files with 358 additions and 219 deletions.
33 changes: 22 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

# Advantages vs. [Spotify-PlayCount](https://github.com/evilarceus/Spotify-PlayCount)
* Does NOT require the Spotify desktop app (woo)
* Lower CPU and memory usage (can be even lower once the code is fully changed)
* Lower CPU and memory usage
* More information given in API response
* Provides endpoint for artist info (monthly listeners, top tracks, follower count, etc)

## Requirements
* Java 8+
Expand All @@ -20,14 +21,22 @@
4. Run the JAR again: `java -jar sp-playcount-librespot.jar`

## Usage
Simply make a GET request to the endpoint with the query string "albumid" set to the ID of a Spotify album (ex. if the URL is https://open.spotify.com/album/6Lq1lrCfkpxKa4jCo5gKWr or spotify:album:6Lq1lrCfkpxKa4jCo5gKWr, the string is 6Lq1lrCfkpxKa4jCo5gKWr)
Simply make a GET request to the endpoint with the query string `albumid` set to the ID of a Spotify album (ex. if the URL is https://open.spotify.com/album/6Lq1lrCfkpxKa4jCo5gKWr or spotify:album:6Lq1lrCfkpxKa4jCo5gKWr, the string is 6Lq1lrCfkpxKa4jCo5gKWr)

Curl example: (endpoint is /albumPlayCount)
```bash
$ curl https://example.com/albumPlayCount?albumid=6Lq1lrCfkpxKa4jCo5gKWr
{"success": true, "data": {"uri":"spotify:album:6Lq1lrCfkpxKa4jCo5gKWr","name":"Good Faith","cover":{"uri":"https://i.scdn.co/image/ab67616d00001e02dc384e6d13983fe1cd415ade"},"year":2019,"track_count":10,"discs":[{"number":1 ...
```
There is also an endpoint for retrieving artist info. `artistid` must be set to the ID of a Spotify artist.
Curl example: (endpoint is /artistInfo)
```bash
$ curl https://example.com/artistInfo?artistid=7A0awCXkE1FtSU8B0qwOJQ
{"success": true, "data": {"uri":"spotify:artist:7A0awCXkE1FtSU8B0qwOJQ","info":{"uri":"spotify:artist:7A0awCXkE1FtSU8B0qwOJQ","name":"Jamie xx","portraits": ...
```
## Compiling (requires Maven)
1. Clone this repository: `git clone https://github.com/evilarceus/sp-playcount-librespot && cd sp-playcount-librespot`
2. Build with Maven: `mvn clean package`
Expand All @@ -39,18 +48,20 @@ To reset the configuration, simply delete the file and run the JAR again.
```
[server]
port = 8080
endpoint = "/albumPlayCount"
albumEndpoint = "/albumPlayCount"
artistEndpoint = "/artistInfo"
enableHttps = false
httpsKs = ""
httpsKsPass = ""
```
| Option | Description |
|---------------|--------------------------------------------------------------------------|
| `port` | Selects what port to listen for HTTP requests on |
| `endpoint` | Endpoint at which the user can send HTTP GET requests to the API |
| `enableHttps` | If true, enables HTTPS support (requires certificate, see section below) |
| `httpsKs` | Location to keystore with HTTPS certificate and key |
| `httpsKsPass` | Password to HTTPS keystore file (if applicable) |
| Option | Description |
|--------------------|---------------------------------------------------------------------------------------|
| `port` | Selects what port to listen for HTTP requests on |
| `albumEndpoint` | Endpoint at which the user can send HTTP GET requests to the API for album info |
| `artistEndpoint` | Endpoint at which the user can send HTTP GET requests to the API for artist info |
| `enableHttps` | If true, enables HTTPS support (requires certificate, see section below) |
| `httpsKs` | Location to keystore with HTTPS certificate and key |
| `httpsKsPass` | Password to HTTPS keystore file (if applicable) |
### HTTPS Configuration
The server can be configured to use HTTPS. If you're using LetsEncrypt, use [this guide](https://www.wissel.net/blog/2018/03/letsencrypt-java-keystore.html) to create a keystore with the certificate.
Expand All @@ -65,6 +76,6 @@ httpsKsPass = "<keystore password (if applicable)>"
```
## Public API
I am currently hosting this API at https://api.t4ils.dev/albumPlayCount.
I am currently hosting this API at https://api.t4ils.dev. (endpoints: /albumPlayCount, /artistInfo)
If your application previously used Spotify-PlayCount, you will need to update your application to support the new API response.
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,13 @@ public int port() {
}

@Override
public @NotNull String endpoint() {
return config.get("server.endpoint");
public @NotNull String albumEndpoint() {
return config.get("server.albumEndpoint");
}

@Override
public @NotNull String artistEndpoint() {
return config.get("server.artistEndpoint");
}

@Override
Expand Down
195 changes: 0 additions & 195 deletions core/src/main/java/xyz/gianlu/librespot/HTTPSServer.java

This file was deleted.

108 changes: 108 additions & 0 deletions core/src/main/java/xyz/gianlu/librespot/HTTPServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package xyz.gianlu.librespot;

import com.sun.net.httpserver.HttpsServer;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsParameters;
import org.cache2k.Cache;
import org.cache2k.Cache2kBuilder;
import org.cache2k.event.CacheEntryExpiredListener;
import xyz.gianlu.librespot.handler.ArtistInfoHandler;
import xyz.gianlu.librespot.handler.PlayCountHandler;
import xyz.gianlu.librespot.mercury.MercuryClient;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.security.KeyStore;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class HTTPServer {
private AbsConfiguration conf;
private MercuryClient mercuryClient;
public HttpsServer httpsServer;
public HttpServer httpServer;
public ThreadPoolExecutor threadPoolExecutor;
private Cache<String, String> cache;

public HTTPServer(AbsConfiguration conf, MercuryClient mc) {
this.conf = conf;
this.mercuryClient = mc;
this.cache = new Cache2kBuilder<String, String>() {}
.name("cache")
.entryCapacity(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.resilienceDuration(5, TimeUnit.SECONDS)
.addListener((CacheEntryExpiredListener<String, String>) (cache, cacheEntry) -> {
cache.remove(cacheEntry.getKey()); // Remove cached result when it expires; will be refreshed when requested
})
.build();
}

public void start() {
try {
InetSocketAddress address = new InetSocketAddress(System.getenv("PORT") != null ? Integer.parseInt(System.getenv("PORT")) : conf.port());

if (this.conf.enableHttps()) {
this.httpsServer = HttpsServer.create(address, 0);
SSLContext sslContext = SSLContext.getInstance("TLS");

char[] password = Objects.requireNonNull(conf.httpsKsPass()).toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream(Objects.requireNonNull(conf.httpsKs()));
ks.load(fis, password);

KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password);

TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);

sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
public void configure(HttpsParameters params) {
try {
SSLContext context = getSSLContext();
SSLEngine engine = context.createSSLEngine();
params.setNeedClientAuth(false);
params.setCipherSuites(engine.getEnabledCipherSuites());
params.setProtocols(engine.getEnabledProtocols());

SSLParameters sslParameters = context.getSupportedSSLParameters();
params.setSSLParameters(sslParameters);
} catch (Exception ex) {
System.out.println("Failed to create HTTPS port");
ex.printStackTrace();
}
}
});

threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
httpsServer.createContext(conf.albumEndpoint(), new PlayCountHandler(this.mercuryClient, this.cache));
httpsServer.createContext(conf.artistEndpoint(), new ArtistInfoHandler(this.mercuryClient, this.cache));
httpsServer.setExecutor(threadPoolExecutor);
httpsServer.start();
System.out.println("Listening on port " + httpsServer.getAddress().getPort());
} else {
this.httpServer = HttpServer.create(address, 0);
threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
httpServer.createContext(conf.albumEndpoint(), new PlayCountHandler(this.mercuryClient, this.cache));
httpServer.createContext(conf.artistEndpoint(), new ArtistInfoHandler(this.mercuryClient, this.cache));
httpServer.setExecutor(threadPoolExecutor);
httpServer.start();
System.out.println("Listening on port " + httpServer.getAddress().getPort());
}
} catch (Exception e) {
e.printStackTrace();
}
}


}
Loading

0 comments on commit d4c9f41

Please sign in to comment.