Skip to content

Commit a60ee49

Browse files
committed
Handle Zeroconf correctly by leaving the service always visible (also fixes #42)
1 parent 3604b4e commit a60ee49

8 files changed

Lines changed: 109 additions & 89 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
import org.jetbrains.annotations.Nullable;
44
import xyz.gianlu.librespot.core.AuthConfiguration;
55
import xyz.gianlu.librespot.core.Session;
6-
import xyz.gianlu.librespot.core.ZeroconfAuthenticator;
6+
import xyz.gianlu.librespot.core.ZeroconfServer;
77
import xyz.gianlu.librespot.player.CacheManager;
88
import xyz.gianlu.librespot.player.Player;
99

1010
/**
1111
* @author Gianlu
1212
*/
13-
public abstract class AbsConfiguration implements Player.Configuration, CacheManager.CacheConfiguration, AuthConfiguration, ZeroconfAuthenticator.Configuration {
13+
public abstract class AbsConfiguration implements Player.Configuration, CacheManager.CacheConfiguration, AuthConfiguration, ZeroconfServer.Configuration {
1414

1515
@Nullable
1616
public abstract String deviceName();

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,23 +79,23 @@ public Session.DeviceType deviceType() {
7979
}
8080

8181
@Override
82-
public @Nullable String username() {
82+
public @Nullable String authUsername() {
8383
return null;
8484
}
8585

8686
@Override
87-
public @Nullable String password() {
87+
public @Nullable String authPassword() {
8888
return null;
8989
}
9090

9191
@Override
92-
public @Nullable String blob() {
92+
public @Nullable String authBlob() {
9393
return null;
9494
}
9595

9696
@NotNull
9797
@Override
98-
public Strategy strategy() {
98+
public Strategy authStrategy() {
9999
return Strategy.ZEROCONF;
100100
}
101101

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,24 +137,24 @@ public boolean logAvailableMixers() {
137137
}
138138

139139
@Override
140-
public @Nullable String username() {
140+
public @Nullable String authUsername() {
141141
return properties.getProperty("auth.username", null);
142142
}
143143

144144
@Override
145-
public @Nullable String password() {
145+
public @Nullable String authPassword() {
146146
return properties.getProperty("auth.password", null);
147147
}
148148

149149
@Override
150-
public @Nullable String blob() {
150+
public @Nullable String authBlob() {
151151
return properties.getProperty("auth.blob", null);
152152
}
153153

154154
@NotNull
155155
@Override
156-
public Strategy strategy() {
157-
return getEnum(Strategy.class, "auth.strategy", defaults.strategy());
156+
public Strategy authStrategy() {
157+
return getEnum(Strategy.class, "auth.strategy", defaults.authStrategy());
158158
}
159159

160160
@Override
Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package xyz.gianlu.librespot;
22

3+
import xyz.gianlu.librespot.core.AuthConfiguration;
34
import xyz.gianlu.librespot.core.Session;
5+
import xyz.gianlu.librespot.core.ZeroconfServer;
46
import xyz.gianlu.librespot.mercury.MercuryClient;
57
import xyz.gianlu.librespot.spirc.SpotifyIrc;
68

@@ -13,11 +15,12 @@
1315
*/
1416
public class Main {
1517

16-
public static void main(String[] args) throws IOException, GeneralSecurityException, Session.SpotifyAuthenticationException, SpotifyIrc.IrcException, MercuryClient.PubSubException, InterruptedException {
17-
Session session = new Session.Builder(new FileConfiguration(new File("conf.properties"), args)).create();
18-
19-
Thread.sleep(8000);
20-
21-
session.close();
18+
public static void main(String[] args) throws IOException, GeneralSecurityException, Session.SpotifyAuthenticationException, SpotifyIrc.IrcException, MercuryClient.PubSubException {
19+
AbsConfiguration conf = new FileConfiguration(new File("conf.properties"), args);
20+
if (conf.authStrategy() == AuthConfiguration.Strategy.ZEROCONF) {
21+
ZeroconfServer.create(conf);
22+
} else {
23+
new Session.Builder(conf).create();
24+
}
2225
}
2326
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@
88
*/
99
public interface AuthConfiguration {
1010
@Nullable
11-
String username();
11+
String authUsername();
1212

1313
@Nullable
14-
String password();
14+
String authPassword();
1515

1616
@Nullable
17-
String blob();
17+
String authBlob();
1818

1919
@NotNull
20-
Strategy strategy();
20+
Strategy authStrategy();
2121

2222
enum Strategy {
2323
FACEBOOK, BLOB,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ private void authData(@NotNull String json) {
8080

8181
JsonObject data = obj.getAsJsonObject("credentials");
8282
credentials = Authentication.LoginCredentials.newBuilder()
83-
.setUsername(data.get("username").getAsString())
83+
.setUsername(data.get("authUsername").getAsString())
8484
.setTyp(Authentication.AuthenticationType.forNumber(data.get("auth_type").getAsInt()))
8585
.setAuthData(ByteString.copyFrom(Base64.getDecoder().decode(data.get("encoded_auth_blob").getAsString())))
8686
.build();

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

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,12 @@ private static int readBlobInt(ByteBuffer buffer) {
7575
return lo & 0x7f | hi << 7;
7676
}
7777

78-
private void connect() throws IOException, GeneralSecurityException, SpotifyAuthenticationException {
78+
@NotNull
79+
static Session from(@NotNull Inner inner) throws IOException {
80+
return new Session(inner, ApResolver.getSocketFromRandomAccessPoint());
81+
}
82+
83+
void connect() throws IOException, GeneralSecurityException, SpotifyAuthenticationException {
7984
Accumulator acc = new Accumulator();
8085

8186
// Send ClientHello
@@ -191,7 +196,7 @@ private void connect() throws IOException, GeneralSecurityException, SpotifyAuth
191196
LOGGER.info("Connected successfully!");
192197
}
193198

194-
private void authenticate(@NotNull Authentication.LoginCredentials credentials) throws IOException, GeneralSecurityException, SpotifyAuthenticationException, MercuryClient.PubSubException, SpotifyIrc.IrcException {
199+
void authenticate(@NotNull Authentication.LoginCredentials credentials) throws IOException, GeneralSecurityException, SpotifyAuthenticationException, MercuryClient.PubSubException, SpotifyIrc.IrcException {
195200
if (cipherPair == null) throw new IllegalStateException("Connection not established!");
196201

197202
Authentication.ClientResponseEncrypted clientResponseEncrypted = Authentication.ClientResponseEncrypted.newBuilder()
@@ -300,6 +305,10 @@ public Authentication.APWelcome apWelcome() {
300305
return apWelcome;
301306
}
302307

308+
public boolean valid() {
309+
return apWelcome != null && !socket.isClosed();
310+
}
311+
303312
@NotNull
304313
public String deviceId() {
305314
return inner.deviceId;
@@ -360,6 +369,19 @@ private Inner(DeviceType deviceType, String deviceName, AbsConfiguration configu
360369
this.deviceId = UUID.randomUUID().toString();
361370
}
362371

372+
@NotNull
373+
static Inner from(@NotNull AbsConfiguration configuration) {
374+
String deviceName = configuration.deviceName();
375+
if (deviceName == null || deviceName.isEmpty())
376+
throw new IllegalArgumentException("Device name required: " + deviceName);
377+
378+
DeviceType deviceType = configuration.deviceType();
379+
if (deviceType == null)
380+
throw new IllegalArgumentException("Device type required!");
381+
382+
return new Inner(deviceType, deviceName, configuration);
383+
}
384+
363385
@NotNull Authentication.LoginCredentials decryptBlob(String username, byte[] encryptedBlob) throws GeneralSecurityException, IOException {
364386
encryptedBlob = Base64.getDecoder().decode(encryptedBlob);
365387

@@ -408,28 +430,18 @@ public static class Builder {
408430
private final Inner inner;
409431
private Authentication.LoginCredentials loginCredentials = null;
410432
private AuthConfiguration authConf;
411-
private ZeroconfAuthenticator.Configuration zeroconfConf;
412433

413434
public Builder(@NotNull DeviceType deviceType, @NotNull String deviceName, @NotNull AbsConfiguration configuration) {
414435
this.inner = new Inner(deviceType, deviceName, configuration);
415436
this.authConf = configuration;
416-
this.zeroconfConf = configuration;
417437
}
418438

419439
public Builder(@NotNull AbsConfiguration configuration) {
420-
String deviceName = configuration.deviceName();
421-
if (deviceName == null || deviceName.isEmpty())
422-
throw new IllegalArgumentException("Device name required: " + deviceName);
423-
424-
DeviceType deviceType = configuration.deviceType();
425-
if (deviceType == null)
426-
throw new IllegalArgumentException("Device type required!");
427-
428-
this.inner = new Inner(deviceType, deviceName, configuration);
440+
this.inner = Inner.from(configuration);
429441
this.authConf = configuration;
430-
this.zeroconfConf = configuration;
431442
}
432443

444+
@NotNull
433445
public Builder facebook() throws IOException {
434446
try (FacebookAuthenticator authenticator = new FacebookAuthenticator()) {
435447
loginCredentials = authenticator.lockUntilCredentials();
@@ -439,20 +451,13 @@ public Builder facebook() throws IOException {
439451
}
440452
}
441453

442-
public Builder zeroconf() throws IOException {
443-
try (ZeroconfAuthenticator authenticator = new ZeroconfAuthenticator(inner, zeroconfConf)) {
444-
loginCredentials = authenticator.lockUntilCredentials();
445-
return this;
446-
} catch (InterruptedException ex) {
447-
throw new IOException(ex);
448-
}
449-
}
450-
454+
@NotNull
451455
public Builder blob(String username, byte[] blob) throws GeneralSecurityException, IOException {
452456
loginCredentials = inner.decryptBlob(username, blob);
453457
return this;
454458
}
455459

460+
@NotNull
456461
public Builder userPass(@NotNull String username, @NotNull String password) {
457462
loginCredentials = Authentication.LoginCredentials.newBuilder()
458463
.setUsername(username)
@@ -466,36 +471,35 @@ public Builder userPass(@NotNull String username, @NotNull String password) {
466471
public Session create() throws IOException, GeneralSecurityException, SpotifyAuthenticationException, MercuryClient.PubSubException, SpotifyIrc.IrcException {
467472
if (loginCredentials == null) {
468473
if (authConf != null) {
469-
String blob = authConf.blob();
470-
String username = authConf.username();
471-
String password = authConf.password();
474+
String blob = authConf.authBlob();
475+
String username = authConf.authUsername();
476+
String password = authConf.authPassword();
472477

473-
switch (authConf.strategy()) {
478+
switch (authConf.authStrategy()) {
474479
case FACEBOOK:
475480
facebook();
476481
break;
477482
case BLOB:
478-
if (username == null) throw new IllegalArgumentException("Missing username!");
479-
if (blob == null) throw new IllegalArgumentException("Missing blob!");
483+
if (username == null) throw new IllegalArgumentException("Missing authUsername!");
484+
if (blob == null) throw new IllegalArgumentException("Missing authBlob!");
480485
blob(username, Base64.getDecoder().decode(blob));
481486
break;
482487
case USER_PASS:
483-
if (username == null) throw new IllegalArgumentException("Missing username!");
484-
if (password == null) throw new IllegalArgumentException("Missing password!");
488+
if (username == null) throw new IllegalArgumentException("Missing authUsername!");
489+
if (password == null) throw new IllegalArgumentException("Missing authPassword!");
485490
userPass(username, password);
486491
break;
487492
case ZEROCONF:
488-
zeroconf();
489-
break;
493+
throw new IllegalStateException("Cannot handle ZEROCONF! Use ZeroconfServer.");
490494
default:
491-
throw new IllegalStateException("Unknown auth strategy: " + authConf.strategy());
495+
throw new IllegalStateException("Unknown auth authStrategy: " + authConf.authStrategy());
492496
}
493497
} else {
494498
throw new IllegalStateException("Missing credentials!");
495499
}
496500
}
497501

498-
Session session = new Session(inner, ApResolver.getSocketFromRandomAccessPoint());
502+
Session session = Session.from(inner);
499503
session.connect();
500504
session.authenticate(loginCredentials);
501505
return session;

0 commit comments

Comments
 (0)