Merge remote-tracking branch 'upstream/develop' into biconou_develop_PR_SpringBoot

Conflicts:
	libresonic-main/src/main/resources/applicationContext-security.xml
master
Rémi Cocula 8 years ago
commit c0ee1ef786
  1. 0
      documentation/BUILD.md
  2. 0
      documentation/MIGRATE.md
  3. 109
      documentation/PROXY.md
  4. 2
      libresonic-assembly/pom.xml
  5. 2
      libresonic-booter/pom.xml
  6. 2
      libresonic-installer-debian/pom.xml
  7. 2
      libresonic-installer-mac/pom.xml
  8. 2
      libresonic-installer-rpm/pom.xml
  9. 2
      libresonic-installer-windows/pom.xml
  10. 6
      libresonic-main/pom.xml
  11. 110
      libresonic-main/src/main/java/org/libresonic/player/ajax/CoverArtService.java
  12. 20
      libresonic-main/src/main/java/org/libresonic/player/ajax/LyricsService.java
  13. 10
      libresonic-main/src/main/java/org/libresonic/player/ajax/PlayQueueInfo.java
  14. 24
      libresonic-main/src/main/java/org/libresonic/player/ajax/PlayQueueService.java
  15. 34
      libresonic-main/src/main/java/org/libresonic/player/controller/ProxyController.java
  16. 2
      libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java
  17. 2
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema62.java
  18. 11
      libresonic-main/src/main/java/org/libresonic/player/domain/PlayQueue.java
  19. 24
      libresonic-main/src/main/java/org/libresonic/player/service/AudioScrobblerService.java
  20. 58
      libresonic-main/src/main/java/org/libresonic/player/service/NetworkService.java
  21. 157
      libresonic-main/src/main/java/org/libresonic/player/service/PodcastService.java
  22. 6
      libresonic-main/src/main/java/org/libresonic/player/service/SettingsService.java
  23. 18
      libresonic-main/src/main/java/org/libresonic/player/service/TranscodingService.java
  24. 40
      libresonic-main/src/main/java/org/libresonic/player/service/VersionService.java
  25. 20
      libresonic-main/src/main/java/org/libresonic/player/service/sonos/SonosServiceRegistration.java
  26. 6
      libresonic-main/src/main/resources/applicationContext-security.xml
  27. 4
      libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties
  28. 4
      libresonic-main/src/main/webapp/WEB-INF/jsp/login.jsp
  29. 15
      libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp
  30. 19
      libresonic-main/src/main/webapp/WEB-INF/jsp/playQueue.jsp
  31. 2
      libresonic-rest-api/pom.xml
  32. 2
      libresonic-sonos-api/pom.xml
  33. 2
      pom.xml

@ -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
<VirtualHost *:80>
ServerName example.com
ErrorDocument 404 /404.html
DocumentRoot /var/www
ProxyPass /libresonic http://localhost:4040/libresonic
ProxyPassReverse /libresonic http://localhost:4040/libresonic
</VirtualHost>
```
### 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
```

@ -9,7 +9,7 @@
<parent>
<groupId>org.libresonic.player</groupId>
<artifactId>libresonic</artifactId>
<version>6.2.spring4-SNAPSHOT</version>
<version>6.2.beta1.spring4</version>
</parent>
<dependencies>

@ -8,7 +8,7 @@
<parent>
<groupId>org.libresonic.player</groupId>
<artifactId>libresonic</artifactId>
<version>6.2.spring4-SNAPSHOT</version>
<version>6.2.beta1.spring4</version>
</parent>
<dependencies>

@ -9,7 +9,7 @@
<parent>
<groupId>org.libresonic.player</groupId>
<artifactId>libresonic</artifactId>
<version>6.2.spring4-SNAPSHOT</version>
<version>6.2.beta1.spring4</version>
</parent>
<profiles>

@ -9,7 +9,7 @@
<parent>
<groupId>org.libresonic.player</groupId>
<artifactId>libresonic</artifactId>
<version>6.2.spring4-SNAPSHOT</version>
<version>6.2.beta1.spring4</version>
</parent>
<dependencies>

@ -9,7 +9,7 @@
<parent>
<groupId>org.libresonic.player</groupId>
<artifactId>libresonic</artifactId>
<version>6.2.spring4-SNAPSHOT</version>
<version>6.2.beta1.spring4</version>
</parent>
<profiles>

@ -9,7 +9,7 @@
<parent>
<groupId>org.libresonic.player</groupId>
<artifactId>libresonic</artifactId>
<version>6.2.spring4-SNAPSHOT</version>
<version>6.2.beta1.spring4</version>
</parent>
<properties>

@ -9,7 +9,7 @@
<parent>
<groupId>org.libresonic.player</groupId>
<artifactId>libresonic</artifactId>
<version>6.2.spring4-SNAPSHOT</version>
<version>6.2.beta1.spring4</version>
</parent>
<properties>
@ -136,13 +136,13 @@
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.2.4</version>
<version>4.4.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2.4</version>
<version>4.5.2</version>
</dependency>
<dependency>

@ -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();
}
}

@ -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<String> responseHandler = new BasicResponseHandler();
return client.execute(method, responseHandler);
} finally {
client.getConnectionManager().shutdown();
}
}
}

@ -33,15 +33,17 @@ public class PlayQueueInfo {
private final List<Entry> 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<Entry> entries, boolean stopEnabled, boolean repeatEnabled, boolean sendM3U, float gain) {
public PlayQueueInfo(List<Entry> 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;
}
}
}
}

@ -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;
}
}
}

@ -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;
}

@ -199,7 +199,7 @@ public class RandomPlayQueueController extends ParameterizableViewController {
List<MusicFolder> 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(

@ -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.");

@ -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
}
}
}

@ -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<RegistrationData> queue = new LinkedBlockingQueue<RegistrationData>();
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<String, String> 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<String> responseHandler = new BasicResponseHandler();
String response = client.execute(request, responseHandler);
return response.split("\\n");
} finally {
client.getConnectionManager().shutdown();
}
}

@ -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();
}
}
}

@ -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();
}
}

@ -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;

@ -329,8 +329,8 @@ public class TranscodingService {
title = "Unknown Artist";
}
List<String> result = new LinkedList<String>(Arrays.asList(StringUtil.split(command)));
result.set(0, getTranscodeDirectory().getPath() + File.separatorChar + result.get(0));
List<String> 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<String> 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 <code>null</code> if no
* transcoding should be done.

@ -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<String> 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();
}
}
}

@ -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<String, String> 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<String> responseHandler = new BasicResponseHandler();
return client.execute(request, responseHandler);
} finally {
client.getConnectionManager().shutdown();
}
}
}

@ -56,7 +56,11 @@
<!-- ROLE_USER -->
<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<security:form-login login-page="/login.view" default-target-url="/home.view" authentication-failure-url="/login.view?error=1"/>
<security:form-login login-page="/login.view"
default-target-url="/index.view"
authentication-failure-url="/login.view?error=1"
always-use-default-target="true"/>
<security:remember-me user-service-ref="securityService" key="libresonic"/>
</security:http>
<security:authentication-manager>

@ -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

@ -35,6 +35,10 @@
<tr>
<td align="left"><input name="submit" type="submit" value="<fmt:message key="login.login"/>" tabindex="4"></td>
<td align="left" class="detail">
<input type="checkbox" name="_spring_security_remember_me" id="remember" class="checkbox" tabindex="3">
<label for="remember"><fmt:message key="login.remember"/></label>
</td>
</tr>
<tr>
<td></td>

@ -232,24 +232,13 @@
<option value="mp3">MP3</option>
</select>
</td>
<td><fmt:message key="more.random.addtoplaylist"/></td>
<td>
<input name="addToPlaylist" value="true" type="checkbox"/>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="<fmt:message key="more.random.ok"/>">
<input type="submit" name="addToPlaylist" value="<fmt:message key="more.random.add"/>">
<input type="submit" name="autoRandom" value="<fmt:message key="more.random.radio"/>">
</td>
</tr>
<c:if test="${not model.clientSidePlaylist}">
<tr>
<td colspan="9">
<input type="checkbox" name="autoRandom" id="autoRandom" class="checkbox"/>
<label for="autoRandom"><fmt:message key="more.random.auto"/></label>
</td>
</tr>
</c:if>
</table>
</form>
</c:if>

@ -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 ? "<fmt:message key="playlist.repeat_on"/>" : "<fmt:message key="playlist.repeat_off"/>";
$("#toggleRepeat").html(text);
if (radioEnabled) {
$("#toggleRepeat").html("<fmt:message key="playlist.repeat_radio"/>");
} else if (repeatEnabled) {
$("#toggleRepeat").html("<fmt:message key="playlist.repeat_on"/>");
} else {
$("#toggleRepeat").html("<fmt:message key="playlist.repeat_off"/>");
}
}
if (songs.length == 0) {

@ -8,7 +8,7 @@
<parent>
<groupId>org.libresonic.player</groupId>
<artifactId>libresonic</artifactId>
<version>6.2.spring4-SNAPSHOT</version>
<version>6.2.beta1.spring4</version>
</parent>
<build>

@ -8,7 +8,7 @@
<parent>
<groupId>org.libresonic.player</groupId>
<artifactId>libresonic</artifactId>
<version>6.2.spring4-SNAPSHOT</version>
<version>6.2.beta1.spring4</version>
</parent>
<build>

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.libresonic.player</groupId>
<artifactId>libresonic</artifactId>
<version>6.2.spring4-SNAPSHOT</version>
<version>6.2.beta1.spring4</version>
<name>Libresonic</name>
<packaging>pom</packaging>
<organization>

Loading…
Cancel
Save