|
1 | 1 | package xyz.gianlu.librespot.player; |
2 | 2 |
|
3 | | -import com.google.gson.JsonArray; |
4 | | -import com.google.gson.JsonObject; |
5 | | -import com.google.protobuf.ByteString; |
6 | 3 | import org.apache.log4j.Logger; |
7 | 4 | import org.jetbrains.annotations.NotNull; |
8 | 5 | import xyz.gianlu.librespot.common.Utils; |
9 | 6 | import xyz.gianlu.librespot.common.proto.Spirc; |
10 | 7 | import xyz.gianlu.librespot.core.Session; |
11 | | -import xyz.gianlu.librespot.mercury.MercuryClient; |
12 | | -import xyz.gianlu.librespot.mercury.MercuryRequests; |
13 | | -import xyz.gianlu.librespot.mercury.model.TrackId; |
14 | 8 | import xyz.gianlu.librespot.spirc.FrameListener; |
15 | 9 | import xyz.gianlu.librespot.spirc.SpotifyIrc; |
16 | 10 |
|
17 | 11 | import java.io.IOException; |
18 | | -import java.util.ArrayList; |
19 | | -import java.util.Collections; |
20 | | -import java.util.Iterator; |
21 | | -import java.util.List; |
| 12 | +import java.util.*; |
22 | 13 |
|
23 | 14 | /** |
24 | 15 | * @author Gianlu |
25 | 16 | */ |
26 | 17 | public class Player implements FrameListener, TrackHandler.Listener { |
27 | 18 | private static final Logger LOGGER = Logger.getLogger(Player.class); |
28 | | - |
29 | 19 | private final Session session; |
30 | 20 | private final SpotifyIrc spirc; |
31 | 21 | private final Spirc.State.Builder state; |
32 | 22 | private final PlayerConfiguration conf; |
33 | 23 | private final CacheManager cacheManager; |
34 | 24 | private TrackHandler trackHandler; |
35 | 25 | private TrackHandler preloadTrackHandler; |
| 26 | + private long shuffleSeed; |
36 | 27 |
|
37 | 28 | public Player(@NotNull PlayerConfiguration conf, @NotNull CacheManager.CacheConfiguration cacheConfiguration, @NotNull Session session) { |
38 | 29 | this.conf = conf; |
@@ -176,51 +167,57 @@ private int getPosition() { |
176 | 167 | return state.getPositionMs() + diff; |
177 | 168 | } |
178 | 169 |
|
| 170 | + private static int[] getShuffleExchanges(int size, long seed) { |
| 171 | + int[] exchanges = new int[size - 1]; |
| 172 | + Random rand = new Random(seed); |
| 173 | + for (int i = size - 1; i > 0; i--) { |
| 174 | + int n = rand.nextInt(i + 1); |
| 175 | + exchanges[size - 1 - i] = n; |
| 176 | + } |
| 177 | + return exchanges; |
| 178 | + } |
| 179 | + |
179 | 180 | private void shuffleTracks() { |
| 181 | + shuffleSeed = session.random().nextLong(); |
| 182 | + |
180 | 183 | List<Spirc.TrackRef> tracks = new ArrayList<>(state.getTrackList()); |
181 | 184 | if (state.getPlayingTrackIndex() != 0) { |
182 | 185 | Collections.swap(tracks, 0, state.getPlayingTrackIndex()); |
183 | 186 | state.setPlayingTrackIndex(0); |
184 | 187 | } |
185 | 188 |
|
186 | | - for (int i = tracks.size(); i > 1; i--) |
187 | | - Collections.swap(tracks, i - 1, session.random().nextInt(i - 1) + 1); |
| 189 | + int size = tracks.size() - 1; |
| 190 | + int[] exchanges = getShuffleExchanges(size, shuffleSeed); |
| 191 | + for (int i = size - 1; i > 0; i--) { |
| 192 | + int n = exchanges[size - 1 - i]; |
| 193 | + Collections.swap(tracks, i, n); |
| 194 | + } |
188 | 195 |
|
189 | 196 | state.clearTrack(); |
190 | 197 | state.addAllTrack(tracks); |
191 | 198 | } |
192 | 199 |
|
193 | | - private void handleShuffle() { |
194 | | - if (state.getShuffle()) { |
195 | | - shuffleTracks(); |
196 | | - } else { |
197 | | - String contextUri = state.getContextUri(); |
198 | | - |
199 | | - JsonArray tracks; |
200 | | - try { |
201 | | - MercuryRequests.ResolvedContextWrapper context = session.mercury().sendSync(MercuryRequests.resolveContext(contextUri)); |
202 | | - tracks = context.pages().get(0).getAsJsonObject().getAsJsonArray("tracks"); |
203 | | - } catch (IOException | MercuryClient.MercuryException ex) { |
204 | | - LOGGER.fatal("Failed requesting context!", ex); |
205 | | - return; |
206 | | - } |
207 | | - |
208 | | - int num = Math.min(100, tracks.size()); |
209 | | - List<Spirc.TrackRef> rebuildState = new ArrayList<>(num); |
210 | | - for (int i = 0; i < num; i++) { |
211 | | - JsonObject track = tracks.get(i).getAsJsonObject(); |
212 | | - rebuildState.add(Spirc.TrackRef.newBuilder() |
213 | | - .setGid(ByteString.copyFrom(TrackId.fromUri(track.get("uri").getAsString()).getGid())) |
214 | | - .build()); |
215 | | - } |
216 | | - |
217 | | - Spirc.TrackRef current = state.getTrack(state.getPlayingTrackIndex()); |
| 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 | + } |
218 | 206 |
|
219 | | - state.clearTrack(); |
220 | | - state.addAllTrack(rebuildState); |
221 | | - state.setPlayingTrackIndex(Utils.indexOf(rebuildState, current)); |
| 207 | + int size = tracks.size() - 1; |
| 208 | + int[] exchanges = getShuffleExchanges(size, shuffleSeed); |
| 209 | + for (int i = 1; i < size; i++) { |
| 210 | + int n = exchanges[size - i - 1]; |
| 211 | + Collections.swap(tracks, i, n); |
222 | 212 | } |
223 | 213 |
|
| 214 | + state.clearTrack(); |
| 215 | + state.addAllTrack(tracks); |
| 216 | + } |
| 217 | + |
| 218 | + private void handleShuffle() { |
| 219 | + if (state.getShuffle()) shuffleTracks(); |
| 220 | + else unshuffleTracks(); |
224 | 221 | stateUpdated(); |
225 | 222 | } |
226 | 223 |
|
|
0 commit comments