diff --git a/BUILD.md b/documentation/BUILD.md
similarity index 100%
rename from BUILD.md
rename to documentation/BUILD.md
diff --git a/MIGRATE.md b/documentation/MIGRATE.md
similarity index 100%
rename from MIGRATE.md
rename to documentation/MIGRATE.md
diff --git a/documentation/PROXY.md b/documentation/PROXY.md
new file mode 100644
index 00000000..b922f6a5
--- /dev/null
+++ b/documentation/PROXY.md
@@ -0,0 +1,109 @@
+# Setting up a reverse proxy
+
+A reverse proxy is a public-facing web server sitting in front of an internal
+server such as Libresonic. The Libresonic server never communicates with the
+outside ; instead, the reverse proxy handles all HTTP(S) requests and forwards
+them to Libresonic.
+
+This is useful in many ways, such as gathering all web configuration in the
+same place. It also handles some options (HTTPS) much better than the bundled
+Libresonic server or a servlet container such as Tomcat.
+
+This guide assumes you already have a working Libresonic installation after
+following the [installation guide](documentation/INSTALL.md).
+
+## Getting a TLS certificate
+
+This guide assumes you already have a TLS certificate. [Let's
+Encrypt](https://letsencrypt.org) currently provides such certificates for
+free.
+
+## Libresonic configuration
+
+A few settings can be tweaked in Libresonic's startup script or Tomcat
+configuration.
+
+The reverse proxy will handle HTTPS connections, so there is no need for
+Libresonic to handle them, which is why we set `httpsPort` to 0:
+
+ libresonic.httpsPort=0
+
+Furthermore, the internal Libresonic server should only be accessible from the
+inside of the reverse proxy : we tell Libresonic to listen on the local IP
+only:
+
+ libresonic.host=127.0.0.1
+ libresonic.port=4040
+
+Finally, if Libresonic should be accessible from a subdirectory, the context
+path must be set correctly:
+
+ libresonic.contextPath=/libresonic
+
+## Reverse proxy configuration
+
+### Nginx
+
+The following configuration works for Nginx (HTTPS with HTTP redirection):
+
+```nginx
+# Redirect HTTP to HTTPS
+server {
+ listen 80;
+ server_name example.com;
+ return 301 https://$server_name$request_uri;
+}
+
+server {
+
+ # Setup HTTPS certificates
+ listen 443 default ssl;
+ server_name example.com;
+ ssl_certificate cert.pem;
+ ssl_certificate_key key.pem;
+
+ # Proxy to the Libresonic server
+ location /libresonic {
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto https;
+ proxy_set_header Host $http_host;
+ proxy_max_temp_file_size 0;
+ proxy_pass http://127.0.0.1:4040;
+ proxy_redirect http:// https://;
+ }
+}
+```
+
+### Apache
+
+The following configuration works for Apache (without HTTPS):
+
+```apache
+
+ ServerName example.com
+ ErrorDocument 404 /404.html
+ DocumentRoot /var/www
+ ProxyPass /libresonic http://localhost:4040/libresonic
+ ProxyPassReverse /libresonic http://localhost:4040/libresonic
+
+```
+
+### HAProxy
+
+The following configuration works for HAProxy (HTTPS only):
+
+```haproxy
+frontend https
+ bind $server_public_ip$:443 ssl crt /etc/haproxy/ssl/$server_ssl_keys$.pem
+
+ # Let Libresonic handle all requests under /libresonic
+ acl url_libresonic path_beg -i /libresonic
+ use_backend libresonic-backend if url_libresonic
+
+ # Change default backend to libresonic backend if you don't have a web backend
+ default_backend web-backend
+
+backend libresonic-backend
+ server libresonic 127.0.0.1:4040 check
+```
diff --git a/libresonic-assembly/pom.xml b/libresonic-assembly/pom.xml
index 97ba9468..d77e8e89 100644
--- a/libresonic-assembly/pom.xml
+++ b/libresonic-assembly/pom.xml
@@ -9,7 +9,7 @@
org.libresonic.player
libresonic
- 6.2.spring4-SNAPSHOT
+ 6.2.beta1.spring4
diff --git a/libresonic-booter/pom.xml b/libresonic-booter/pom.xml
index 8c19afde..8831c500 100644
--- a/libresonic-booter/pom.xml
+++ b/libresonic-booter/pom.xml
@@ -8,7 +8,7 @@
org.libresonic.player
libresonic
- 6.2.spring4-SNAPSHOT
+ 6.2.beta1.spring4
diff --git a/libresonic-installer-debian/pom.xml b/libresonic-installer-debian/pom.xml
index 915e2ca5..39baa5f9 100644
--- a/libresonic-installer-debian/pom.xml
+++ b/libresonic-installer-debian/pom.xml
@@ -9,7 +9,7 @@
org.libresonic.player
libresonic
- 6.2.spring4-SNAPSHOT
+ 6.2.beta1.spring4
diff --git a/libresonic-installer-mac/pom.xml b/libresonic-installer-mac/pom.xml
index 7095aa86..a20abb07 100644
--- a/libresonic-installer-mac/pom.xml
+++ b/libresonic-installer-mac/pom.xml
@@ -9,7 +9,7 @@
org.libresonic.player
libresonic
- 6.2.spring4-SNAPSHOT
+ 6.2.beta1.spring4
diff --git a/libresonic-installer-rpm/pom.xml b/libresonic-installer-rpm/pom.xml
index ef68ab1d..794d29b3 100644
--- a/libresonic-installer-rpm/pom.xml
+++ b/libresonic-installer-rpm/pom.xml
@@ -9,7 +9,7 @@
org.libresonic.player
libresonic
- 6.2.spring4-SNAPSHOT
+ 6.2.beta1.spring4
diff --git a/libresonic-installer-windows/pom.xml b/libresonic-installer-windows/pom.xml
index a49724f1..b213a361 100644
--- a/libresonic-installer-windows/pom.xml
+++ b/libresonic-installer-windows/pom.xml
@@ -9,7 +9,7 @@
org.libresonic.player
libresonic
- 6.2.spring4-SNAPSHOT
+ 6.2.beta1.spring4
diff --git a/libresonic-main/pom.xml b/libresonic-main/pom.xml
index df895f37..9a28397e 100644
--- a/libresonic-main/pom.xml
+++ b/libresonic-main/pom.xml
@@ -9,7 +9,7 @@
org.libresonic.player
libresonic
- 6.2.spring4-SNAPSHOT
+ 6.2.beta1.spring4
@@ -136,13 +136,13 @@
org.apache.httpcomponents
httpcore
- 4.2.4
+ 4.4.5
org.apache.httpcomponents
httpclient
- 4.2.4
+ 4.5.2
diff --git a/libresonic-main/src/main/java/org/libresonic/player/ajax/CoverArtService.java b/libresonic-main/src/main/java/org/libresonic/player/ajax/CoverArtService.java
index 9464ecdc..360c668d 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/ajax/CoverArtService.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/ajax/CoverArtService.java
@@ -25,11 +25,11 @@ import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
import org.libresonic.player.Logger;
import org.libresonic.player.domain.MediaFile;
@@ -72,69 +72,69 @@ public class CoverArtService {
private void saveCoverArt(String path, String url) throws Exception {
InputStream input = null;
OutputStream output = null;
- HttpClient client = new DefaultHttpClient();
- try {
- HttpConnectionParams.setConnectionTimeout(client.getParams(), 20 * 1000); // 20 seconds
- HttpConnectionParams.setSoTimeout(client.getParams(), 20 * 1000); // 20 seconds
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(20 * 1000) // 20 seconds
+ .setSocketTimeout(20 * 1000) // 20 seconds
+ .build();
HttpGet method = new HttpGet(url);
+ method.setConfig(requestConfig);
+ try (CloseableHttpResponse response = client.execute(method)) {
+ input = response.getEntity().getContent();
+
+ // Attempt to resolve proper suffix.
+ String suffix = "jpg";
+ if (url.toLowerCase().endsWith(".gif")) {
+ suffix = "gif";
+ } else if (url.toLowerCase().endsWith(".png")) {
+ suffix = "png";
+ }
- HttpResponse response = client.execute(method);
- input = response.getEntity().getContent();
-
- // Attempt to resolve proper suffix.
- String suffix = "jpg";
- if (url.toLowerCase().endsWith(".gif")) {
- suffix = "gif";
- } else if (url.toLowerCase().endsWith(".png")) {
- suffix = "png";
- }
-
- // Check permissions.
- File newCoverFile = new File(path, "cover." + suffix);
- if (!securityService.isWriteAllowed(newCoverFile)) {
- throw new Exception("Permission denied: " + StringUtil.toHtml(newCoverFile.getPath()));
- }
-
- // If file exists, create a backup.
- backup(newCoverFile, new File(path, "cover." + suffix + ".backup"));
-
- // Write file.
- output = new FileOutputStream(newCoverFile);
- IOUtils.copy(input, output);
-
- MediaFile dir = mediaFileService.getMediaFile(path);
-
- // Refresh database.
- mediaFileService.refreshMediaFile(dir);
- dir = mediaFileService.getMediaFile(dir.getId());
+ // Check permissions.
+ File newCoverFile = new File(path, "cover." + suffix);
+ if (!securityService.isWriteAllowed(newCoverFile)) {
+ throw new Exception("Permission denied: " + StringUtil.toHtml(newCoverFile.getPath()));
+ }
- // Rename existing cover files if new cover file is not the preferred.
- try {
- while (true) {
- File coverFile = mediaFileService.getCoverArt(dir);
- if (coverFile != null && !isMediaFile(coverFile) && !newCoverFile.equals(coverFile)) {
- if (!coverFile.renameTo(new File(coverFile.getCanonicalPath() + ".old"))) {
- LOG.warn("Unable to rename old image file " + coverFile);
+ // If file exists, create a backup.
+ backup(newCoverFile, new File(path, "cover." + suffix + ".backup"));
+
+ // Write file.
+ output = new FileOutputStream(newCoverFile);
+ IOUtils.copy(input, output);
+
+ MediaFile dir = mediaFileService.getMediaFile(path);
+
+ // Refresh database.
+ mediaFileService.refreshMediaFile(dir);
+ dir = mediaFileService.getMediaFile(dir.getId());
+
+ // Rename existing cover files if new cover file is not the preferred.
+ try {
+ while (true) {
+ File coverFile = mediaFileService.getCoverArt(dir);
+ if (coverFile != null && !isMediaFile(coverFile) && !newCoverFile.equals(coverFile)) {
+ if (!coverFile.renameTo(new File(coverFile.getCanonicalPath() + ".old"))) {
+ LOG.warn("Unable to rename old image file " + coverFile);
+ break;
+ }
+ LOG.info("Renamed old image file " + coverFile);
+
+ // Must refresh again.
+ mediaFileService.refreshMediaFile(dir);
+ dir = mediaFileService.getMediaFile(dir.getId());
+ } else {
break;
}
- LOG.info("Renamed old image file " + coverFile);
-
- // Must refresh again.
- mediaFileService.refreshMediaFile(dir);
- dir = mediaFileService.getMediaFile(dir.getId());
- } else {
- break;
}
+ } catch (Exception x) {
+ LOG.warn("Failed to rename existing cover file.", x);
}
- } catch (Exception x) {
- LOG.warn("Failed to rename existing cover file.", x);
}
-
} finally {
IOUtils.closeQuietly(input);
IOUtils.closeQuietly(output);
- client.getConnectionManager().shutdown();
}
}
diff --git a/libresonic-main/src/main/java/org/libresonic/player/ajax/LyricsService.java b/libresonic-main/src/main/java/org/libresonic/player/ajax/LyricsService.java
index 9ccc46a6..f2578614 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/ajax/LyricsService.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/ajax/LyricsService.java
@@ -24,12 +24,12 @@ import java.io.StringReader;
import java.net.SocketException;
import org.apache.commons.lang.StringUtils;
-import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
@@ -92,17 +92,15 @@ public class LyricsService {
}
private String executeGetRequest(String url) throws IOException {
- HttpClient client = new DefaultHttpClient();
- HttpConnectionParams.setConnectionTimeout(client.getParams(), 15000);
- HttpConnectionParams.setSoTimeout(client.getParams(), 15000);
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(15000)
+ .setSocketTimeout(15000)
+ .build();
HttpGet method = new HttpGet(url);
- try {
-
+ method.setConfig(requestConfig);
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
ResponseHandler responseHandler = new BasicResponseHandler();
return client.execute(method, responseHandler);
-
- } finally {
- client.getConnectionManager().shutdown();
}
}
}
diff --git a/libresonic-main/src/main/java/org/libresonic/player/ajax/PlayQueueInfo.java b/libresonic-main/src/main/java/org/libresonic/player/ajax/PlayQueueInfo.java
index 8d522cc4..0cb5f541 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/ajax/PlayQueueInfo.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/ajax/PlayQueueInfo.java
@@ -33,15 +33,17 @@ public class PlayQueueInfo {
private final List entries;
private final boolean stopEnabled;
private final boolean repeatEnabled;
+ private final boolean radioEnabled;
private final boolean sendM3U;
private final float gain;
private int startPlayerAt = -1;
private long startPlayerAtPosition; // millis
- public PlayQueueInfo(List entries, boolean stopEnabled, boolean repeatEnabled, boolean sendM3U, float gain) {
+ public PlayQueueInfo(List entries, boolean stopEnabled, boolean repeatEnabled, boolean radioEnabled, boolean sendM3U, float gain) {
this.entries = entries;
this.stopEnabled = stopEnabled;
this.repeatEnabled = repeatEnabled;
+ this.radioEnabled = radioEnabled;
this.sendM3U = sendM3U;
this.gain = gain;
}
@@ -72,6 +74,10 @@ public class PlayQueueInfo {
return repeatEnabled;
}
+ public boolean isRadioEnabled() {
+ return radioEnabled;
+ }
+
public float getGain() {
return gain;
}
@@ -215,4 +221,4 @@ public class PlayQueueInfo {
return remoteCoverArtUrl;
}
}
-}
\ No newline at end of file
+}
diff --git a/libresonic-main/src/main/java/org/libresonic/player/ajax/PlayQueueService.java b/libresonic-main/src/main/java/org/libresonic/player/ajax/PlayQueueService.java
index a2496290..bf8ff160 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/ajax/PlayQueueService.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/ajax/PlayQueueService.java
@@ -148,6 +148,18 @@ public class PlayQueueService {
return convert(request, player, serverSidePlaylist, offset);
}
+ public PlayQueueInfo reloadSearchCriteria() throws Exception {
+ HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
+ HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
+ String username = securityService.getCurrentUsername(request);
+ Player player = getCurrentPlayer(request, response);
+ PlayQueue playQueue = player.getPlayQueue();
+ if (playQueue.getRandomSearchCriteria() != null) {
+ playQueue.addFiles(true, mediaFileService.getRandomSongs(playQueue.getRandomSearchCriteria(), username));
+ }
+ return convert(request, player, false);
+ }
+
public void savePlayQueue(int currentSongIndex, long positionMillis) {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
@@ -581,7 +593,13 @@ public class PlayQueueService {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
- player.getPlayQueue().setRepeatEnabled(!player.getPlayQueue().isRepeatEnabled());
+ PlayQueue playQueue = player.getPlayQueue();
+ if (playQueue.isRadioEnabled()) {
+ playQueue.setRandomSearchCriteria(null);
+ playQueue.setRepeatEnabled(false);
+ } else {
+ playQueue.setRepeatEnabled(!player.getPlayQueue().isRepeatEnabled());
+ }
return convert(request, player, false);
}
@@ -668,7 +686,7 @@ public class PlayQueueService {
}
boolean isStopEnabled = playQueue.getStatus() == PlayQueue.Status.PLAYING && !player.isExternalWithPlaylist();
float gain = jukeboxService.getGain();
- return new PlayQueueInfo(entries, isStopEnabled, playQueue.isRepeatEnabled(), serverSidePlaylist, gain);
+ return new PlayQueueInfo(entries, isStopEnabled, playQueue.isRepeatEnabled(), playQueue.isRadioEnabled(), serverSidePlaylist, gain);
}
private String formatFileSize(Long fileSize, Locale locale) {
@@ -751,4 +769,4 @@ public class PlayQueueService {
public void setPlaylistService(PlaylistService playlistService) {
this.playlistService = playlistService;
}
-}
\ No newline at end of file
+}
diff --git a/libresonic-main/src/main/java/org/libresonic/player/controller/ProxyController.java b/libresonic-main/src/main/java/org/libresonic/player/controller/ProxyController.java
index 06691f1e..552aae50 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/controller/ProxyController.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/controller/ProxyController.java
@@ -25,12 +25,12 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
-import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
-import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
@@ -45,24 +45,26 @@ public class ProxyController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
String url = ServletRequestUtils.getRequiredStringParameter(request, "url");
- HttpClient client = new DefaultHttpClient();
- HttpConnectionParams.setConnectionTimeout(client.getParams(), 15000);
- HttpConnectionParams.setSoTimeout(client.getParams(), 15000);
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(15000)
+ .setSocketTimeout(15000)
+ .build();
HttpGet method = new HttpGet(url);
+ method.setConfig(requestConfig);
InputStream in = null;
- try {
- HttpResponse resp = client.execute(method);
- int statusCode = resp.getStatusLine().getStatusCode();
- if (statusCode != HttpStatus.SC_OK) {
- response.sendError(statusCode);
- } else {
- in = resp.getEntity().getContent();
- IOUtils.copy(in, response.getOutputStream());
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ try (CloseableHttpResponse resp = client.execute(method)) {
+ int statusCode = resp.getStatusLine().getStatusCode();
+ if (statusCode != HttpStatus.SC_OK) {
+ response.sendError(statusCode);
+ } else {
+ in = resp.getEntity().getContent();
+ IOUtils.copy(in, response.getOutputStream());
+ }
}
} finally {
IOUtils.closeQuietly(in);
- client.getConnectionManager().shutdown();
}
return null;
}
diff --git a/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java b/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java
index e741635d..886f278c 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java
@@ -199,7 +199,7 @@ public class RandomPlayQueueController extends ParameterizableViewController {
List musicFolders = getMusicFolders(request);
// Do we add to the current playlist or do we replace it?
- boolean shouldAddToPlayList = ServletRequestUtils.getBooleanParameter(request, "addToPlaylist", false);
+ boolean shouldAddToPlayList = request.getParameter("addToPlaylist") != null;
// Search the database using these criteria
RandomSearchCriteria criteria = new RandomSearchCriteria(
diff --git a/libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema62.java b/libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema62.java
index a8a5fb0c..cdd233c2 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema62.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema62.java
@@ -39,7 +39,7 @@ public class Schema62 extends Schema {
template.execute("insert into version values (27)");
}
- if (!columnExists(template, "player", "m3u_bom_enabled")) {
+ if (!columnExists(template, "m3u_bom_enabled", "player")) {
LOG.info("Database column 'player.m3u_bom_enabled' not found. Creating it.");
template.execute("alter table player add m3u_bom_enabled boolean default false not null");
LOG.info("Database column 'player.m3u_bom_enabled' was added successfully.");
diff --git a/libresonic-main/src/main/java/org/libresonic/player/domain/PlayQueue.java b/libresonic-main/src/main/java/org/libresonic/player/domain/PlayQueue.java
index 2cc2ae42..ef665d19 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/domain/PlayQueue.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/domain/PlayQueue.java
@@ -368,6 +368,15 @@ public class PlayQueue {
this.repeatEnabled = repeatEnabled;
}
+ /**
+ * Returns whether the playlist is a shuffle radio
+ *
+ * @return Whether the playlist is a shuffle radio.
+ */
+ public synchronized boolean isRadioEnabled() {
+ return this.randomSearchCriteria != null;
+ }
+
/**
* Revert the last operation.
*/
@@ -455,4 +464,4 @@ public class PlayQueue {
ARTIST,
ALBUM
}
-}
\ No newline at end of file
+}
diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/AudioScrobblerService.java b/libresonic-main/src/main/java/org/libresonic/player/service/AudioScrobblerService.java
index fcdc76fc..9546ab30 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/service/AudioScrobblerService.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/service/AudioScrobblerService.java
@@ -29,16 +29,16 @@ import java.util.concurrent.LinkedBlockingQueue;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.NameValuePair;
-import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.BasicResponseHandler;
-import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.params.HttpConnectionParams;
import org.libresonic.player.Logger;
import org.libresonic.player.domain.MediaFile;
@@ -62,7 +62,10 @@ public class AudioScrobblerService {
private final LinkedBlockingQueue queue = new LinkedBlockingQueue();
private SettingsService settingsService;
-
+ private final RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(15000)
+ .setSocketTimeout(15000)
+ .build();
/**
* Registers the given media file at www.last.fm. This method returns immediately, the actual registration is done
@@ -234,7 +237,9 @@ public class AudioScrobblerService {
}
private String[] executeGetRequest(String url) throws IOException {
- return executeRequest(new HttpGet(url));
+ HttpGet method = new HttpGet(url);
+ method.setConfig(requestConfig);
+ return executeRequest(method);
}
private String[] executePostRequest(String url, Map parameters) throws IOException {
@@ -245,22 +250,17 @@ public class AudioScrobblerService {
HttpPost request = new HttpPost(url);
request.setEntity(new UrlEncodedFormEntity(params, StringUtil.ENCODING_UTF8));
-
+ request.setConfig(requestConfig);
return executeRequest(request);
}
private String[] executeRequest(HttpUriRequest request) throws IOException {
- HttpClient client = new DefaultHttpClient();
- HttpConnectionParams.setConnectionTimeout(client.getParams(), 15000);
- HttpConnectionParams.setSoTimeout(client.getParams(), 15000);
- try {
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
ResponseHandler responseHandler = new BasicResponseHandler();
String response = client.execute(request, responseHandler);
return response.split("\\n");
- } finally {
- client.getConnectionManager().shutdown();
}
}
diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/NetworkService.java b/libresonic-main/src/main/java/org/libresonic/player/service/NetworkService.java
index 279923c3..1992c0ea 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/service/NetworkService.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/service/NetworkService.java
@@ -28,19 +28,18 @@ import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import org.apache.commons.lang.StringUtils;
-import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
-import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
-import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.params.HttpConnectionParams;
import org.apache.http.util.EntityUtils;
import org.libresonic.player.Logger;
@@ -250,34 +249,32 @@ public class NetworkService {
params.add(new BasicNameValuePair("licenseHolder", settingsService.getLicenseEmail()));
}
- HttpClient client = new DefaultHttpClient();
- try {
+
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
urlRedirectionStatus.setText(enable ? "Registering web address..." : "Unregistering web address...");
request.setEntity(new UrlEncodedFormEntity(params, StringUtil.ENCODING_UTF8));
- HttpResponse response = client.execute(request);
- StatusLine status = response.getStatusLine();
-
- switch (status.getStatusCode()) {
- case HttpStatus.SC_BAD_REQUEST:
- urlRedirectionStatus.setText(EntityUtils.toString(response.getEntity()));
- testUrlRedirection = false;
- break;
- case HttpStatus.SC_OK:
- urlRedirectionStatus.setText(enable ? "Successfully registered web address." : "Web address disabled.");
- break;
- default:
- testUrlRedirection = false;
- throw new IOException(status.getStatusCode() + " " + status.getReasonPhrase());
+ try (CloseableHttpResponse response = client.execute(request)) {
+ StatusLine status = response.getStatusLine();
+
+ switch (status.getStatusCode()) {
+ case HttpStatus.SC_BAD_REQUEST:
+ urlRedirectionStatus.setText(EntityUtils.toString(response.getEntity()));
+ testUrlRedirection = false;
+ break;
+ case HttpStatus.SC_OK:
+ urlRedirectionStatus.setText(enable ? "Successfully registered web address." : "Web address disabled.");
+ break;
+ default:
+ testUrlRedirection = false;
+ throw new IOException(status.getStatusCode() + " " + status.getReasonPhrase());
+ }
}
-
} catch (Throwable x) {
LOG.warn(enable ? "Failed to register web address." : "Failed to unregister web address.", x);
urlRedirectionStatus.setText(enable ? ("Failed to register web address. " + x.getMessage() +
" (" + x.getClass().getSimpleName() + ")") : "Web address disabled.");
- } finally {
- client.getConnectionManager().shutdown();
}
// Test redirection, but only once.
@@ -305,20 +302,19 @@ public class NetworkService {
}
HttpGet request = new HttpGet(url);
- HttpClient client = new DefaultHttpClient();
- HttpConnectionParams.setConnectionTimeout(client.getParams(), 10000);
- HttpConnectionParams.setSoTimeout(client.getParams(), 30000);
- try {
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(15000)
+ .setSocketTimeout(15000)
+ .build();
+ request.setConfig(requestConfig);
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
urlRedirectionStatus.setText("Testing web address " + urlToTest + ". Please wait...");
String response = client.execute(request, new BasicResponseHandler());
urlRedirectionStatus.setText(response);
-
} catch (Throwable x) {
LOG.warn("Failed to test web address.", x);
urlRedirectionStatus.setText("Failed to test web address. " + x.getMessage() + " (" + x.getClass().getSimpleName() + ")");
- } finally {
- client.getConnectionManager().shutdown();
}
}
}
diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/PodcastService.java b/libresonic-main/src/main/java/org/libresonic/player/service/PodcastService.java
index 8754345c..7fd2ab9d 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/service/PodcastService.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/service/PodcastService.java
@@ -39,25 +39,25 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.ContentType;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
import org.libresonic.player.Logger;
import org.libresonic.player.dao.PodcastDao;
import org.libresonic.player.domain.MediaFile;
@@ -302,33 +302,34 @@ public class PodcastService {
@SuppressWarnings({"unchecked"})
private void doRefreshChannel(PodcastChannel channel, boolean downloadEpisodes) {
InputStream in = null;
- HttpClient client = new DefaultHttpClient();
- try {
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
channel.setStatus(PodcastStatus.DOWNLOADING);
channel.setErrorMessage(null);
podcastDao.updateChannel(channel);
-
- HttpConnectionParams.setConnectionTimeout(client.getParams(), 2 * 60 * 1000); // 2 minutes
- HttpConnectionParams.setSoTimeout(client.getParams(), 10 * 60 * 1000); // 10 minutes
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(2 * 60 * 1000) // 2 minutes
+ .setSocketTimeout(10 * 60 * 1000) // 10 minutes
+ .build();
HttpGet method = new HttpGet(channel.getUrl());
+ method.setConfig(requestConfig);
- HttpResponse response = client.execute(method);
- in = response.getEntity().getContent();
-
- Document document = new SAXBuilder().build(in);
- Element channelElement = document.getRootElement().getChild("channel");
+ try (CloseableHttpResponse response = client.execute(method)) {
+ in = response.getEntity().getContent();
- channel.setTitle(StringUtil.removeMarkup(channelElement.getChildTextTrim("title")));
- channel.setDescription(StringUtil.removeMarkup(channelElement.getChildTextTrim("description")));
- channel.setImageUrl(getChannelImageUrl(channelElement));
- channel.setStatus(PodcastStatus.COMPLETED);
- channel.setErrorMessage(null);
- podcastDao.updateChannel(channel);
+ Document document = new SAXBuilder().build(in);
+ Element channelElement = document.getRootElement().getChild("channel");
- downloadImage(channel);
- refreshEpisodes(channel, channelElement.getChildren("item"));
+ channel.setTitle(StringUtil.removeMarkup(channelElement.getChildTextTrim("title")));
+ channel.setDescription(StringUtil.removeMarkup(channelElement.getChildTextTrim("description")));
+ channel.setImageUrl(getChannelImageUrl(channelElement));
+ channel.setStatus(PodcastStatus.COMPLETED);
+ channel.setErrorMessage(null);
+ podcastDao.updateChannel(channel);
+ downloadImage(channel);
+ refreshEpisodes(channel, channelElement.getChildren("item"));
+ }
} catch (Exception x) {
LOG.warn("Failed to get/parse RSS file for Podcast channel " + channel.getUrl(), x);
channel.setStatus(PodcastStatus.ERROR);
@@ -336,7 +337,6 @@ public class PodcastService {
podcastDao.updateChannel(channel);
} finally {
IOUtils.closeQuietly(in);
- client.getConnectionManager().shutdown();
}
if (downloadEpisodes) {
@@ -349,10 +349,9 @@ public class PodcastService {
}
private void downloadImage(PodcastChannel channel) {
- HttpClient client = new DefaultHttpClient();
InputStream in = null;
OutputStream out = null;
- try {
+ try(CloseableHttpClient client = HttpClients.createDefault()) {
String imageUrl = channel.getImageUrl();
if (imageUrl == null) {
return;
@@ -367,17 +366,17 @@ public class PodcastService {
}
HttpGet method = new HttpGet(imageUrl);
- HttpResponse response = client.execute(method);
- in = response.getEntity().getContent();
- out = new FileOutputStream(new File(dir, "cover." + getCoverArtSuffix(response)));
- IOUtils.copy(in, out);
- mediaFileService.refreshMediaFile(channelMediaFile);
+ try (CloseableHttpResponse response = client.execute(method)) {
+ in = response.getEntity().getContent();
+ out = new FileOutputStream(new File(dir, "cover." + getCoverArtSuffix(response)));
+ IOUtils.copy(in, out);
+ mediaFileService.refreshMediaFile(channelMediaFile);
+ }
} catch (Exception x) {
LOG.warn("Failed to download cover art for podcast channel '" + channel.getTitle() + "': " + x, x);
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
- client.getConnectionManager().shutdown();
}
}
@@ -534,68 +533,69 @@ public class PodcastService {
LOG.info("Starting to download Podcast from " + episode.getUrl());
- HttpClient client = new DefaultHttpClient();
- try {
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
if (!settingsService.getLicenseInfo().isLicenseOrTrialValid()) {
throw new Exception("Sorry, the trial period is expired.");
}
PodcastChannel channel = getChannel(episode.getChannelId());
-
- HttpConnectionParams.setConnectionTimeout(client.getParams(), 2 * 60 * 1000); // 2 minutes
- HttpConnectionParams.setSoTimeout(client.getParams(), 10 * 60 * 1000); // 10 minutes
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(2 * 60 * 1000) // 2 minutes
+ .setSocketTimeout(10 * 60 * 1000) // 10 minutes
+ .build();
HttpGet method = new HttpGet(episode.getUrl());
+ method.setConfig(requestConfig);
- HttpResponse response = client.execute(method);
- in = response.getEntity().getContent();
+ try (CloseableHttpResponse response = client.execute(method)) {
+ in = response.getEntity().getContent();
- File file = getFile(channel, episode);
- out = new FileOutputStream(file);
+ File file = getFile(channel, episode);
+ out = new FileOutputStream(file);
- episode.setStatus(PodcastStatus.DOWNLOADING);
- episode.setBytesDownloaded(0L);
- episode.setErrorMessage(null);
- episode.setPath(file.getPath());
- podcastDao.updateEpisode(episode);
+ episode.setStatus(PodcastStatus.DOWNLOADING);
+ episode.setBytesDownloaded(0L);
+ episode.setErrorMessage(null);
+ episode.setPath(file.getPath());
+ podcastDao.updateEpisode(episode);
- byte[] buffer = new byte[4096];
- long bytesDownloaded = 0;
- int n;
- long nextLogCount = 30000L;
+ byte[] buffer = new byte[4096];
+ long bytesDownloaded = 0;
+ int n;
+ long nextLogCount = 30000L;
- while ((n = in.read(buffer)) != -1) {
- out.write(buffer, 0, n);
- bytesDownloaded += n;
+ while ((n = in.read(buffer)) != -1) {
+ out.write(buffer, 0, n);
+ bytesDownloaded += n;
- if (bytesDownloaded > nextLogCount) {
- episode.setBytesDownloaded(bytesDownloaded);
- nextLogCount += 30000L;
+ if (bytesDownloaded > nextLogCount) {
+ episode.setBytesDownloaded(bytesDownloaded);
+ nextLogCount += 30000L;
- // Abort download if episode was deleted by user.
- if (isEpisodeDeleted(episode)) {
- break;
+ // Abort download if episode was deleted by user.
+ if (isEpisodeDeleted(episode)) {
+ break;
+ }
+ podcastDao.updateEpisode(episode);
}
- podcastDao.updateEpisode(episode);
}
- }
- if (isEpisodeDeleted(episode)) {
- LOG.info("Podcast " + episode.getUrl() + " was deleted. Aborting download.");
- IOUtils.closeQuietly(out);
- file.delete();
- } else {
- addMediaFileIdToEpisodes(Arrays.asList(episode));
- episode.setBytesDownloaded(bytesDownloaded);
- podcastDao.updateEpisode(episode);
- LOG.info("Downloaded " + bytesDownloaded + " bytes from Podcast " + episode.getUrl());
- IOUtils.closeQuietly(out);
- updateTags(file, episode);
- episode.setStatus(PodcastStatus.COMPLETED);
- podcastDao.updateEpisode(episode);
- deleteObsoleteEpisodes(channel);
+ if (isEpisodeDeleted(episode)) {
+ LOG.info("Podcast " + episode.getUrl() + " was deleted. Aborting download.");
+ IOUtils.closeQuietly(out);
+ file.delete();
+ } else {
+ addMediaFileIdToEpisodes(Arrays.asList(episode));
+ episode.setBytesDownloaded(bytesDownloaded);
+ podcastDao.updateEpisode(episode);
+ LOG.info("Downloaded " + bytesDownloaded + " bytes from Podcast " + episode.getUrl());
+ IOUtils.closeQuietly(out);
+ updateTags(file, episode);
+ episode.setStatus(PodcastStatus.COMPLETED);
+ podcastDao.updateEpisode(episode);
+ deleteObsoleteEpisodes(channel);
+ }
}
-
} catch (Exception x) {
LOG.warn("Failed to download Podcast from " + episode.getUrl(), x);
episode.setStatus(PodcastStatus.ERROR);
@@ -604,7 +604,6 @@ public class PodcastService {
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
- client.getConnectionManager().shutdown();
}
}
diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/SettingsService.java b/libresonic-main/src/main/java/org/libresonic/player/service/SettingsService.java
index 4ca1eeef..44bcd48d 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/service/SettingsService.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/service/SettingsService.java
@@ -43,12 +43,6 @@ import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.ResponseHandler;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.BasicResponseHandler;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.HttpConnectionParams;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.AvatarDao;
diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/TranscodingService.java b/libresonic-main/src/main/java/org/libresonic/player/service/TranscodingService.java
index 36871490..f28da40f 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/service/TranscodingService.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/service/TranscodingService.java
@@ -329,8 +329,8 @@ public class TranscodingService {
title = "Unknown Artist";
}
- List result = new LinkedList(Arrays.asList(StringUtil.split(command)));
- result.set(0, getTranscodeDirectory().getPath() + File.separatorChar + result.get(0));
+ List result = new LinkedList<>(Arrays.asList(StringUtil.split(command)));
+ result.set(0, getExecutableName(result));
File tmpFile = null;
@@ -381,6 +381,20 @@ public class TranscodingService {
return new TranscodeInputStream(new ProcessBuilder(result), in, tmpFile);
}
+ private String getExecutableName(List transcodeTokens) {
+ String executableName = transcodeTokens.get(0);
+ String transcodeDirectoryPath = getTranscodeDirectory().getPath() + File.separatorChar + executableName;
+ File file = new File(transcodeDirectoryPath);
+ if(file.exists()) {
+ if(!file.canExecute()) {
+ throw new RuntimeException("Transcoder is not executable at " + transcodeDirectoryPath);
+ }
+ return transcodeDirectoryPath;
+ } else {
+ return executableName;
+ }
+ }
+
/**
* Returns an applicable transcoding for the given file and player, or null
if no
* transcoding should be done.
diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/VersionService.java b/libresonic-main/src/main/java/org/libresonic/player/service/VersionService.java
index 25938cdf..c93e117d 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/service/VersionService.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/service/VersionService.java
@@ -19,16 +19,6 @@
*/
package org.libresonic.player.service;
-import org.libresonic.player.Logger;
-import org.libresonic.player.domain.Version;
-import org.apache.commons.io.IOUtils;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.ResponseHandler;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.BasicResponseHandler;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.HttpConnectionParams;
-
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@@ -40,6 +30,17 @@ import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+
+import org.libresonic.player.Logger;
+import org.libresonic.player.domain.Version;
+
/**
* Provides version-related services, including functionality for determining whether a newer
* version of Libresonic is available.
@@ -231,25 +232,22 @@ public class VersionService {
*/
private void readLatestVersion() throws IOException {
- HttpClient client = new DefaultHttpClient();
- HttpConnectionParams.setConnectionTimeout(client.getParams(), 10000);
- HttpConnectionParams.setSoTimeout(client.getParams(), 10000);
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(10000)
+ .setSocketTimeout(10000)
+ .build();
HttpGet method = new HttpGet(VERSION_URL + "?v=" + getLocalVersion());
+ method.setConfig(requestConfig);
String content;
- try {
-
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
ResponseHandler responseHandler = new BasicResponseHandler();
content = client.execute(method, responseHandler);
-
- } finally {
- client.getConnectionManager().shutdown();
}
- BufferedReader reader = new BufferedReader(new StringReader(content));
Pattern finalPattern = Pattern.compile("LIBRESONIC_FULL_VERSION_BEGIN(.*)LIBRESONIC_FULL_VERSION_END");
Pattern betaPattern = Pattern.compile("LIBRESONIC_BETA_VERSION_BEGIN(.*)LIBRESONIC_BETA_VERSION_END");
- try {
+ try (BufferedReader reader = new BufferedReader(new StringReader(content))) {
String line = reader.readLine();
while (line != null) {
Matcher finalMatcher = finalPattern.matcher(line);
@@ -265,8 +263,6 @@ public class VersionService {
line = reader.readLine();
}
- } finally {
- reader.close();
}
}
}
diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/sonos/SonosServiceRegistration.java b/libresonic-main/src/main/java/org/libresonic/player/service/sonos/SonosServiceRegistration.java
index 593652a3..5215073b 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/service/sonos/SonosServiceRegistration.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/service/sonos/SonosServiceRegistration.java
@@ -24,15 +24,15 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.http.NameValuePair;
-import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.BasicResponseHandler;
-import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.params.HttpConnectionParams;
import org.libresonic.player.Logger;
import org.libresonic.player.util.Pair;
@@ -82,24 +82,24 @@ public class SonosServiceRegistration {
for (Pair parameter : parameters) {
params.add(new BasicNameValuePair(parameter.getFirst(), parameter.getSecond()));
}
-
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(20 * 1000) // 20 seconds
+ .setSocketTimeout(20 * 1000) // 20 seconds
+ .build();
HttpPost request = new HttpPost(url);
+ request.setConfig(requestConfig);
request.setEntity(new UrlEncodedFormEntity(params, StringUtil.ENCODING_UTF8));
return executeRequest(request);
}
private String executeRequest(HttpUriRequest request) throws IOException {
- HttpClient client = new DefaultHttpClient();
- HttpConnectionParams.setConnectionTimeout(client.getParams(), 10000);
- HttpConnectionParams.setSoTimeout(client.getParams(), 10000);
- try {
+
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
ResponseHandler responseHandler = new BasicResponseHandler();
return client.execute(request, responseHandler);
- } finally {
- client.getConnectionManager().shutdown();
}
}
}
diff --git a/libresonic-main/src/main/resources/applicationContext-security.xml b/libresonic-main/src/main/resources/applicationContext-security.xml
index 2bb5347a..f1903e76 100644
--- a/libresonic-main/src/main/resources/applicationContext-security.xml
+++ b/libresonic-main/src/main/resources/applicationContext-security.xml
@@ -56,7 +56,11 @@
-
+
+
diff --git a/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties b/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties
index 771acad9..25cff3ab 100644
--- a/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties
+++ b/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties
@@ -100,6 +100,7 @@ playlist.clear = Clear
playlist.shuffle = Shuffle
playlist.repeat_on = Repeat is on
playlist.repeat_off = Repeat is off
+playlist.repeat_radio = Stop shuffle radio
playlist.undo = Undo
playlist.settings = Settings
playlist.more = More actions...
@@ -233,7 +234,8 @@ more.random.text = Shuffle play
more.random.songs = {0} songs
more.random.auto = Play more random songs when end of play queue is reached.
more.random.ok = OK
-more.random.addtoplaylist = Add to current playlist
+more.random.add = Add to queue
+more.random.radio = Shuffle radio
more.random.any = Any
more.random.format = Format
more.random.genre = Genre
diff --git a/libresonic-main/src/main/webapp/WEB-INF/jsp/login.jsp b/libresonic-main/src/main/webapp/WEB-INF/jsp/login.jsp
index 6209b414..4218a07a 100644
--- a/libresonic-main/src/main/webapp/WEB-INF/jsp/login.jsp
+++ b/libresonic-main/src/main/webapp/WEB-INF/jsp/login.jsp
@@ -35,6 +35,10 @@
" tabindex="4"> |
+
+
+
+ |
|
diff --git a/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp b/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp
index 3d708bf5..cc23dd5a 100644
--- a/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp
+++ b/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp
@@ -232,24 +232,13 @@
- |
-
-
- |
- ">
+ ">
+ ">
|
-
-
-
-
-
- |
-
-
diff --git a/libresonic-main/src/main/webapp/WEB-INF/jsp/playQueue.jsp b/libresonic-main/src/main/webapp/WEB-INF/jsp/playQueue.jsp
index c8917a44..aa48c89d 100644
--- a/libresonic-main/src/main/webapp/WEB-INF/jsp/playQueue.jsp
+++ b/libresonic-main/src/main/webapp/WEB-INF/jsp/playQueue.jsp
@@ -37,6 +37,7 @@
var songs = null;
var currentStreamUrl = null;
var repeatEnabled = false;
+ var radioEnabled = false;
var isVisible = ${model.autoHide ? 'false' : 'true'};
var CastPlayer = new CastPlayer();
var ignore = false;
@@ -269,7 +270,13 @@
}
function onNext(wrap) {
var index = parseInt(getCurrentSongIndex()) + 1;
- if (wrap) {
+ if (radioEnabled && index >= songs.length) {
+ playQueueService.reloadSearchCriteria(function(playQueue) {
+ playQueueCallback(playQueue);
+ onSkip(index);
+ });
+ return;
+ } else if (wrap) {
index = index % songs.length;
}
onSkip(index);
@@ -402,14 +409,20 @@
function playQueueCallback(playQueue) {
songs = playQueue.entries;
repeatEnabled = playQueue.repeatEnabled;
+ radioEnabled = playQueue.radioEnabled;
if ($("#start")) {
$("#start").toggle(!playQueue.stopEnabled);
$("#stop").toggle(playQueue.stopEnabled);
}
if ($("#toggleRepeat")) {
- var text = repeatEnabled ? "" : "";
- $("#toggleRepeat").html(text);
+ if (radioEnabled) {
+ $("#toggleRepeat").html("");
+ } else if (repeatEnabled) {
+ $("#toggleRepeat").html("");
+ } else {
+ $("#toggleRepeat").html("");
+ }
}
if (songs.length == 0) {
diff --git a/libresonic-rest-api/pom.xml b/libresonic-rest-api/pom.xml
index 420ba6c7..e95a1a8d 100644
--- a/libresonic-rest-api/pom.xml
+++ b/libresonic-rest-api/pom.xml
@@ -8,7 +8,7 @@
org.libresonic.player
libresonic
- 6.2.spring4-SNAPSHOT
+ 6.2.beta1.spring4
diff --git a/libresonic-sonos-api/pom.xml b/libresonic-sonos-api/pom.xml
index 61daab0a..1012b990 100644
--- a/libresonic-sonos-api/pom.xml
+++ b/libresonic-sonos-api/pom.xml
@@ -8,7 +8,7 @@
org.libresonic.player
libresonic
- 6.2.spring4-SNAPSHOT
+ 6.2.beta1.spring4
diff --git a/pom.xml b/pom.xml
index 3d6bdb47..7d64bd46 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
org.libresonic.player
libresonic
- 6.2.spring4-SNAPSHOT
+ 6.2.beta1.spring4
Libresonic
pom