Skip to content

Commit 0b6e80b

Browse files
committed
Merge branch 'dev'
2 parents 6d71955 + 43c9bcf commit 0b6e80b

95 files changed

Lines changed: 3937 additions & 2001 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,35 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.4.0] - 14-06-2020
8+
### Added
9+
- Report to server that we played a track (#155, still buggy)
10+
- Retrieve tracks in state from API (#222)
11+
- Add/remove tracks from queue from API (#222)
12+
- Seek from API (6104db1a66b9defabc43ec21e6668899cf8c2683)
13+
- Added logout feature (5839d5b48133cd69cf59a028ee423b44caec8627)
14+
15+
### Changed
16+
- Rewritten player and related bug fixing (#155, 08282b94d6adc7e5e5a07fe20c150829fc912943, #216, #217, 13423cdcfd39b8a5722e3a14bb19e53ab426b4ea, e549f08356696ef46add79f60ad8e28891738bd7)
17+
- Using Log4j2 (5bb16797b78a76618821d28f547e92c217bdb8c8, 73a668c47db36932567777216330479d8147d80b, 52a60cbf0c059d4582dfe278a65bf786a4df43a3, 548163e4d3b2267d3030f7597ed5b4e8eeeab29c)
18+
- Improved error message for mixers (#220)
19+
- Refactored audio quality selection (#223)
20+
- Close readers properly (6ad0f3cddc89e6d172eb16c5635ed46128eb65db)
21+
- Better truncation of sensitive values (661c171fcd353471924a4ae0544dc27ff0ca83d0)
22+
23+
### Fixed
24+
- Fixed time bar in wrong position after resuming (#213)
25+
- Do not get Cipher instance every time (#215)
26+
- Fixed loading of some podcasts (#223)
27+
- Fixed crash when pressing next after adding song to queue (#226)
28+
- Prevent deadlock when closing after network issue (#227)
29+
- Fixed old issue with Zeroconf active session (#225, #229, #231)
30+
- Avoid establishing two connections (afc7dc366379391f7e59ce8a37a0b007b2c69303)
31+
- Start line before writing for the first time (#232)
32+
- Do not close `System.out` for any reason (#234)
33+
- Shutdown OkHttp threads when closing (#235)
34+
35+
736
## [1.3.1] - 26-04-2020
837
### Added
938
- Added explicit content filter (#200)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ On some systems, many mixers could be installed making librespot-java playback o
5151
4) Pick the right mixer and copy its name inside the `mixerSearchKeywords` option. If you need to specify more search keywords, you can separate them with a semicolon
5252
5) Restart and enjoy
5353

54+
> **Linux note:** librespot-java will not be able to detect the mixers available on the system if you are running headless OpenJDK. You'll need to install a headful version of OpenJDK (usually doesn't end with `-headless`).
55+
5456
## Build it
5557
This project uses [Maven](https://maven.apache.org/), after installing it you can compile with `mvn clean package` in the project root, if the compilation succeeds you'll be pleased with a JAR executable in `core/target`.
5658
To run the newly build jar run `java -jar ./core/target/librespot-core-jar-with-dependencies.jar`.

api/README.md

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,24 @@ This module depends on `librespot-core` and provides an API to interact with the
55

66
## Available endpoints
77
All the endpoints will respond with `200` if successful or:
8-
- `204`, if there isn't any active session (Zeroconf only)
9-
- `500`, if the session is invalid
10-
- `503`, if the session is reconnecting (`Retry-After` is always 10 seconds)
8+
- `204` If there isn't any active session (Zeroconf only)
9+
- `500` If the session is invalid
10+
- `503` If the session is reconnecting (`Retry-After` is always 10 seconds)
1111

1212
### Player
1313
- `POST /player/load` Load a track from a given URI. The request body should contain two parameters: `uri` and `play`.
1414
- `POST /player/pause` Pause playback.
1515
- `POST /player/resume` Resume playback.
1616
- `POST /player/next` Skip to next track.
1717
- `POST /player/prev` Skip to previous track.
18+
- `POST /player/seek` Seek to a given position in ms specified by `pos`.
1819
- `POST /player/set-volume` Set volume to a given `volume` value from 0 to 65536.
1920
- `POST /player/volume-up` Up the volume a little bit.
2021
- `POST /player/volume-down` Lower the volume a little bit.
2122
- `POST /player/current` Retrieve information about the current track (metadata and time).
23+
- `POST /player/tracks` Retrieve all the tracks in the player state with metadata, you can specify `withQueue`.
24+
- `POST /player/addToQueue` Add a track to the queue, specified by `uri`.
25+
- `POST /player/removeFromQueue` Remove a track from the queue, specified by `uri`.
2226

2327
### Metadata
2428
- `POST /metadata/{type}/{uri}` Retrieve metadata. `type` can be one of `episode`, `track`, `album`, `show`, `artist` or `playlist`, `uri` is the standard Spotify uri.
@@ -33,19 +37,20 @@ All the endpoints will respond with `200` if successful or:
3337
### Events
3438
You can subscribe for players events by creating a WebSocket connection to `/events`.
3539
The currently available events are:
36-
- `contextChanged`, the Spotify context URI changed
37-
- `trackChanged`, the Spotify track URI changed
38-
- `playbackPaused`, playback has been paused
39-
- `playbackResumed`, playback has been resumed
40-
- `volumeChanged`, playback volume changed
41-
- `trackSeeked`, track has been seeked
42-
- `metadataAvailable`, metadata for the current track is available
43-
- `playbackHaltStateChanged`, playback halted or resumed from halt
44-
- `sessionCleared`, (Zeroconf only) current session went away
45-
- `sessionChanged`, (Zeroconf only) current session changed
46-
- `inactiveSession`, current session is now inactive (no audio)
47-
- `connectionDropped`, a network error occurred and we're trying to reconnect
48-
- `connectionEstablished`, successfully reconnected
40+
- `contextChanged` The Spotify context URI changed
41+
- `trackChanged` The Spotify track URI changed
42+
- `playbackPaused` Playback has been paused
43+
- `playbackResumed` Playback has been resumed
44+
- `volumeChanged` Playback volume changed
45+
- `trackSeeked` Track has been seeked
46+
- `metadataAvailable` Metadata for the current track is available
47+
- `playbackHaltStateChanged` Playback halted or resumed from halt
48+
- `sessionCleared` Current session went away (Zeroconf only)
49+
- `sessionChanged` Current session changed (Zeroconf only)
50+
- `inactiveSession` Current session is now inactive (no audio)
51+
- `connectionDropped` A network error occurred and we're trying to reconnect
52+
- `connectionEstablished` Successfully reconnected
53+
- `panic` Entered the panic state, playback is stopped. This is usually recoverable.
4954

5055
## Examples
5156
`curl -X POST -d "uri=spotify:track:xxxxxxxxxxxxxxxxxxxxxx&play=true" http://localhost:24879/player/load`

api/pom.xml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>xyz.gianlu.librespot</groupId>
77
<artifactId>librespot-java</artifactId>
8-
<version>1.3.1</version>
8+
<version>1.4.0</version>
99
<relativePath>../</relativePath>
1010
</parent>
1111

@@ -19,7 +19,7 @@
1919
<plugin>
2020
<groupId>org.apache.maven.plugins</groupId>
2121
<artifactId>maven-assembly-plugin</artifactId>
22-
<version>3.2.0</version>
22+
<version>3.3.0</version>
2323
<executions>
2424
<execution>
2525
<phase>package</phase>
@@ -33,6 +33,9 @@
3333
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
3434
<mainClass>xyz.gianlu.librespot.api.Main</mainClass>
3535
</manifest>
36+
<manifestEntries>
37+
<Multi-Release>true</Multi-Release>
38+
</manifestEntries>
3639
</archive>
3740
<descriptorRefs>
3841
<descriptorRef>jar-with-dependencies</descriptorRef>
@@ -54,7 +57,7 @@
5457
<dependency>
5558
<groupId>io.undertow</groupId>
5659
<artifactId>undertow-core</artifactId>
57-
<version>2.0.29.Final</version>
60+
<version>2.1.0.Final</version>
5861
</dependency>
5962
</dependencies>
6063
</project>

api/src/main/java/xyz/gianlu/librespot/api/ApiServer.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
import io.undertow.Undertow;
44
import io.undertow.server.HttpHandler;
55
import io.undertow.server.RoutingHandler;
6-
import org.apache.log4j.Logger;
6+
import org.apache.logging.log4j.LogManager;
7+
import org.apache.logging.log4j.Logger;
78
import org.jetbrains.annotations.NotNull;
89
import xyz.gianlu.librespot.api.handlers.*;
910

1011
public class ApiServer {
11-
private static final Logger LOGGER = Logger.getLogger(ApiServer.class);
12+
private static final Logger LOGGER = LogManager.getLogger(ApiServer.class);
1213
private final int port;
1314
private final String host;
1415
private final HttpHandler handler;
@@ -35,7 +36,7 @@ public void start() {
3536

3637
undertow = Undertow.builder().addHttpListener(port, host, handler).build();
3738
undertow.start();
38-
LOGGER.info(String.format("Server started on port %d!", port));
39+
LOGGER.info("Server started on port {}!", port);
3940
}
4041

4142
public void stop() {

api/src/main/java/xyz/gianlu/librespot/api/CorsHandler.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import io.undertow.server.HttpHandler;
44
import io.undertow.server.HttpServerExchange;
5-
import io.undertow.server.RoutingHandler;
65
import io.undertow.util.HeaderMap;
76
import io.undertow.util.HttpString;
87

api/src/main/java/xyz/gianlu/librespot/api/Main.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package xyz.gianlu.librespot.api;
22

3-
import org.apache.log4j.LogManager;
3+
4+
import org.apache.logging.log4j.core.config.Configurator;
45
import xyz.gianlu.librespot.AbsConfiguration;
56
import xyz.gianlu.librespot.FileConfiguration;
7+
import xyz.gianlu.librespot.common.Log4JUncaughtExceptionHandler;
68
import xyz.gianlu.librespot.core.AuthConfiguration;
79
import xyz.gianlu.librespot.core.Session;
810
import xyz.gianlu.librespot.core.ZeroconfServer;
@@ -18,7 +20,8 @@ public class Main {
1820

1921
public static void main(String[] args) throws IOException, MercuryClient.MercuryException, GeneralSecurityException, Session.SpotifyAuthenticationException {
2022
AbsConfiguration conf = new FileConfiguration(args);
21-
LogManager.getRootLogger().setLevel(conf.loggingLevel());
23+
Configurator.setRootLevel(conf.loggingLevel());
24+
Thread.setDefaultUncaughtExceptionHandler(new Log4JUncaughtExceptionHandler());
2225

2326
SessionWrapper wrapper;
2427
if (conf.authStrategy() == AuthConfiguration.Strategy.ZEROCONF && !conf.hasStoredCredentials())

api/src/main/java/xyz/gianlu/librespot/api/SessionWrapper.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ private void set(@NotNull Session session) {
5757
}
5858

5959
private void clear() {
60+
Session old = ref.get();
6061
ref.set(null);
61-
if (listener != null) listener.onSessionCleared();
62+
if (listener != null && old != null) listener.onSessionCleared(old);
6263
}
6364

6465
@Nullable
@@ -73,7 +74,7 @@ public Session get() {
7374
}
7475

7576
public interface Listener {
76-
void onSessionCleared();
77+
void onSessionCleared(@NotNull Session old);
7778

7879
void onNewSession(@NotNull Session session);
7980
}

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

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package xyz.gianlu.librespot.api.handlers;
22

33
import com.google.gson.JsonObject;
4-
import com.spotify.metadata.Metadata;
54
import io.undertow.websockets.WebSocketConnectionCallback;
65
import io.undertow.websockets.WebSocketProtocolHandshakeHandler;
76
import io.undertow.websockets.core.WebSocketChannel;
87
import io.undertow.websockets.core.WebSockets;
9-
import org.apache.log4j.Logger;
8+
import org.apache.logging.log4j.LogManager;
9+
import org.apache.logging.log4j.Logger;
1010
import org.jetbrains.annotations.NotNull;
1111
import org.jetbrains.annotations.Nullable;
1212
import org.jetbrains.annotations.Range;
@@ -15,12 +15,13 @@
1515
import xyz.gianlu.librespot.core.Session;
1616
import xyz.gianlu.librespot.mercury.model.PlayableId;
1717
import xyz.gianlu.librespot.player.Player;
18+
import xyz.gianlu.librespot.player.TrackOrEpisode;
1819

1920
public final class EventsHandler extends WebSocketProtocolHandshakeHandler implements Player.EventsListener, SessionWrapper.Listener, Session.ReconnectionListener {
20-
private static final Logger LOGGER = Logger.getLogger(EventsHandler.class);
21+
private static final Logger LOGGER = LogManager.getLogger(EventsHandler.class);
2122

2223
public EventsHandler() {
23-
super((WebSocketConnectionCallback) (exchange, channel) -> LOGGER.info(String.format("Accepted new websocket connection from %s.", channel.getSourceAddress().getAddress())));
24+
super((WebSocketConnectionCallback) (exchange, channel) -> LOGGER.info("Accepted new websocket connection from {}.", channel.getSourceAddress().getAddress()));
2425
}
2526

2627
private void dispatch(@NotNull JsonObject obj) {
@@ -37,12 +38,15 @@ public void onContextChanged(@NotNull String newUri) {
3738
}
3839

3940
@Override
40-
public void onTrackChanged(@NotNull PlayableId id, Metadata.@Nullable Track track, Metadata.@Nullable Episode episode) {
41+
public void onTrackChanged(@NotNull PlayableId id, @Nullable TrackOrEpisode metadata) {
4142
JsonObject obj = new JsonObject();
4243
obj.addProperty("event", "trackChanged");
4344
obj.addProperty("uri", id.toSpotifyUri());
44-
if (track != null) obj.add("track", ProtobufToJson.convert(track));
45-
if (episode != null) obj.add("episode", ProtobufToJson.convert(episode));
45+
if (metadata != null) {
46+
if (metadata.track != null) obj.add("track", ProtobufToJson.convert(metadata.track));
47+
else if (metadata.episode != null) obj.add("episode", ProtobufToJson.convert(metadata.episode));
48+
}
49+
4650
dispatch(obj);
4751
}
4852

@@ -71,11 +75,11 @@ public void onTrackSeeked(long trackTime) {
7175
}
7276

7377
@Override
74-
public void onMetadataAvailable(Metadata.@Nullable Track track, Metadata.@Nullable Episode episode) {
78+
public void onMetadataAvailable(@NotNull TrackOrEpisode metadata) {
7579
JsonObject obj = new JsonObject();
7680
obj.addProperty("event", "metadataAvailable");
77-
if (track != null) obj.add("track", ProtobufToJson.convert(track));
78-
if (episode != null) obj.add("episode", ProtobufToJson.convert(episode));
81+
if (metadata.track != null) obj.add("track", ProtobufToJson.convert(metadata.track));
82+
else if (metadata.episode != null) obj.add("episode", ProtobufToJson.convert(metadata.episode));
7983
dispatch(obj);
8084
}
8185

@@ -105,7 +109,17 @@ public void onVolumeChanged(@Range(from = 0, to = 1) float volume) {
105109
}
106110

107111
@Override
108-
public void onSessionCleared() {
112+
public void onPanicState() {
113+
JsonObject obj = new JsonObject();
114+
obj.addProperty("event", "panic");
115+
dispatch(obj);
116+
}
117+
118+
@Override
119+
public void onSessionCleared(@NotNull Session old) {
120+
old.player().removeEventsListener(this);
121+
old.removeReconnectionListener(this);
122+
109123
JsonObject obj = new JsonObject();
110124
obj.addProperty("event", "sessionCleared");
111125
dispatch(obj);

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
import com.google.gson.JsonObject;
44
import io.undertow.server.HttpServerExchange;
5-
import org.apache.log4j.Logger;
5+
import org.apache.logging.log4j.LogManager;
6+
import org.apache.logging.log4j.Logger;
67
import org.jetbrains.annotations.NotNull;
78
import org.jetbrains.annotations.Nullable;
89
import xyz.gianlu.librespot.api.SessionWrapper;
@@ -23,7 +24,7 @@
2324
* @author Gianlu
2425
*/
2526
public final class MetadataHandler extends AbsSessionHandler {
26-
private static final Logger LOGGER = Logger.getLogger(MetadataHandler.class);
27+
private static final Logger LOGGER = LogManager.getLogger(MetadataHandler.class);
2728
private final boolean needsType;
2829

2930
public MetadataHandler(@NotNull SessionWrapper wrapper, boolean needsType) {
@@ -74,10 +75,10 @@ public void handleRequest(@NotNull HttpServerExchange exchange, @NotNull Session
7475
}
7576

7677
Utils.internalError(exchange, ex);
77-
LOGGER.error(String.format("Failed handling api request. {type: %s, uri: %s, code: %d}", type, uri, ex.code), ex);
78+
LOGGER.error("Failed handling api request. {type: {}, uri: {}, code: {}}", type, uri, ex.code, ex);
7879
} catch (IOException | MercuryClient.MercuryException ex) {
7980
Utils.internalError(exchange, ex);
80-
LOGGER.error(String.format("Failed handling api request. {type: %s, uri: %s}", type, uri), ex);
81+
LOGGER.error("Failed handling api request. {type: {}, uri: {}}", type, uri, ex);
8182
} catch (IllegalArgumentException ex) {
8283
Utils.invalidParameter(exchange, "uri", "Invalid uri for type: " + type);
8384
}

0 commit comments

Comments
 (0)