Skip to content

Commit a7b18bb

Browse files
authored
Merge pull request #34 from librespot-org/radio-dev
Radio implementation
2 parents 147b1f0 + a2560df commit a7b18bb

6 files changed

Lines changed: 392 additions & 111 deletions

File tree

core/src/main/java/xyz/gianlu/librespot/mercury/model/TrackId.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ public final class TrackId implements SpotifyId {
1717
private final String hexId;
1818

1919
private TrackId(@NotNull String hex) {
20-
this.hexId = hex;
20+
if (hex.length() == 32) this.hexId = hex;
21+
else if (hex.length() == 34 && hex.startsWith("00")) this.hexId = hex.substring(2);
22+
else throw new IllegalArgumentException("Illegal track id: " + hex);
2123
}
2224

2325
@NotNull

core/src/main/java/xyz/gianlu/librespot/player/Player.java

Lines changed: 107 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
import xyz.gianlu.librespot.common.Utils;
66
import xyz.gianlu.librespot.common.proto.Spirc;
77
import xyz.gianlu.librespot.core.Session;
8+
import xyz.gianlu.librespot.mercury.model.TrackId;
9+
import xyz.gianlu.librespot.player.tracks.PlaylistProvider;
10+
import xyz.gianlu.librespot.player.tracks.StationProvider;
11+
import xyz.gianlu.librespot.player.tracks.TracksProvider;
812
import xyz.gianlu.librespot.spirc.FrameListener;
913
import xyz.gianlu.librespot.spirc.SpotifyIrc;
1014

1115
import java.io.IOException;
12-
import java.util.*;
1316

1417
/**
1518
* @author Gianlu
@@ -18,18 +21,18 @@ public class Player implements FrameListener, TrackHandler.Listener {
1821
private static final Logger LOGGER = Logger.getLogger(Player.class);
1922
private final Session session;
2023
private final SpotifyIrc spirc;
21-
private final Spirc.State.Builder state;
24+
private final StateWrapper state;
2225
private final PlayerConfiguration conf;
2326
private final CacheManager cacheManager;
27+
private TracksProvider tracksProvider;
2428
private TrackHandler trackHandler;
2529
private TrackHandler preloadTrackHandler;
26-
private long shuffleSeed = 0;
2730

2831
public Player(@NotNull PlayerConfiguration conf, @NotNull CacheManager.CacheConfiguration cacheConfiguration, @NotNull Session session) {
2932
this.conf = conf;
3033
this.session = session;
3134
this.spirc = session.spirc();
32-
this.state = initState();
35+
this.state = new StateWrapper(initState());
3336

3437
try {
3538
this.cacheManager = new CacheManager(cacheConfiguration);
@@ -40,16 +43,6 @@ public Player(@NotNull PlayerConfiguration conf, @NotNull CacheManager.CacheConf
4043
spirc.addListener(this);
4144
}
4245

43-
private static int[] getShuffleExchanges(int size, long seed) {
44-
int[] exchanges = new int[size - 1];
45-
Random rand = new Random(seed);
46-
for (int i = size - 1; i > 0; i--) {
47-
int n = rand.nextInt(i + 1);
48-
exchanges[size - 1 - i] = n;
49-
}
50-
return exchanges;
51-
}
52-
5346
public void playPause() {
5447
handlePlayPause();
5548
}
@@ -137,8 +130,8 @@ public void frame(@NotNull Spirc.Frame frame) {
137130
}
138131

139132
private void handlePlayPause() {
140-
if (state.getStatus() == Spirc.PlayStatus.kPlayStatusPlay) handlePause();
141-
else if (state.getStatus() == Spirc.PlayStatus.kPlayStatusPause) handlePlay();
133+
if (state.isStatus(Spirc.PlayStatus.kPlayStatusPlay)) handlePause();
134+
else if (state.isStatus(Spirc.PlayStatus.kPlayStatusPause)) handlePlay();
142135
}
143136

144137
private void handleSetVolume(int volume) {
@@ -169,58 +162,34 @@ private void handleVolumeUp() {
169162
}
170163

171164
private void stateUpdated() {
172-
spirc.deviceStateUpdated(state);
165+
spirc.deviceStateUpdated(state.state);
173166
}
174167

175168
private int getPosition() {
176169
int diff = (int) (System.currentTimeMillis() - state.getPositionMeasuredAt());
177170
return state.getPositionMs() + diff;
178171
}
179172

180-
private void shuffleTracks() {
181-
shuffleSeed = session.random().nextLong();
182-
183-
List<Spirc.TrackRef> tracks = new ArrayList<>(state.getTrackList());
184-
if (state.getPlayingTrackIndex() != 0) {
185-
Collections.swap(tracks, 0, state.getPlayingTrackIndex());
186-
state.setPlayingTrackIndex(0);
187-
}
188-
189-
int size = tracks.size() - 1;
190-
int[] exchanges = getShuffleExchanges(size, shuffleSeed);
191-
for (int i = size - 1; i > 1; i--) {
192-
int n = exchanges[size - 1 - i];
193-
Collections.swap(tracks, i, n + 1);
194-
}
195-
196-
state.clearTrack();
197-
state.addAllTrack(tracks);
198-
}
199-
200-
private void unshuffleTracks() {
201-
List<Spirc.TrackRef> tracks = new ArrayList<>(state.getTrackList());
202-
if (state.getPlayingTrackIndex() != 0) {
203-
Collections.swap(tracks, 0, state.getPlayingTrackIndex());
204-
state.setPlayingTrackIndex(0);
205-
}
206-
207-
int size = tracks.size() - 1;
208-
int[] exchanges = getShuffleExchanges(size, shuffleSeed);
209-
for (int i = 2; i < size; i++) {
210-
int n = exchanges[size - i - 1];
211-
Collections.swap(tracks, i, n + 1);
212-
}
213-
214-
state.clearTrack();
215-
state.addAllTrack(tracks);
216-
}
217-
218173
private void handleShuffle() {
219174
if (state.getShuffle()) shuffleTracks();
220175
else unshuffleTracks();
221176
stateUpdated();
222177
}
223178

179+
private void shuffleTracks() {
180+
if (tracksProvider instanceof PlaylistProvider)
181+
((PlaylistProvider) tracksProvider).shuffleTracks(session.random());
182+
else
183+
LOGGER.warn("Cannot shuffle TracksProvider: " + tracksProvider);
184+
}
185+
186+
private void unshuffleTracks() {
187+
if (tracksProvider instanceof PlaylistProvider)
188+
((PlaylistProvider) tracksProvider).unshuffleTracks();
189+
else
190+
LOGGER.warn("Cannot unshuffle TracksProvider: " + tracksProvider);
191+
}
192+
224193
private void handleSeek(int pos) {
225194
state.setPositionMs(pos);
226195
state.setPositionMeasuredAt(System.currentTimeMillis());
@@ -229,10 +198,12 @@ private void handleSeek(int pos) {
229198
}
230199

231200
private void updatedTracks(@NotNull Spirc.Frame frame) {
232-
state.setPlayingTrackIndex(frame.getState().getPlayingTrackIndex());
233-
state.clearTrack();
234-
state.addAllTrack(frame.getState().getTrackList());
235-
state.setContextUri(frame.getState().getContextUri());
201+
state.update(frame);
202+
String context = frame.getState().getContextUri();
203+
204+
if (context.startsWith("spotify:station:")) tracksProvider = new StationProvider(session, state.state, frame);
205+
else tracksProvider = new PlaylistProvider(state.state, frame);
206+
236207
state.setRepeat(frame.getState().getRepeat());
237208
state.setShuffle(frame.getState().getShuffle());
238209
if (state.getShuffle()) shuffleTracks();
@@ -250,9 +221,9 @@ public void finishedLoading(@NotNull TrackHandler handler, boolean play) {
250221
}
251222

252223
@Override
253-
public void loadingError(@NotNull TrackHandler handler, @NotNull Exception ex) {
224+
public void loadingError(@NotNull TrackHandler handler, @NotNull TrackId id, @NotNull Exception ex) {
254225
if (handler == trackHandler) {
255-
LOGGER.fatal("Failed loading track!", ex);
226+
LOGGER.fatal(String.format("Failed loading track, gid: %s", Utils.bytesToHex(id.getGid())), ex);
256227
state.setStatus(Spirc.PlayStatus.kPlayStatusStop);
257228
stateUpdated();
258229
} else if (handler == preloadTrackHandler) {
@@ -272,7 +243,7 @@ public void endOfTrack(@NotNull TrackHandler handler) {
272243
@Override
273244
public void preloadNextTrack(@NotNull TrackHandler handler) {
274245
if (handler == trackHandler) {
275-
Spirc.TrackRef next = state.getTrack(getQueuedTrack(false));
246+
TrackId next = tracksProvider.getTrackAt(tracksProvider.getNextTrackIndex(false));
276247

277248
preloadTrackHandler = new TrackHandler(session, cacheManager, conf, this);
278249
preloadTrackHandler.sendLoad(next, false, 0);
@@ -287,6 +258,8 @@ private void handleLoad(@NotNull Spirc.Frame frame) {
287258
.setBecameActiveAt(System.currentTimeMillis());
288259
}
289260

261+
LOGGER.debug(String.format("Loading context, uri: %s", frame.getState().getContextUri()));
262+
290263
updatedTracks(frame);
291264

292265
if (state.getTrackCount() > 0) {
@@ -304,14 +277,14 @@ private void handleLoad(@NotNull Spirc.Frame frame) {
304277
private void loadTrack(boolean play) {
305278
if (trackHandler != null) trackHandler.close();
306279

307-
Spirc.TrackRef ref = state.getTrack(state.getPlayingTrackIndex());
308-
if (preloadTrackHandler != null && preloadTrackHandler.isTrack(ref)) {
280+
TrackId id = tracksProvider.getCurrentTrack();
281+
if (preloadTrackHandler != null && preloadTrackHandler.isTrack(id)) {
309282
trackHandler = preloadTrackHandler;
310283
preloadTrackHandler = null;
311284
trackHandler.sendSeek(state.getPositionMs());
312285
} else {
313286
trackHandler = new TrackHandler(session, cacheManager, conf, this);
314-
trackHandler.sendLoad(ref, play, state.getPositionMs());
287+
trackHandler.sendLoad(id, play, state.getPositionMs());
315288
state.setStatus(Spirc.PlayStatus.kPlayStatusLoading);
316289
}
317290

@@ -326,7 +299,7 @@ private void loadTrack(boolean play) {
326299
}
327300

328301
private void handlePlay() {
329-
if (state.getStatus() == Spirc.PlayStatus.kPlayStatusPause) {
302+
if (state.isStatus(Spirc.PlayStatus.kPlayStatusPause)) {
330303
if (trackHandler != null) trackHandler.sendPlay();
331304
state.setStatus(Spirc.PlayStatus.kPlayStatusPlay);
332305
state.setPositionMeasuredAt(System.currentTimeMillis());
@@ -335,7 +308,7 @@ private void handlePlay() {
335308
}
336309

337310
private void handlePause() {
338-
if (state.getStatus() == Spirc.PlayStatus.kPlayStatusPlay) {
311+
if (state.isStatus(Spirc.PlayStatus.kPlayStatusPlay)) {
339312
if (trackHandler != null) trackHandler.sendPause();
340313
state.setStatus(Spirc.PlayStatus.kPlayStatusPause);
341314

@@ -349,7 +322,7 @@ private void handlePause() {
349322
}
350323

351324
private void handleNext() {
352-
int newTrack = getQueuedTrack(true);
325+
int newTrack = tracksProvider.getNextTrackIndex(true);
353326
boolean play = true;
354327
if (newTrack >= state.getTrackCount()) {
355328
newTrack = 0;
@@ -365,26 +338,7 @@ private void handleNext() {
365338

366339
private void handlePrev() {
367340
if (getPosition() < 3000) {
368-
List<Spirc.TrackRef> queueTracks = new ArrayList<>();
369-
Iterator<Spirc.TrackRef> iter = state.getTrackList().iterator();
370-
while (iter.hasNext()) {
371-
Spirc.TrackRef track = iter.next();
372-
if (track.getQueued()) {
373-
queueTracks.add(track);
374-
iter.remove();
375-
}
376-
}
377-
378-
int current = state.getPlayingTrackIndex();
379-
int newIndex;
380-
if (current > 0) newIndex = current - 1;
381-
else if (state.getRepeat()) newIndex = state.getTrackCount() - 1;
382-
else newIndex = 0;
383-
384-
for (int i = 0; i < queueTracks.size(); i++)
385-
state.getTrackList().add(newIndex + 1 + i, queueTracks.get(i));
386-
387-
state.setPlayingTrackIndex(newIndex);
341+
state.setPlayingTrackIndex(tracksProvider.getPrevTrackIndex(true));
388342
state.setPositionMs(0);
389343
state.setPositionMeasuredAt(System.currentTimeMillis());
390344

@@ -397,16 +351,6 @@ private void handlePrev() {
397351
}
398352
}
399353

400-
private int getQueuedTrack(boolean consume) {
401-
int current = state.getPlayingTrackIndex();
402-
if (state.getTrack(current).getQueued()) {
403-
if (consume) state.removeTrack(current);
404-
return current;
405-
}
406-
407-
return current + 1;
408-
}
409-
410354
public interface PlayerConfiguration {
411355
@NotNull
412356
StreamFeeder.AudioQuality preferredQuality();
@@ -415,4 +359,69 @@ public interface PlayerConfiguration {
415359

416360
float normalisationPregain();
417361
}
362+
363+
private class StateWrapper {
364+
private final Spirc.State.Builder state;
365+
366+
StateWrapper(@NotNull Spirc.State.Builder state) {
367+
this.state = state;
368+
}
369+
370+
@NotNull
371+
Spirc.PlayStatus getStatus() {
372+
return state.getStatus();
373+
}
374+
375+
void setStatus(@NotNull Spirc.PlayStatus status) {
376+
state.setStatus(status);
377+
}
378+
379+
boolean isStatus(@NotNull Spirc.PlayStatus status) {
380+
return status == getStatus();
381+
}
382+
383+
boolean getShuffle() {
384+
return state.getShuffle();
385+
}
386+
387+
void setShuffle(boolean shuffle) {
388+
state.setShuffle(shuffle && (tracksProvider == null || tracksProvider.canShuffle()));
389+
}
390+
391+
void update(@NotNull Spirc.Frame frame) {
392+
state.setContextUri(frame.getState().getContextUri());
393+
}
394+
395+
long getPositionMeasuredAt() {
396+
return state.getPositionMeasuredAt();
397+
}
398+
399+
void setPositionMeasuredAt(long ms) {
400+
state.setPositionMeasuredAt(ms);
401+
}
402+
403+
int getPositionMs() {
404+
return state.getPositionMs();
405+
}
406+
407+
void setPositionMs(int pos) {
408+
state.setPositionMs(pos);
409+
}
410+
411+
boolean getRepeat() {
412+
return state.getRepeat();
413+
}
414+
415+
void setRepeat(boolean repeat) {
416+
state.setRepeat(repeat && (tracksProvider == null || tracksProvider.canRepeat()));
417+
}
418+
419+
void setPlayingTrackIndex(int i) {
420+
state.setPlayingTrackIndex(i);
421+
}
422+
423+
int getTrackCount() {
424+
return state.getTrackCount();
425+
}
426+
}
418427
}

0 commit comments

Comments
 (0)