Skip to content

Commit ba8b2fb

Browse files
committed
Added TokensHandler and SearchHandler
1 parent 26c1ebb commit ba8b2fb

5 files changed

Lines changed: 103 additions & 12 deletions

File tree

api/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ All the endpoints will respond with `200` if successful or `204` if there isn't
2121
### Metadata
2222
- `POST \metadata\{type}\{uri}` Retrieve metadata. `type` can be one of `episode`, `track`, `album`, `show`, `artist`, `uri` is the standard Spotify uri.
2323

24+
### Search
25+
- `POST \search\{query}` Make a search.
26+
27+
### Tokens
28+
- `POST \token\{scope}` Request an access token for a specific scope.
29+
2430
### Events
2531

2632
You can subscribe for players events by creating a WebSocket connection to `/events`.

api/src/main/java/xyz/gianlu/librespot/api/ApiServer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66
import io.undertow.server.RoutingHandler;
77
import org.apache.log4j.Logger;
88
import org.jetbrains.annotations.NotNull;
9-
import xyz.gianlu.librespot.api.handlers.EventsHandler;
10-
import xyz.gianlu.librespot.api.handlers.MetadataHandler;
11-
import xyz.gianlu.librespot.api.handlers.PlayerHandler;
9+
import xyz.gianlu.librespot.api.handlers.*;
1210

1311
public class ApiServer {
1412
private static final Logger LOGGER = Logger.getLogger(ApiServer.class);
@@ -27,6 +25,8 @@ public ApiServer(@NotNull ApiConfiguration conf, @NotNull SessionWrapper wrapper
2725
handler = new RoutingHandler();
2826
handler.post("/player/{cmd}", new PlayerHandler(wrapper))
2927
.post("/metadata/{type}/{uri}", new MetadataHandler(wrapper))
28+
.post("/search/{query}", new SearchHandler(wrapper))
29+
.post("/token/{scope}", new TokensHandler(wrapper))
3030
.get("/events", events);
3131
}
3232

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package xyz.gianlu.librespot.api.handlers;
2+
3+
import io.undertow.server.HttpServerExchange;
4+
import org.jetbrains.annotations.NotNull;
5+
import xyz.gianlu.librespot.api.SessionWrapper;
6+
import xyz.gianlu.librespot.api.Utils;
7+
import xyz.gianlu.librespot.core.SearchManager;
8+
import xyz.gianlu.librespot.core.Session;
9+
10+
import java.util.Deque;
11+
import java.util.Map;
12+
13+
public final class SearchHandler extends AbsSessionHandler {
14+
15+
public SearchHandler(@NotNull SessionWrapper wrapper) {
16+
super(wrapper);
17+
}
18+
19+
@Override
20+
protected void handleRequest(@NotNull HttpServerExchange exchange, @NotNull Session session) throws Exception {
21+
exchange.startBlocking();
22+
if (exchange.isInIoThread()) {
23+
exchange.dispatch(this);
24+
return;
25+
}
26+
27+
Map<String, Deque<String>> params = Utils.readParameters(exchange);
28+
String query = Utils.getFirstString(params, "query");
29+
if (query == null) {
30+
Utils.invalidParameter(exchange, "query");
31+
return;
32+
}
33+
34+
exchange.getResponseSender().send(session.search()
35+
.request(new SearchManager.SearchRequest(query))
36+
.toString());
37+
}
38+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package xyz.gianlu.librespot.api.handlers;
2+
3+
import com.google.gson.JsonObject;
4+
import io.undertow.server.HttpServerExchange;
5+
import org.jetbrains.annotations.NotNull;
6+
import xyz.gianlu.librespot.api.SessionWrapper;
7+
import xyz.gianlu.librespot.api.Utils;
8+
import xyz.gianlu.librespot.core.Session;
9+
import xyz.gianlu.librespot.core.TokenProvider;
10+
11+
import java.util.Deque;
12+
import java.util.Map;
13+
14+
public final class TokensHandler extends AbsSessionHandler {
15+
16+
public TokensHandler(@NotNull SessionWrapper wrapper) {
17+
super(wrapper);
18+
}
19+
20+
@Override
21+
protected void handleRequest(@NotNull HttpServerExchange exchange, @NotNull Session session) throws Exception {
22+
exchange.startBlocking();
23+
if (exchange.isInIoThread()) {
24+
exchange.dispatch(this);
25+
return;
26+
}
27+
28+
Map<String, Deque<String>> params = Utils.readParameters(exchange);
29+
String scope = Utils.getFirstString(params, "scope");
30+
if (scope == null) {
31+
Utils.invalidParameter(exchange, "scope");
32+
return;
33+
}
34+
35+
TokenProvider.StoredToken token = session.tokens().getToken(scope);
36+
JsonObject obj = new JsonObject();
37+
obj.addProperty("token", token.accessToken);
38+
obj.addProperty("timestamp", token.timestamp);
39+
obj.addProperty("expiresIn", token.expiresIn);
40+
exchange.getResponseSender().send(obj.toString());
41+
}
42+
}

core/src/main/java/xyz/gianlu/librespot/core/TokenProvider.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ public class TokenProvider {
2525
}
2626

2727
@NotNull
28-
public String get(@NotNull String scope) throws IOException, MercuryClient.MercuryException {
28+
public StoredToken getToken(@NotNull String scope) throws IOException, MercuryClient.MercuryException {
2929
if (scope.contains(",")) throw new UnsupportedOperationException("Only single scope tokens are supported.");
3030

3131
StoredToken token = tokens.get(scope);
3232
if (token != null) {
3333
if (token.expired()) tokens.remove(scope);
34-
else return token.accessToken;
34+
else return token;
3535
}
3636

3737
LOGGER.debug(String.format("Token expired or not suitable, requesting again. {scope: %s, oldToken: %s}", scope, token));
@@ -41,14 +41,19 @@ public String get(@NotNull String scope) throws IOException, MercuryClient.Mercu
4141
LOGGER.debug(String.format("Updated token successfully! {scope: %s, newToken: %s}", scope, token));
4242

4343
tokens.put(scope, token);
44-
return token.accessToken;
44+
return token;
45+
}
46+
47+
@NotNull
48+
public String get(@NotNull String scope) throws IOException, MercuryClient.MercuryException {
49+
return getToken(scope).accessToken;
4550
}
4651

47-
private static class StoredToken {
48-
final int expiresIn;
49-
final String accessToken;
50-
final String[] scopes;
51-
final long timestamp;
52+
public static class StoredToken {
53+
public final int expiresIn;
54+
public final String accessToken;
55+
public final String[] scopes;
56+
public final long timestamp;
5257

5358
private StoredToken(@NotNull MercuryRequests.KeymasterToken token) {
5459
timestamp = TimeProvider.currentTimeMillis();
@@ -61,7 +66,7 @@ private StoredToken(@NotNull MercuryRequests.KeymasterToken token) {
6166
scopes[i] = scopesArray.get(i).getAsString();
6267
}
6368

64-
private boolean expired() {
69+
public boolean expired() {
6570
return timestamp + (expiresIn - TOKEN_EXPIRE_THRESHOLD) * 1000 < TimeProvider.currentTimeMillis();
6671
}
6772

0 commit comments

Comments
 (0)