Skip to content

Commit fe90a7d

Browse files
fiechtordevgianlu
authored andcommitted
Zeroconf ApiServer (#128)
* Add Zeroconf Apiserver functionality xyz.gianlu.librespot.core.ZeroconfServer now extends java.util.Observable, notifies observers when a new Session is created add method xyz.gianlu.librespot.api.server.ApiServer.clearHandlers(), clears the ApiHandlers Map add class ZeroconfApiServer, extends xyz.gianlu.librespot.api.server.ApiServer, observes the ZeroconfServer, updates when a new Session is created in ZeroconfServer * fix Observer not getting updated Session had to be authenticated before registering request handlers * Moved from Observable to Listener pattern Observer and Observable is deprecated in java 11+ * removed unused imports * Minor refactoring
1 parent 296b7af commit fe90a7d

4 files changed

Lines changed: 67 additions & 6 deletions

File tree

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package xyz.gianlu.librespot.api;
22

3+
import xyz.gianlu.librespot.AbsConfiguration;
34
import xyz.gianlu.librespot.FileConfiguration;
45
import xyz.gianlu.librespot.api.server.ApiServer;
6+
import xyz.gianlu.librespot.api.server.ZeroconfApiServer;
7+
import xyz.gianlu.librespot.core.AuthConfiguration;
58
import xyz.gianlu.librespot.core.Session;
9+
import xyz.gianlu.librespot.core.ZeroconfServer;
610
import xyz.gianlu.librespot.spirc.SpotifyIrc;
711

812
import java.io.IOException;
@@ -14,11 +18,16 @@
1418
public class Main {
1519

1620
public static void main(String[] args) throws IOException, GeneralSecurityException, SpotifyIrc.IrcException, Session.SpotifyAuthenticationException {
17-
Session session = new Session.Builder(new FileConfiguration(args)).create();
21+
AbsConfiguration conf = new FileConfiguration(args);
22+
if (conf.authStrategy() == AuthConfiguration.Strategy.ZEROCONF) {
23+
ZeroconfServer.create(conf).addSessionListener(new ZeroconfApiServer(24879));
24+
} else {
25+
Session session = new Session.Builder(conf).create();
1826

19-
ApiServer server = new ApiServer(24879);
20-
server.registerHandler(new PlayerHandler(session));
21-
server.registerHandler(new MetadataHandler(session));
22-
server.registerHandler(new MercuryHandler(session));
27+
ApiServer server = new ApiServer(24879);
28+
server.registerHandler(new PlayerHandler(session));
29+
server.registerHandler(new MetadataHandler(session));
30+
server.registerHandler(new MercuryHandler(session));
31+
}
2332
}
2433
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ public void registerHandler(@NotNull AbsApiHandler handler) {
6464
handlers.put(handler.prefix, handler);
6565
}
6666

67+
public void clearHandlers() {
68+
handlers.clear();
69+
}
70+
6771
private void handleRequest(@NotNull Request request) throws PredefinedJsonRpcException {
6872
int index = request.method.indexOf('.');
6973
if (index == -1)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package xyz.gianlu.librespot.api.server;
2+
3+
import org.apache.log4j.Logger;
4+
import org.jetbrains.annotations.NotNull;
5+
import xyz.gianlu.librespot.api.MercuryHandler;
6+
import xyz.gianlu.librespot.api.MetadataHandler;
7+
import xyz.gianlu.librespot.api.PlayerHandler;
8+
import xyz.gianlu.librespot.core.Session;
9+
import xyz.gianlu.librespot.core.ZeroconfServer;
10+
11+
import java.io.IOException;
12+
13+
public class ZeroconfApiServer extends ApiServer implements ZeroconfServer.SessionListener {
14+
private static final Logger LOGGER = Logger.getLogger(ZeroconfApiServer.class);
15+
16+
public ZeroconfApiServer(int port) throws IOException {
17+
super(port);
18+
}
19+
20+
@Override
21+
public void sessionChanged(@NotNull Session session) {
22+
clearHandlers();
23+
24+
registerHandler(new PlayerHandler(session));
25+
registerHandler(new MetadataHandler(session));
26+
registerHandler(new MercuryHandler(session));
27+
LOGGER.info("Refreshed handlers for new session.");
28+
}
29+
}

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.IOException;
2323
import java.io.OutputStream;
2424
import java.net.*;
25+
import java.nio.charset.StandardCharsets;
2526
import java.security.GeneralSecurityException;
2627
import java.security.MessageDigest;
2728
import java.util.*;
@@ -81,10 +82,12 @@ public class ZeroconfServer implements Closeable {
8182
private final DiffieHellman keys;
8283
private final JmDNS[] instances;
8384
private volatile Session session;
85+
private final List<SessionListener> sessionListeners;
8486

8587
private ZeroconfServer(Session.Inner inner, Configuration conf) throws IOException {
8688
this.inner = inner;
8789
this.keys = new DiffieHellman(inner.random);
90+
this.sessionListeners = new ArrayList<>();
8891

8992
int port = conf.zeroconfListenPort();
9093
if (port == -1)
@@ -312,12 +315,22 @@ private void handleAddUser(OutputStream out, Map<String, String> params, String
312315

313316
session.connect();
314317
session.authenticate(credentials);
318+
319+
sessionListeners.forEach(l -> l.sessionChanged(session));
315320
} catch (Session.SpotifyAuthenticationException | SpotifyIrc.IrcException ex) {
316321
LOGGER.fatal("Failed handling connection! Going away.", ex);
317322
close();
318323
}
319324
}
320325

326+
public void addSessionListener(@NotNull SessionListener listener) {
327+
sessionListeners.add(listener);
328+
}
329+
330+
public void removeSessionListener(@NotNull SessionListener listener) {
331+
sessionListeners.remove(listener);
332+
}
333+
321334
public interface Configuration {
322335
boolean zeroconfListenAll();
323336

@@ -327,6 +340,10 @@ public interface Configuration {
327340
String[] zeroconfInterfaces();
328341
}
329342

343+
public interface SessionListener {
344+
void sessionChanged(@NotNull Session session);
345+
}
346+
330347
private class HttpRunner implements Runnable, Closeable {
331348
private final ServerSocket serverSocket;
332349
private final ExecutorService executorService = Executors.newCachedThreadPool();
@@ -402,7 +419,7 @@ private void handle(@NotNull Socket socket) throws IOException {
402419
Map<String, String> params = new HashMap<>(pairs.length);
403420
for (String pair : pairs) {
404421
String[] split = Utils.split(pair, '=');
405-
params.put(URLDecoder.decode(split[0], "UTF-8"), URLDecoder.decode(split[1], "UTF-8"));
422+
params.put(URLDecoder.decode(split[0], StandardCharsets.UTF_8), URLDecoder.decode(split[1], StandardCharsets.UTF_8));
406423
}
407424

408425
String action = params.get("action");
@@ -436,5 +453,7 @@ public void close() throws IOException {
436453
shouldStop = true;
437454
serverSocket.close();
438455
}
456+
439457
}
458+
440459
}

0 commit comments

Comments
 (0)