Skip to content

Commit 626e58b

Browse files
authored
Merge pull request #1 from librespot-org/dev
merge librespot-java
2 parents 802ecf8 + 0291caa commit 626e58b

5 files changed

Lines changed: 103 additions & 22 deletions

File tree

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ private void authenticate(@NotNull Authentication.LoginCredentials credentials)
388388
* {@code true} for {@link Session#reconnect()}.
389389
*/
390390
private void authenticatePartial(@NotNull Authentication.LoginCredentials credentials, boolean removeLock) throws IOException, GeneralSecurityException, SpotifyAuthenticationException {
391-
if (cipherPair == null) throw new IllegalStateException("Connection not established!");
391+
if (conn == null || cipherPair == null) throw new IllegalStateException("Connection not established!");
392392

393393
Authentication.ClientResponseEncrypted clientResponseEncrypted = Authentication.ClientResponseEncrypted.newBuilder()
394394
.setLoginCredentials(credentials)
@@ -409,7 +409,6 @@ private void authenticatePartial(@NotNull Authentication.LoginCredentials creden
409409

410410
receiver = new Receiver();
411411

412-
413412
byte[] bytes0x0f = new byte[20];
414413
random().nextBytes(bytes0x0f);
415414
sendUnchecked(Packet.Type.Unknown_0x0f, bytes0x0f);
@@ -452,6 +451,8 @@ private void authenticatePartial(@NotNull Authentication.LoginCredentials creden
452451
public void close() throws IOException {
453452
LOGGER.info("Closing session. {deviceId: {}}", inner.deviceId);
454453

454+
if (scheduledReconnect != null) scheduledReconnect.cancel(true);
455+
455456
closing = true;
456457

457458
scheduler.shutdownNow();
@@ -513,6 +514,9 @@ public void close() throws IOException {
513514
}
514515

515516
private void sendUnchecked(Packet.Type cmd, byte[] payload) throws IOException {
517+
if (conn == null)
518+
throw new IOException("Cannot write to missing connection.");
519+
516520
cipherPair.sendEncoded(conn.out, cmd.val, payload);
517521
}
518522

@@ -692,6 +696,9 @@ public Configuration configuration() {
692696
}
693697

694698
private void reconnect() {
699+
if (closing)
700+
return;
701+
695702
synchronized (reconnectionListeners) {
696703
reconnectionListeners.forEach(ReconnectionListener::onConnectionDropped);
697704
}
@@ -716,6 +723,9 @@ private void reconnect() {
716723
reconnectionListeners.forEach(ReconnectionListener::onConnectionEstablished);
717724
}
718725
} catch (IOException | GeneralSecurityException | SpotifyAuthenticationException ex) {
726+
if (closing)
727+
return;
728+
719729
conn = null;
720730
LOGGER.error("Failed reconnecting, retrying in 10 seconds...", ex);
721731

@@ -1310,7 +1320,7 @@ public void run() {
13101320
continue;
13111321
}
13121322
} catch (IOException | GeneralSecurityException ex) {
1313-
if (running) {
1323+
if (running && !closing) {
13141324
LOGGER.error("Failed reading packet!", ex);
13151325
reconnect();
13161326
}

player/src/main/java/xyz/gianlu/librespot/player/codecs/Codec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public abstract class Codec implements Closeable {
4444
protected int seekZero = 0;
4545
private OutputAudioFormat format;
4646

47-
Codec(@NotNull GeneralAudioStream audioFile, @Nullable NormalizationData normalizationData, @NotNull PlayerConfiguration conf, int duration) {
47+
public Codec(@NotNull GeneralAudioStream audioFile, @Nullable NormalizationData normalizationData, @NotNull PlayerConfiguration conf, int duration) {
4848
this.audioIn = audioFile.stream();
4949
this.audioFile = audioFile;
5050
this.duration = duration;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2021 devgianlu
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package xyz.gianlu.librespot.player.codecs;
18+
19+
import org.jetbrains.annotations.NotNull;
20+
import org.jetbrains.annotations.Nullable;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
import xyz.gianlu.librespot.audio.GeneralAudioStream;
24+
import xyz.gianlu.librespot.audio.NormalizationData;
25+
import xyz.gianlu.librespot.audio.format.SuperAudioFormat;
26+
import xyz.gianlu.librespot.player.PlayerConfiguration;
27+
28+
import java.util.*;
29+
30+
/**
31+
* @author devgianlu
32+
*/
33+
public final class Codecs {
34+
private static final Map<SuperAudioFormat, HashSet<Class<? extends Codec>>> codecs = new EnumMap<>(SuperAudioFormat.class);
35+
private static final Logger LOGGER = LoggerFactory.getLogger(Codecs.class);
36+
37+
static {
38+
registerCodec(SuperAudioFormat.VORBIS, VorbisCodec.class);
39+
registerCodec(SuperAudioFormat.MP3, Mp3Codec.class);
40+
}
41+
42+
private Codecs() {
43+
}
44+
45+
@Nullable
46+
public static Codec initCodec(@NotNull SuperAudioFormat format, @NotNull GeneralAudioStream audioFile, @Nullable NormalizationData normalizationData, @NotNull PlayerConfiguration conf, int duration) {
47+
Set<Class<? extends Codec>> set = codecs.get(format);
48+
if (set == null) return null;
49+
50+
Optional<Class<? extends Codec>> opt = set.stream().findFirst();
51+
if (!opt.isPresent()) return null;
52+
53+
try {
54+
Class<? extends Codec> clazz = opt.get();
55+
return clazz.getConstructor(GeneralAudioStream.class, NormalizationData.class, PlayerConfiguration.class, int.class).newInstance(audioFile, normalizationData, conf, duration);
56+
} catch (ReflectiveOperationException ex) {
57+
LOGGER.error("Failed initializing Codec instance for {}", format, ex);
58+
return null;
59+
}
60+
}
61+
62+
public static void registerCodec(@NotNull SuperAudioFormat format, @NotNull Class<? extends Codec> clazz) {
63+
codecs.computeIfAbsent(format, (key) -> new HashSet<>(5)).add(clazz);
64+
}
65+
66+
public static void replaceCodecs(@NotNull SuperAudioFormat format, @NotNull Class<? extends Codec> clazz) {
67+
Set<Class<? extends Codec>> set = codecs.get(format);
68+
if (set != null) set.clear();
69+
registerCodec(format, clazz);
70+
}
71+
72+
public static void unregisterCodec(@NotNull Class<? extends Codec> clazz) {
73+
for (Set<Class<? extends Codec>> set : codecs.values())
74+
set.remove(clazz);
75+
}
76+
}

player/src/main/java/xyz/gianlu/librespot/player/playback/PlayerQueueEntry.java

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package xyz.gianlu.librespot.player.playback;
1818

19-
import javazoom.jl.decoder.BitstreamException;
2019
import org.jetbrains.annotations.NotNull;
2120
import org.jetbrains.annotations.Nullable;
2221
import org.slf4j.Logger;
@@ -33,8 +32,7 @@
3332
import xyz.gianlu.librespot.player.PlayerConfiguration;
3433
import xyz.gianlu.librespot.player.StateWrapper;
3534
import xyz.gianlu.librespot.player.codecs.Codec;
36-
import xyz.gianlu.librespot.player.codecs.Mp3Codec;
37-
import xyz.gianlu.librespot.player.codecs.VorbisCodec;
35+
import xyz.gianlu.librespot.player.codecs.Codecs;
3836
import xyz.gianlu.librespot.player.codecs.VorbisOnlyAudioQuality;
3937
import xyz.gianlu.librespot.player.crossfade.CrossfadeController;
4038
import xyz.gianlu.librespot.player.metrics.PlaybackMetrics;
@@ -132,20 +130,9 @@ private void load(boolean preload) throws IOException, Codec.CodecException, Mer
132130
if (crossfade.hasAnyFadeOut() || conf.preloadEnabled)
133131
notifyInstant(INSTANT_PRELOAD, (int) (crossfade.fadeOutStartTimeMin() - TimeUnit.SECONDS.toMillis(20)));
134132

135-
switch (stream.in.codec()) {
136-
case VORBIS:
137-
codec = new VorbisCodec(stream.in, stream.normalizationData, conf, metadata.duration());
138-
break;
139-
case MP3:
140-
try {
141-
codec = new Mp3Codec(stream.in, stream.normalizationData, conf, metadata.duration());
142-
} catch (BitstreamException ex) {
143-
throw new IOException(ex);
144-
}
145-
break;
146-
default:
147-
throw new UnsupportedEncodingException(stream.in.codec().toString());
148-
}
133+
codec = Codecs.initCodec(stream.in.codec(), stream.in, stream.normalizationData, conf, metadata.duration());
134+
if (codec == null)
135+
throw new UnsupportedEncodingException(stream.in.codec().toString());
149136

150137
LOGGER.trace("Loaded {} codec. {of: {}, format: {}, playbackId: {}}", stream.in.codec(), stream.in.describe(), codec.getAudioFormat(), playbackId);
151138
}

player/src/main/java/xyz/gianlu/librespot/player/state/DeviceStateHandler.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.io.UnsupportedEncodingException;
4545
import java.net.URLDecoder;
4646
import java.util.*;
47+
import java.util.concurrent.RejectedExecutionException;
4748

4849
/**
4950
* @author Gianlu
@@ -65,6 +66,7 @@ public final class DeviceStateHandler implements Closeable, DealerClient.Message
6566
private final Connect.PutStateRequest.Builder putState;
6667
private final AsyncWorker<Connect.PutStateRequest> putStateWorker;
6768
private volatile String connectionId = null;
69+
private volatile boolean closing = false;
6870

6971
public DeviceStateHandler(@NotNull Session session, @NotNull PlayerConfiguration conf) {
7072
this.session = session;
@@ -226,7 +228,11 @@ public synchronized void updateState(@NotNull Connect.PutStateReason reason, int
226228
.setClientSideTimestamp(TimeProvider.currentTimeMillis())
227229
.getDeviceBuilder().setDeviceInfo(deviceInfo).setPlayerState(state);
228230

229-
putStateWorker.submit(putState.build());
231+
try {
232+
putStateWorker.submit(putState.build());
233+
} catch (RejectedExecutionException ex) {
234+
if (!closing) LOGGER.error("Failed to submit update state task.", ex);
235+
}
230236
}
231237

232238
public synchronized int getVolume() {
@@ -244,6 +250,8 @@ public void setVolume(int val) {
244250

245251
@Override
246252
public void close() {
253+
closing = true;
254+
247255
session.dealer().removeMessageListener(this);
248256
session.dealer().removeRequestListener(this);
249257

0 commit comments

Comments
 (0)