Skip to content

Commit 172c2d8

Browse files
committed
Refactored PlayableId + added LocalId + moved "can play" checks to helper functions
1 parent 489a4fd commit 172c2d8

13 files changed

Lines changed: 163 additions & 53 deletions

File tree

api/src/main/java/xyz/gianlu/librespot/api/handlers/PlayerHandler.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import xyz.gianlu.librespot.common.ProtobufToJson;
2626
import xyz.gianlu.librespot.core.Session;
2727
import xyz.gianlu.librespot.metadata.EpisodeId;
28+
import xyz.gianlu.librespot.metadata.LocalId;
2829
import xyz.gianlu.librespot.metadata.PlayableId;
2930
import xyz.gianlu.librespot.metadata.TrackId;
3031
import xyz.gianlu.librespot.player.Player;
@@ -133,6 +134,8 @@ private static void current(HttpServerExchange exchange, @NotNull Player player)
133134
}
134135

135136
obj.add("episode", ProtobufToJson.convert(metadata.episode));
137+
} else if (id instanceof LocalId) {
138+
obj.addProperty("local", ((LocalId) id).fileName()); // TODO: Improve local files metadata
136139
} else if (id != null) {
137140
Utils.internalError(exchange, "Invalid PlayableId: " + id);
138141
return;

lib/src/main/java/xyz/gianlu/librespot/audio/PlayableContentFeeder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import xyz.gianlu.librespot.core.Session;
3737
import xyz.gianlu.librespot.mercury.MercuryClient;
3838
import xyz.gianlu.librespot.metadata.EpisodeId;
39+
import xyz.gianlu.librespot.metadata.LocalId;
3940
import xyz.gianlu.librespot.metadata.PlayableId;
4041
import xyz.gianlu.librespot.metadata.TrackId;
4142

@@ -77,6 +78,8 @@ public final LoadedStream load(@NotNull PlayableId id, @NotNull AudioQualityPick
7778
return loadTrack((TrackId) id, audioQualityPicker, preload, haltListener);
7879
else if (id instanceof EpisodeId)
7980
return loadEpisode((EpisodeId) id, audioQualityPicker, preload, haltListener);
81+
else if (id instanceof LocalId)
82+
throw new UnsupportedOperationException("Cannot play local files!"); // TODO: Play this
8083
else
8184
throw new IllegalArgumentException("Unknown content: " + id);
8285
}

lib/src/main/java/xyz/gianlu/librespot/common/ProtoUtils.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import org.slf4j.Logger;
3535
import org.slf4j.LoggerFactory;
3636
import xyz.gianlu.librespot.metadata.PlayableId;
37-
import xyz.gianlu.librespot.metadata.UnsupportedId;
3837

3938
import java.lang.reflect.Field;
4039
import java.util.*;
@@ -139,8 +138,7 @@ public static ContextTrack jsonToContextTrack(@NotNull JsonObject obj) {
139138
builder.setUri(uri);
140139

141140
PlayableId playable = PlayableId.fromUri(uri);
142-
if (!(playable instanceof UnsupportedId))
143-
builder.setGid(ByteString.copyFrom(playable.getGid()));
141+
if (playable.hasGid()) builder.setGid(ByteString.copyFrom(playable.getGid()));
144142
}
145143

146144
Optional.ofNullable(Utils.optString(obj, "uid", null)).ifPresent(builder::setUid);

lib/src/main/java/xyz/gianlu/librespot/metadata/EpisodeId.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ public static EpisodeId fromHex(@NotNull String hex) {
6868
return hexId;
6969
}
7070

71+
@Override
72+
public boolean hasGid() {
73+
return true;
74+
}
75+
7176
@Override
7277
@NotNull
7378
public byte[] getGid() {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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.metadata;
18+
19+
import org.jetbrains.annotations.NotNull;
20+
21+
/**
22+
* @author devgianlu
23+
*/
24+
public final class LocalId implements PlayableId {
25+
private final String uri;
26+
27+
LocalId(@NotNull String uri) {
28+
this.uri = uri;
29+
}
30+
31+
@Override
32+
public boolean hasGid() {
33+
return false;
34+
}
35+
36+
@Override
37+
public @NotNull String toSpotifyUri() {
38+
return uri;
39+
}
40+
41+
public @NotNull String fileName() {
42+
return uri.substring("spotify:local:".length());
43+
}
44+
45+
@Override
46+
public String toString() {
47+
return "LocalId{" + toSpotifyUri() + "}";
48+
}
49+
}

lib/src/main/java/xyz/gianlu/librespot/metadata/PlayableId.java

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import java.util.Arrays;
2828
import java.util.List;
29-
import java.util.Objects;
3029

3130
import static com.spotify.context.ContextTrackOuterClass.ContextTrack;
3231

@@ -38,21 +37,17 @@ public interface PlayableId {
3837

3938
@NotNull
4039
static PlayableId fromUri(@NotNull String uri) {
41-
if (!isSupported(uri)) return new UnsupportedId(uri);
42-
43-
if (TrackId.PATTERN.matcher(uri).matches()) {
44-
return TrackId.fromUri(uri);
45-
} else if (EpisodeId.PATTERN.matcher(uri).matches()) {
46-
return EpisodeId.fromUri(uri);
47-
} else {
48-
throw new IllegalArgumentException("Unknown uri: " + uri);
49-
}
40+
if (isDelimiter(uri)) return new UnsupportedId(uri);
41+
else if (isLocal(uri)) return new LocalId(uri);
42+
else if (TrackId.PATTERN.matcher(uri).matches()) return TrackId.fromUri(uri);
43+
else if (EpisodeId.PATTERN.matcher(uri).matches()) return EpisodeId.fromUri(uri);
44+
else throw new IllegalArgumentException("Unknown uri: " + uri);
5045
}
5146

5247
static int indexOfTrack(@NotNull List<ContextTrack> tracks, @NotNull PlayableId id) {
5348
ByteString gid;
54-
if (id instanceof UnsupportedId) gid = null;
55-
else gid = ByteString.copyFrom(id.getGid());
49+
if (id.hasGid()) gid = ByteString.copyFrom(id.getGid());
50+
else gid = null;
5651

5752
String uri = id.toSpotifyUri();
5853
for (int i = 0; i < tracks.size(); i++) {
@@ -64,26 +59,17 @@ static int indexOfTrack(@NotNull List<ContextTrack> tracks, @NotNull PlayableId
6459
return -1;
6560
}
6661

67-
static boolean canPlaySomething(@NotNull List<ContextTrack> tracks) {
68-
for (ContextTrack track : tracks)
69-
if (PlayableId.isSupported(track.getUri()) && shouldPlay(track))
70-
return true;
71-
72-
return false;
73-
}
74-
7562
@Nullable
7663
static PlayableId from(@NotNull Player.ProvidedTrack track) {
7764
return track.getUri().isEmpty() ? null : fromUri(track.getUri());
7865
}
7966

80-
static boolean isSupported(@NotNull String uri) {
81-
return !uri.startsWith("spotify:local:") && !Objects.equals(uri, "spotify:delimiter")
82-
&& !Objects.equals(uri, "spotify:meta:delimiter");
67+
static boolean isDelimiter(@NotNull String uri) {
68+
return uri.equals("spotify:delimiter") || uri.equals("spotify:meta:delimiter");
8369
}
8470

85-
static boolean shouldPlay(@NotNull ContextTrack track) {
86-
return track.getMetadataOrDefault("force_remove_reasons", "").isEmpty();
71+
static boolean isLocal(@NotNull String uri) {
72+
return uri.startsWith("spotify:local:");
8773
}
8874

8975
@NotNull
@@ -114,16 +100,25 @@ static String inferUriPrefix(@NotNull String contextUri) {
114100

115101
int hashCode();
116102

117-
@NotNull byte[] getGid();
103+
/**
104+
* @return Whether this {@link PlayableId} has a GID and hex ID.
105+
*/
106+
boolean hasGid();
118107

119-
@NotNull String hexId();
108+
default @NotNull byte[] getGid() {
109+
throw new UnsupportedOperationException();
110+
}
111+
112+
default @NotNull String hexId() {
113+
throw new UnsupportedOperationException();
114+
}
120115

121116
@NotNull String toSpotifyUri();
122117

123118
default boolean matches(@NotNull ContextTrack current) {
124119
if (current.hasUri())
125120
return toSpotifyUri().equals(current.getUri());
126-
else if (current.hasGid() && !(this instanceof UnsupportedId))
121+
else if (current.hasGid() && hasGid())
127122
return Arrays.equals(current.getGid().toByteArray(), getGid());
128123
else
129124
return false;

lib/src/main/java/xyz/gianlu/librespot/metadata/TrackId.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ public static TrackId fromHex(@NotNull String hex) {
6868
return hexId;
6969
}
7070

71+
@Override
72+
public boolean hasGid() {
73+
return true;
74+
}
75+
7176
@Override
7277
@NotNull
7378
public byte[] getGid() {

lib/src/main/java/xyz/gianlu/librespot/metadata/UnsupportedId.java

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

1717
package xyz.gianlu.librespot.metadata;
1818

19-
import org.jetbrains.annotations.Contract;
2019
import org.jetbrains.annotations.NotNull;
2120

2221
/**
@@ -30,15 +29,8 @@ public final class UnsupportedId implements PlayableId {
3029
}
3130

3231
@Override
33-
@Contract("-> fail")
34-
public @NotNull byte[] getGid() {
35-
throw new UnsupportedOperationException();
36-
}
37-
38-
@Override
39-
@Contract("-> fail")
40-
public @NotNull String hexId() {
41-
throw new UnsupportedOperationException();
32+
public boolean hasGid() {
33+
return false;
4234
}
4335

4436
@Override

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ public PlayerConfiguration toPlayer() {
436436
.setReleaseLineDelay(config.get("player.releaseLineDelay"))
437437
.setVolumeSteps(config.get("player.volumeSteps"))
438438
.setBypassSinkVolume(config.get("player.bypassSinkVolume"))
439+
.setLocalFilesPath(getFile("player.localFilesPath"))
439440
.build();
440441
}
441442

player/src/main/java/xyz/gianlu/librespot/player/PlayerConfiguration.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@ public final class PlayerConfiguration {
4848
public final int volumeSteps;
4949
public final boolean bypassSinkVolume;
5050

51+
// Local files
52+
public final File localFilesPath;
53+
5154
private PlayerConfiguration(AudioQuality preferredQuality, boolean enableNormalisation, float normalisationPregain, boolean autoplayEnabled, int crossfadeDuration, boolean preloadEnabled,
5255
AudioOutput output, String outputClass, File outputPipe, File metadataPipe, String[] mixerSearchKeywords, boolean logAvailableMixers, int releaseLineDelay,
53-
int initialVolume, int volumeSteps, boolean bypassSinkVolume) {
56+
int initialVolume, int volumeSteps, boolean bypassSinkVolume, File localFilesPath) {
5457
this.preferredQuality = preferredQuality;
5558
this.enableNormalisation = enableNormalisation;
5659
this.normalisationPregain = normalisationPregain;
@@ -67,6 +70,7 @@ private PlayerConfiguration(AudioQuality preferredQuality, boolean enableNormali
6770
this.volumeSteps = volumeSteps;
6871
this.preloadEnabled = preloadEnabled;
6972
this.bypassSinkVolume = bypassSinkVolume;
73+
this.localFilesPath = localFilesPath;
7074
}
7175

7276
public enum AudioOutput {
@@ -96,6 +100,9 @@ public final static class Builder {
96100
private int volumeSteps = 64;
97101
private boolean bypassSinkVolume = false;
98102

103+
// Local files
104+
private File localFilesPath;
105+
99106
public Builder() {
100107
}
101108

@@ -185,11 +192,16 @@ public Builder setBypassSinkVolume(boolean bypassSinkVolume) {
185192
return this;
186193
}
187194

195+
public Builder setLocalFilesPath(File localFilesPath) {
196+
this.localFilesPath = localFilesPath;
197+
return this;
198+
}
199+
188200
@Contract(value = " -> new", pure = true)
189201
public @NotNull PlayerConfiguration build() {
190202
return new PlayerConfiguration(preferredQuality, enableNormalisation, normalisationPregain, autoplayEnabled, crossfadeDuration, preloadEnabled,
191203
output, outputClass, outputPipe, metadataPipe, mixerSearchKeywords, logAvailableMixers, releaseLineDelay,
192-
initialVolume, volumeSteps, bypassSinkVolume);
204+
initialVolume, volumeSteps, bypassSinkVolume, localFilesPath);
193205
}
194206
}
195207
}

0 commit comments

Comments
 (0)