Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev'
Browse files Browse the repository at this point in the history
# Conflicts:
#	CHANGELOG.md
  • Loading branch information
freyacodes committed Jun 8, 2019
2 parents b0a0eca + 4535dc5 commit 18ee677
Show file tree
Hide file tree
Showing 16 changed files with 226 additions and 153 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
Each release usually includes various fixes and improvements.
The most noteworthy of these, as well as any features and breaking changes, are listed here.

## v3.2.1

* Update dependencies -- fixes frequent youtube HTTP errors
* Return `FriendlyException` message on `LOAD_FAILED` #174
* Add option to disable `ytsearch` and `scsearch` #194

Contributors:
[@Devoxin](https://github.com/Devoxin),
[@duncte123](https://github.com/duncte123),
[@Frederikam](https://github.com/Frederikam), and
[@napstr](https://github.com/napstr)

## v3.2.0.3
* Add compatibility for Java 8-10

Expand Down
16 changes: 16 additions & 0 deletions IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,22 @@ Additionally, in every `/loadtracks` response, a `loadType` property is returned
* `NO_MATCHES` - Returned if no matches/sources could be found for a given identifier.
* `LOAD_FAILED` - Returned if Lavaplayer failed to load something for some reason.

If the loadType is `LOAD_FAILED`, the response will contain an `exception` object with `message` and `severity` properties.
`message` is a string detailing why the track failed to load, and is okay to display to end-users. Severity represents how common the error is.
A severity level of `COMMON` indicates that the error is non-fatal and that the issue is not from Lavalink itself.

```json
{
"loadType": "LOAD_FAILED",
"playlistInfo": {},
"tracks": [],
"exception": {
"message": "The uploader has not made this video available in your country.",
"severity": "COMMON"
}
}
```

All REST responses from Lavalink include a `Lavalink-Api-Version` header.

### Resuming Lavalink sessions
Expand Down
2 changes: 2 additions & 0 deletions LavalinkServer/application.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ lavalink:
local: false
bufferDurationMs: 400
youtubePlaylistLoadLimit: 6 # Number of pages at 100 each
youtubeSearchEnabled: true
soundcloudSearchEnabled: true
gc-warnings: true

metrics:
Expand Down
7 changes: 3 additions & 4 deletions LavalinkServer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ apply plugin: 'com.gorylenko.gradle-git-properties'
apply plugin: 'org.ajoberstar.grgit'
apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: "com.adarshr.test-logger"

description = 'Play audio to discord voice channels'
mainClassName = "lavalink.server.Launcher"
Expand All @@ -32,10 +33,6 @@ bootRun {
}
}

test {
useJUnitPlatform()
}

dependencies {
compile group: 'club.minnced', name: 'magma', version: magmaVersion
compile group: 'com.sedmelluq', name: 'lavaplayer', version: lavaplayerVersion
Expand All @@ -60,6 +57,8 @@ dependencies {
compile group: 'io.prometheus', name: 'simpleclient_hotspot', version: prometheusVersion
compile group: 'io.prometheus', name: 'simpleclient_logback', version: prometheusVersion
compile group: 'io.prometheus', name: 'simpleclient_servlet', version: prometheusVersion

testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootVersion
}

processResources {
Expand Down
1 change: 1 addition & 0 deletions LavalinkServer/src/main/java/lavalink/server/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.web.servlet.ServletComponentScan;

import java.time.Instant;
import java.time.ZoneId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ public Supplier<AudioPlayerManager> audioPlayerManagerSupplier(AudioSourcesConfi
}

if (sources.isYoutube()) {
YoutubeAudioSourceManager youtube = new YoutubeAudioSourceManager();
YoutubeAudioSourceManager youtube = new YoutubeAudioSourceManager(serverConfig.isYoutubeSearchEnabled());
Integer playlistLoadLimit = serverConfig.getYoutubePlaylistLoadLimit();

if (playlistLoadLimit != null) youtube.setPlaylistPageCount(playlistLoadLimit);
audioPlayerManager.registerSourceManager(youtube);
}
if (sources.isBandcamp()) audioPlayerManager.registerSourceManager(new BandcampAudioSourceManager());
if (sources.isSoundcloud()) audioPlayerManager.registerSourceManager(new SoundCloudAudioSourceManager());
if (sources.isSoundcloud()) audioPlayerManager.registerSourceManager(new SoundCloudAudioSourceManager(serverConfig.isSoundcloudSearchEnabled()));
if (sources.isTwitch()) audioPlayerManager.registerSourceManager(new TwitchStreamAudioSourceManager());
if (sources.isVimeo()) audioPlayerManager.registerSourceManager(new VimeoAudioSourceManager());
if (sources.isMixer()) audioPlayerManager.registerSourceManager(new BeamAudioSourceManager());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package lavalink.server.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Configuration
public class RequestAuthorizationFilter implements HandlerInterceptor, WebMvcConfigurer {

private static final Logger log = LoggerFactory.getLogger(RequestAuthorizationFilter.class);
private ServerConfig serverConfig;
private MetricsPrometheusConfigProperties metricsConfig;

public RequestAuthorizationFilter(ServerConfig serverConfig, MetricsPrometheusConfigProperties metricsConfig) {
this.serverConfig = serverConfig;
this.metricsConfig = metricsConfig;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// Collecting metrics is anonymous
if (!metricsConfig.getEndpoint().isEmpty()
&& request.getServletPath().equals(metricsConfig.getEndpoint())) return true;

if (request.getServletPath().equals("/error")) return true;

String authorization = request.getHeader("Authorization");

if (authorization == null || !authorization.equals(serverConfig.getPassword())) {
String method = request.getMethod();
String path = request.getRequestURI().substring(request.getContextPath().length());
String ip = request.getRemoteAddr();

if (authorization == null) {
log.warn("Authorization missing for {} on {} {}", ip, method, path);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}
log.warn("Authorization failed for {} on {} {}", ip, method, path);
response.setStatus(HttpStatus.FORBIDDEN.value());
return false;
}

return true;
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public class ServerConfig {
@Nullable
private Integer youtubePlaylistLoadLimit;
private boolean gcWarnings = true;
private boolean youtubeSearchEnabled = true;
private boolean soundcloudSearchEnabled = true;

public String getPassword() {
return password;
Expand Down Expand Up @@ -84,4 +86,20 @@ public boolean isGcWarnings() {
public void setGcWarnings(boolean gcWarnings) {
this.gcWarnings = gcWarnings;
}

public boolean isYoutubeSearchEnabled() {
return youtubeSearchEnabled;
}

public void setYoutubeSearchEnabled(boolean youtubeSearchEnabled) {
this.youtubeSearchEnabled = youtubeSearchEnabled;
}

public boolean isSoundcloudSearchEnabled() {
return soundcloudSearchEnabled;
}

public void setSoundcloudSearchEnabled(boolean soundcloudSearchEnabled) {
this.soundcloudSearchEnabled = soundcloudSearchEnabled;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package lavalink.server.info;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* Created by napster on 08.03.19.
*/
@RestController
public class InfoRestHandler {

private final AppInfo appInfo;

public InfoRestHandler(AppInfo appInfo) {
this.appInfo = appInfo;
}

@GetMapping("/version")
public String version() {
return appInfo.getVersionBuild();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,8 @@
public class AudioLoader implements AudioLoadResultHandler {

private static final Logger log = LoggerFactory.getLogger(AudioLoader.class);
private static final LoadResult NO_MATCHES = new LoadResult(Collections.emptyList(),
null, ResultStatus.NO_MATCHES, null);
private static final LoadResult LOAD_FAILED = new LoadResult(Collections.emptyList(),
null, ResultStatus.LOAD_FAILED, null);
private static final LoadResult NO_MATCHES = new LoadResult(ResultStatus.NO_MATCHES, Collections.emptyList(),
null, null);

private final AudioPlayerManager audioPlayerManager;

Expand Down Expand Up @@ -71,7 +69,7 @@ public void trackLoaded(AudioTrack audioTrack) {
log.info("Loaded track " + audioTrack.getInfo().title);
ArrayList<AudioTrack> result = new ArrayList<>();
result.add(audioTrack);
this.loadResult.complete(new LoadResult(result, null, ResultStatus.TRACK_LOADED, null));
this.loadResult.complete(new LoadResult(ResultStatus.TRACK_LOADED, result, null, null));
}

@Override
Expand All @@ -88,7 +86,7 @@ public void playlistLoaded(AudioPlaylist audioPlaylist) {
ResultStatus status = audioPlaylist.isSearchResult() ? ResultStatus.SEARCH_RESULT : ResultStatus.PLAYLIST_LOADED;
List<AudioTrack> loadedItems = audioPlaylist.getTracks();

this.loadResult.complete(new LoadResult(loadedItems, playlistName, status, selectedTrack));
this.loadResult.complete(new LoadResult(status, loadedItems, playlistName, selectedTrack));
}

@Override
Expand All @@ -100,7 +98,7 @@ public void noMatches() {
@Override
public void loadFailed(FriendlyException e) {
log.error("Load failed", e);
this.loadResult.complete(LOAD_FAILED);
this.loadResult.complete(new LoadResult(e));
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

@RestController
Expand All @@ -63,20 +61,6 @@ private void log(HttpServletRequest request) {
log.info("GET " + path);
}

//returns an empty answer if the auth succeeded, or a response to send back immediately
private <T> Optional<ResponseEntity<T>> checkAuthorization(HttpServletRequest request) {
if (request.getHeader("Authorization") == null) {
return Optional.of(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
}

if (!request.getHeader("Authorization").equals(serverConfig.getPassword())) {
log.warn("Authorization failed");
return Optional.of(new ResponseEntity<>(HttpStatus.FORBIDDEN));
}

return Optional.empty();
}

private JSONObject trackToJSON(AudioTrack audioTrack) {
AudioTrackInfo trackInfo = audioTrack.getInfo();

Expand Down Expand Up @@ -116,6 +100,14 @@ private JSONObject encodeLoadResult(LoadResult result) {
json.put("loadType", result.loadResultType);
json.put("tracks", tracks);

if (result.loadResultType == ResultStatus.LOAD_FAILED && result.exception != null) {
JSONObject exception = new JSONObject();
exception.put("message", result.exception.getLocalizedMessage());
exception.put("severity", result.exception.severity.toString());

json.put("exception", exception);
}

return json;
}

Expand All @@ -126,11 +118,6 @@ public CompletionStage<ResponseEntity<String>> getLoadTracks(HttpServletRequest

log(request);

Optional<ResponseEntity<String>> notAuthed = checkAuthorization(request);
if (notAuthed.isPresent()) {
return CompletableFuture.completedFuture(notAuthed.get());
}

return new AudioLoader(audioPlayerManager).load(identifier)
.thenApply(this::encodeLoadResult)
.thenApply(loadResultJson -> new ResponseEntity<>(loadResultJson.toString(), HttpStatus.OK));
Expand All @@ -143,11 +130,6 @@ public ResponseEntity<String> getDecodeTrack(HttpServletRequest request, @Reques

log(request);

Optional<ResponseEntity<String>> notAuthed = checkAuthorization(request);
if (notAuthed.isPresent()) {
return notAuthed.get();
}

AudioTrack audioTrack = Util.toAudioTrack(audioPlayerManager, track);

return new ResponseEntity<>(trackToJSON(audioTrack).toString(), HttpStatus.OK);
Expand All @@ -160,11 +142,6 @@ public ResponseEntity<String> postDecodeTracks(HttpServletRequest request, @Requ

log(request);

Optional<ResponseEntity<String>> notAuthed = checkAuthorization(request);
if (notAuthed.isPresent()) {
return notAuthed.get();
}

JSONArray requestJSON = new JSONArray(body);
JSONArray responseJSON = new JSONArray();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
package lavalink.server.player;

import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;

import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;

class LoadResult {
public ResultStatus loadResultType;
public List<AudioTrack> tracks;
public String playlistName;
public ResultStatus loadResultType;
public Integer selectedTrack;
public FriendlyException exception;

public LoadResult(List<AudioTrack> tracks, @Nullable String playlistName, ResultStatus loadResultType,
@Nullable Integer selectedTrack) {
public LoadResult(ResultStatus loadResultType, List<AudioTrack> tracks,
@Nullable String playlistName, @Nullable Integer selectedTrack) {

this.loadResultType = loadResultType;
this.tracks = Collections.unmodifiableList(tracks);
this.playlistName = playlistName;
this.loadResultType = loadResultType;
this.selectedTrack = selectedTrack;
this.exception = null;
}

public LoadResult(FriendlyException exception) {
this.loadResultType = ResultStatus.LOAD_FAILED;
this.tracks = Collections.emptyList();
this.playlistName = null;
this.selectedTrack = null;
this.exception = exception;
}
}
Loading

0 comments on commit 18ee677

Please sign in to comment.