REST api tweaks
- Moved bookmark caching logic into service layer - Removed returning "null" when writing to the response directly - Finish renaming to subsonic rest controller Signed-off-by: Andrew DeMaria <lostonamountain@gmail.com>
This commit is contained in:
@@ -28,7 +28,6 @@ import org.springframework.stereotype.Controller;
|
|||||||
import org.springframework.web.bind.ServletRequestUtils;
|
import org.springframework.web.bind.ServletRequestUtils;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
|
||||||
import org.springframework.web.servlet.mvc.LastModified;
|
import org.springframework.web.servlet.mvc.LastModified;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@@ -60,17 +59,16 @@ public class AvatarController implements LastModified {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET)
|
@RequestMapping(method = RequestMethod.GET)
|
||||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
Avatar avatar = getAvatar(request);
|
Avatar avatar = getAvatar(request);
|
||||||
|
|
||||||
if (avatar == null) {
|
if (avatar == null) {
|
||||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setContentType(avatar.getMimeType());
|
response.setContentType(avatar.getMimeType());
|
||||||
response.getOutputStream().write(avatar.getData());
|
response.getOutputStream().write(avatar.getData());
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Avatar getAvatar(HttpServletRequest request) {
|
private Avatar getAvatar(HttpServletRequest request) {
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import org.springframework.stereotype.Controller;
|
|||||||
import org.springframework.web.bind.ServletRequestUtils;
|
import org.springframework.web.bind.ServletRequestUtils;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
|
||||||
import org.springframework.web.servlet.mvc.LastModified;
|
import org.springframework.web.servlet.mvc.LastModified;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
@@ -98,7 +97,7 @@ public class CoverArtController implements LastModified {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET)
|
@RequestMapping(method = RequestMethod.GET)
|
||||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
|
|
||||||
CoverArtRequest coverArtRequest = createCoverArtRequest(request);
|
CoverArtRequest coverArtRequest = createCoverArtRequest(request);
|
||||||
// LOG.info("handleRequest - " + coverArtRequest);
|
// LOG.info("handleRequest - " + coverArtRequest);
|
||||||
@@ -107,14 +106,14 @@ public class CoverArtController implements LastModified {
|
|||||||
// Send fallback image if no ID is given. (No need to cache it, since it will be cached in browser.)
|
// Send fallback image if no ID is given. (No need to cache it, since it will be cached in browser.)
|
||||||
if (coverArtRequest == null) {
|
if (coverArtRequest == null) {
|
||||||
sendFallback(size, response);
|
sendFallback(size, response);
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimize if no scaling is required.
|
// Optimize if no scaling is required.
|
||||||
if (size == null && coverArtRequest.getCoverArt() != null) {
|
if (size == null && coverArtRequest.getCoverArt() != null) {
|
||||||
// LOG.info("sendUnscaled - " + coverArtRequest);
|
// LOG.info("sendUnscaled - " + coverArtRequest);
|
||||||
sendUnscaled(coverArtRequest, response);
|
sendUnscaled(coverArtRequest, response);
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send cached image, creating it if necessary.
|
// Send cached image, creating it if necessary.
|
||||||
@@ -128,7 +127,6 @@ public class CoverArtController implements LastModified {
|
|||||||
sendFallback(size, response);
|
sendFallback(size, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CoverArtRequest createCoverArtRequest(HttpServletRequest request) {
|
private CoverArtRequest createCoverArtRequest(HttpServletRequest request) {
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import org.springframework.web.bind.ServletRequestBindingException;
|
|||||||
import org.springframework.web.bind.ServletRequestUtils;
|
import org.springframework.web.bind.ServletRequestUtils;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
|
||||||
import org.springframework.web.servlet.mvc.LastModified;
|
import org.springframework.web.servlet.mvc.LastModified;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@@ -89,7 +88,7 @@ public class DownloadController implements LastModified {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET)
|
@RequestMapping(method = RequestMethod.GET)
|
||||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
|
|
||||||
User user = securityService.getCurrentUser(request);
|
User user = securityService.getCurrentUser(request);
|
||||||
TransferStatus status = null;
|
TransferStatus status = null;
|
||||||
@@ -118,7 +117,7 @@ public class DownloadController implements LastModified {
|
|||||||
if (!securityService.isFolderAccessAllowed(mediaFile, user.getUsername())) {
|
if (!securityService.isFolderAccessAllowed(mediaFile, user.getUsername())) {
|
||||||
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||||
"Access to file " + mediaFile.getId() + " is forbidden for user " + user.getUsername());
|
"Access to file " + mediaFile.getId() + " is forbidden for user " + user.getUsername());
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mediaFile.isFile()) {
|
if (mediaFile.isFile()) {
|
||||||
@@ -148,8 +147,6 @@ public class DownloadController implements LastModified {
|
|||||||
securityService.updateUserByteCounts(user, 0L, status.getBytesTransfered(), 0L);
|
securityService.updateUserByteCounts(user, 0L, status.getBytesTransfered(), 0L);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaFile getMediaFile(HttpServletRequest request) throws ServletRequestBindingException {
|
private MediaFile getMediaFile(HttpServletRequest request) throws ServletRequestBindingException {
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import org.springframework.stereotype.Controller;
|
|||||||
import org.springframework.web.bind.ServletRequestUtils;
|
import org.springframework.web.bind.ServletRequestUtils;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@@ -68,7 +67,7 @@ public class HLSController {
|
|||||||
private JWTSecurityService jwtSecurityService;
|
private JWTSecurityService jwtSecurityService;
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET)
|
@RequestMapping(method = RequestMethod.GET)
|
||||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
|
|
||||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
@@ -79,19 +78,19 @@ public class HLSController {
|
|||||||
|
|
||||||
if (mediaFile == null) {
|
if (mediaFile == null) {
|
||||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Media file not found: " + id);
|
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Media file not found: " + id);
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (username != null && !securityService.isFolderAccessAllowed(mediaFile, username)) {
|
if (username != null && !securityService.isFolderAccessAllowed(mediaFile, username)) {
|
||||||
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||||
"Access to file " + mediaFile.getId() + " is forbidden for user " + username);
|
"Access to file " + mediaFile.getId() + " is forbidden for user " + username);
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer duration = mediaFile.getDurationSeconds();
|
Integer duration = mediaFile.getDurationSeconds();
|
||||||
if (duration == null || duration == 0) {
|
if (duration == null || duration == 0) {
|
||||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unknown duration for media file: " + id);
|
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unknown duration for media file: " + id);
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setContentType("application/vnd.apple.mpegurl");
|
response.setContentType("application/vnd.apple.mpegurl");
|
||||||
@@ -104,7 +103,7 @@ public class HLSController {
|
|||||||
generateNormalPlaylist(request, id, player, bitRates.size() == 1 ? bitRates.get(0) : null, duration, writer);
|
generateNormalPlaylist(request, id, player, bitRates.size() == 1 ? bitRates.get(0) : null, duration, writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Pair<Integer, Dimension>> parseBitRates(HttpServletRequest request) throws IllegalArgumentException {
|
private List<Pair<Integer, Dimension>> parseBitRates(HttpServletRequest request) throws IllegalArgumentException {
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ public class JAXBWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeErrorResponse(HttpServletRequest request, HttpServletResponse response,
|
public void writeErrorResponse(HttpServletRequest request, HttpServletResponse response,
|
||||||
RESTController.ErrorCode code, String message) throws Exception {
|
SubsonicRESTController.ErrorCode code, String message) throws Exception {
|
||||||
Response res = createResponse(false);
|
Response res = createResponse(false);
|
||||||
Error error = new Error();
|
Error error = new Error();
|
||||||
res.setError(error);
|
res.setError(error);
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package org.airsonic.player.controller;
|
package org.airsonic.player.controller;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.HttpStatus;
|
|
||||||
import org.apache.http.client.config.RequestConfig;
|
import org.apache.http.client.config.RequestConfig;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
@@ -37,6 +36,8 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static org.springframework.http.HttpStatus.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A proxy for external HTTP requests.
|
* A proxy for external HTTP requests.
|
||||||
*
|
*
|
||||||
@@ -61,7 +62,7 @@ public class ProxyController {
|
|||||||
try (CloseableHttpClient client = HttpClients.createDefault()) {
|
try (CloseableHttpClient client = HttpClients.createDefault()) {
|
||||||
try (CloseableHttpResponse resp = client.execute(method)) {
|
try (CloseableHttpResponse resp = client.execute(method)) {
|
||||||
int statusCode = resp.getStatusLine().getStatusCode();
|
int statusCode = resp.getStatusLine().getStatusCode();
|
||||||
if (statusCode != HttpStatus.SC_OK) {
|
if (statusCode != OK.value()) {
|
||||||
response.sendError(statusCode);
|
response.sendError(statusCode);
|
||||||
} else {
|
} else {
|
||||||
in = resp.getEntity().getContent();
|
in = resp.getEntity().getContent();
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import org.springframework.web.bind.ServletRequestBindingException;
|
|||||||
import org.springframework.web.bind.ServletRequestUtils;
|
import org.springframework.web.bind.ServletRequestUtils;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
@@ -84,7 +83,7 @@ public class StreamController {
|
|||||||
private SearchService searchService;
|
private SearchService searchService;
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET)
|
@RequestMapping(method = RequestMethod.GET)
|
||||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
|
|
||||||
TransferStatus status = null;
|
TransferStatus status = null;
|
||||||
PlayQueueInputStream in = null;
|
PlayQueueInputStream in = null;
|
||||||
@@ -96,7 +95,7 @@ public class StreamController {
|
|||||||
|
|
||||||
if (!(authentication instanceof JWTAuthenticationToken) && !user.isStreamRole()) {
|
if (!(authentication instanceof JWTAuthenticationToken) && !user.isStreamRole()) {
|
||||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Streaming is forbidden for user " + user.getUsername());
|
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Streaming is forbidden for user " + user.getUsername());
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If "playlist" request parameter is set, this is a Podcast request. In that case, create a separate
|
// If "playlist" request parameter is set, this is a Podcast request. In that case, create a separate
|
||||||
@@ -136,7 +135,7 @@ public class StreamController {
|
|||||||
if (!(authentication instanceof JWTAuthenticationToken) && !securityService.isFolderAccessAllowed(file, user.getUsername())) {
|
if (!(authentication instanceof JWTAuthenticationToken) && !securityService.isFolderAccessAllowed(file, user.getUsername())) {
|
||||||
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||||
"Access to file " + file.getId() + " is forbidden for user " + user.getUsername());
|
"Access to file " + file.getId() + " is forbidden for user " + user.getUsername());
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the index of the currently playing media file. At
|
// Update the index of the currently playing media file. At
|
||||||
@@ -189,7 +188,7 @@ public class StreamController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (request.getMethod().equals("HEAD")) {
|
if (request.getMethod().equals("HEAD")) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Terminate any other streams to this player.
|
// Terminate any other streams to this player.
|
||||||
@@ -226,7 +225,7 @@ public class StreamController {
|
|||||||
|
|
||||||
// Check if stream has been terminated.
|
// Check if stream has been terminated.
|
||||||
if (status.terminated()) {
|
if (status.terminated()) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player.getPlayQueue().getStatus() == PlayQueue.Status.STOPPED) {
|
if (player.getPlayQueue().getStatus() == PlayQueue.Status.STOPPED) {
|
||||||
@@ -257,7 +256,7 @@ public class StreamController {
|
|||||||
}
|
}
|
||||||
IOUtils.closeQuietly(in);
|
IOUtils.closeQuietly(in);
|
||||||
}
|
}
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setContentDuration(HttpServletResponse response, MediaFile file) {
|
private void setContentDuration(HttpServletResponse response, MediaFile file) {
|
||||||
|
|||||||
+28
-41
@@ -23,8 +23,12 @@ import org.airsonic.player.ajax.LyricsInfo;
|
|||||||
import org.airsonic.player.ajax.LyricsService;
|
import org.airsonic.player.ajax.LyricsService;
|
||||||
import org.airsonic.player.ajax.PlayQueueService;
|
import org.airsonic.player.ajax.PlayQueueService;
|
||||||
import org.airsonic.player.command.UserSettingsCommand;
|
import org.airsonic.player.command.UserSettingsCommand;
|
||||||
import org.airsonic.player.dao.*;
|
import org.airsonic.player.dao.AlbumDao;
|
||||||
|
import org.airsonic.player.dao.ArtistDao;
|
||||||
|
import org.airsonic.player.dao.MediaFileDao;
|
||||||
|
import org.airsonic.player.dao.PlayQueueDao;
|
||||||
import org.airsonic.player.domain.*;
|
import org.airsonic.player.domain.*;
|
||||||
|
import org.airsonic.player.domain.Bookmark;
|
||||||
import org.airsonic.player.service.*;
|
import org.airsonic.player.service.*;
|
||||||
import org.airsonic.player.util.Pair;
|
import org.airsonic.player.util.Pair;
|
||||||
import org.airsonic.player.util.StringUtil;
|
import org.airsonic.player.util.StringUtil;
|
||||||
@@ -43,7 +47,6 @@ import org.springframework.web.servlet.ModelAndView;
|
|||||||
import org.subsonic.restapi.*;
|
import org.subsonic.restapi.*;
|
||||||
import org.subsonic.restapi.PodcastStatus;
|
import org.subsonic.restapi.PodcastStatus;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletRequestWrapper;
|
import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
@@ -65,9 +68,9 @@ import static org.springframework.web.bind.ServletRequestUtils.*;
|
|||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping(value = "/rest", method = {RequestMethod.GET, RequestMethod.POST})
|
@RequestMapping(value = "/rest", method = {RequestMethod.GET, RequestMethod.POST})
|
||||||
public class RESTController {
|
public class SubsonicRESTController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(RESTController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SubsonicRESTController.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SettingsService settingsService;
|
private SettingsService settingsService;
|
||||||
@@ -124,7 +127,7 @@ public class RESTController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private AlbumDao albumDao;
|
private AlbumDao albumDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
private BookmarkDao bookmarkDao;
|
private BookmarkService bookmarkService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private PlayQueueDao playQueueDao;
|
private PlayQueueDao playQueueDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -136,18 +139,6 @@ public class RESTController {
|
|||||||
private static final String NOT_YET_IMPLEMENTED = "Not yet implemented";
|
private static final String NOT_YET_IMPLEMENTED = "Not yet implemented";
|
||||||
private static final String NO_LONGER_SUPPORTED = "No longer supported";
|
private static final String NO_LONGER_SUPPORTED = "No longer supported";
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void init() {
|
|
||||||
refreshBookmarkCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshBookmarkCache() {
|
|
||||||
bookmarkCache.clear();
|
|
||||||
for (org.airsonic.player.domain.Bookmark bookmark : bookmarkDao.getBookmarks()) {
|
|
||||||
bookmarkCache.put(BookmarkKey.forBookmark(bookmark), bookmark);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(value = "/ping")
|
@RequestMapping(value = "/ping")
|
||||||
public void ping(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public void ping(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
Response res = createResponse();
|
Response res = createResponse();
|
||||||
@@ -1337,12 +1328,12 @@ public class RESTController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/download")
|
@RequestMapping(value = "/download")
|
||||||
public ModelAndView download(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public void download(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
request = wrapRequest(request);
|
request = wrapRequest(request);
|
||||||
org.airsonic.player.domain.User user = securityService.getCurrentUser(request);
|
org.airsonic.player.domain.User user = securityService.getCurrentUser(request);
|
||||||
if (!user.isDownloadRole()) {
|
if (!user.isDownloadRole()) {
|
||||||
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to download files.");
|
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to download files.");
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
|
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
|
||||||
@@ -1350,49 +1341,47 @@ public class RESTController {
|
|||||||
|
|
||||||
if (ifModifiedSince != -1 && lastModified != -1 && lastModified <= ifModifiedSince) {
|
if (ifModifiedSince != -1 && lastModified != -1 && lastModified <= ifModifiedSince) {
|
||||||
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
|
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastModified != -1) {
|
if (lastModified != -1) {
|
||||||
response.setDateHeader("Last-Modified", lastModified);
|
response.setDateHeader("Last-Modified", lastModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
return downloadController.handleRequest(request, response);
|
downloadController.handleRequest(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/stream")
|
@RequestMapping(value = "/stream")
|
||||||
public ModelAndView stream(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public void stream(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
request = wrapRequest(request);
|
request = wrapRequest(request);
|
||||||
org.airsonic.player.domain.User user = securityService.getCurrentUser(request);
|
org.airsonic.player.domain.User user = securityService.getCurrentUser(request);
|
||||||
if (!user.isStreamRole()) {
|
if (!user.isStreamRole()) {
|
||||||
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to play files.");
|
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to play files.");
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
streamController.handleRequest(request, response);
|
streamController.handleRequest(request, response);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/hls")
|
@RequestMapping(value = "/hls")
|
||||||
public ModelAndView hls(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public void hls(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
request = wrapRequest(request);
|
request = wrapRequest(request);
|
||||||
org.airsonic.player.domain.User user = securityService.getCurrentUser(request);
|
org.airsonic.player.domain.User user = securityService.getCurrentUser(request);
|
||||||
if (!user.isStreamRole()) {
|
if (!user.isStreamRole()) {
|
||||||
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to play files.");
|
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to play files.");
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
int id = getRequiredIntParameter(request, "id");
|
int id = getRequiredIntParameter(request, "id");
|
||||||
MediaFile video = mediaFileDao.getMediaFile(id);
|
MediaFile video = mediaFileDao.getMediaFile(id);
|
||||||
if (video == null || video.isDirectory()) {
|
if (video == null || video.isDirectory()) {
|
||||||
error(request, response, ErrorCode.NOT_FOUND, "Video not found.");
|
error(request, response, ErrorCode.NOT_FOUND, "Video not found.");
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
if (!securityService.isFolderAccessAllowed(video, user.getUsername())) {
|
if (!securityService.isFolderAccessAllowed(video, user.getUsername())) {
|
||||||
error(request, response, ErrorCode.NOT_AUTHORIZED, "Access denied");
|
error(request, response, ErrorCode.NOT_AUTHORIZED, "Access denied");
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
hlsController.handleRequest(request, response);
|
hlsController.handleRequest(request, response);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/scrobble")
|
@RequestMapping(value = "/scrobble")
|
||||||
@@ -1702,7 +1691,7 @@ public class RESTController {
|
|||||||
String username = securityService.getCurrentUsername(request);
|
String username = securityService.getCurrentUsername(request);
|
||||||
|
|
||||||
Bookmarks result = new Bookmarks();
|
Bookmarks result = new Bookmarks();
|
||||||
for (org.airsonic.player.domain.Bookmark bookmark : bookmarkDao.getBookmarks(username)) {
|
for (Bookmark bookmark : bookmarkService.getBookmarks(username)) {
|
||||||
org.subsonic.restapi.Bookmark b = new org.subsonic.restapi.Bookmark();
|
org.subsonic.restapi.Bookmark b = new org.subsonic.restapi.Bookmark();
|
||||||
result.getBookmark().add(b);
|
result.getBookmark().add(b);
|
||||||
b.setPosition(bookmark.getPositionMillis());
|
b.setPosition(bookmark.getPositionMillis());
|
||||||
@@ -1729,9 +1718,8 @@ public class RESTController {
|
|||||||
String comment = request.getParameter("comment");
|
String comment = request.getParameter("comment");
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
|
|
||||||
org.airsonic.player.domain.Bookmark bookmark = new org.airsonic.player.domain.Bookmark(0, mediaFileId, position, username, comment, now, now);
|
Bookmark bookmark = new Bookmark(0, mediaFileId, position, username, comment, now, now);
|
||||||
bookmarkDao.createOrUpdateBookmark(bookmark);
|
bookmarkService.createOrUpdateBookmark(bookmark);
|
||||||
refreshBookmarkCache();
|
|
||||||
writeEmptyResponse(request, response);
|
writeEmptyResponse(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1741,8 +1729,7 @@ public class RESTController {
|
|||||||
|
|
||||||
String username = securityService.getCurrentUsername(request);
|
String username = securityService.getCurrentUsername(request);
|
||||||
int mediaFileId = getRequiredIntParameter(request, "id");
|
int mediaFileId = getRequiredIntParameter(request, "id");
|
||||||
bookmarkDao.deleteBookmark(username, mediaFileId);
|
bookmarkService.deleteBookmark(username, mediaFileId);
|
||||||
refreshBookmarkCache();
|
|
||||||
|
|
||||||
writeEmptyResponse(request, response);
|
writeEmptyResponse(request, response);
|
||||||
}
|
}
|
||||||
@@ -1954,15 +1941,15 @@ public class RESTController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/getCoverArt")
|
@RequestMapping(value = "/getCoverArt")
|
||||||
public ModelAndView getCoverArt(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public void getCoverArt(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
request = wrapRequest(request);
|
request = wrapRequest(request);
|
||||||
return coverArtController.handleRequest(request, response);
|
coverArtController.handleRequest(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/getAvatar")
|
@RequestMapping(value = "/getAvatar")
|
||||||
public ModelAndView getAvatar(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public void getAvatar(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
request = wrapRequest(request);
|
request = wrapRequest(request);
|
||||||
return avatarController.handleRequest(request, response);
|
avatarController.handleRequest(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/changePassword")
|
@RequestMapping(value = "/changePassword")
|
||||||
@@ -2237,7 +2224,7 @@ public class RESTController {
|
|||||||
|
|
||||||
MediaFile mediaFile = this.mediaFileService.getMediaFile(id);
|
MediaFile mediaFile = this.mediaFileService.getMediaFile(id);
|
||||||
if (mediaFile == null) {
|
if (mediaFile == null) {
|
||||||
error(request, response, RESTController.ErrorCode.NOT_FOUND, "Media file not found.");
|
error(request, response, SubsonicRESTController.ErrorCode.NOT_FOUND, "Media file not found.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AlbumNotes albumNotes = this.lastFmService.getAlbumNotes(mediaFile);
|
AlbumNotes albumNotes = this.lastFmService.getAlbumNotes(mediaFile);
|
||||||
@@ -2256,7 +2243,7 @@ public class RESTController {
|
|||||||
|
|
||||||
Album album = this.albumDao.getAlbum(id);
|
Album album = this.albumDao.getAlbum(id);
|
||||||
if (album == null) {
|
if (album == null) {
|
||||||
error(request, response, RESTController.ErrorCode.NOT_FOUND, "Album not found.");
|
error(request, response, SubsonicRESTController.ErrorCode.NOT_FOUND, "Album not found.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AlbumNotes albumNotes = this.lastFmService.getAlbumNotes(album);
|
AlbumNotes albumNotes = this.lastFmService.getAlbumNotes(album);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
package org.airsonic.player.filter;
|
package org.airsonic.player.filter;
|
||||||
|
|
||||||
import org.airsonic.player.controller.JAXBWriter;
|
import org.airsonic.player.controller.JAXBWriter;
|
||||||
import org.airsonic.player.controller.RESTController;
|
import org.airsonic.player.controller.SubsonicRESTController;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.web.bind.ServletRequestBindingException;
|
import org.springframework.web.bind.ServletRequestBindingException;
|
||||||
@@ -61,7 +61,7 @@ public class RESTFilter implements Filter {
|
|||||||
x = x.getCause();
|
x = x.getCause();
|
||||||
}
|
}
|
||||||
|
|
||||||
RESTController.ErrorCode code = (x instanceof ServletRequestBindingException) ? RESTController.ErrorCode.MISSING_PARAMETER : RESTController.ErrorCode.GENERIC;
|
SubsonicRESTController.ErrorCode code = (x instanceof ServletRequestBindingException) ? SubsonicRESTController.ErrorCode.MISSING_PARAMETER : SubsonicRESTController.ErrorCode.GENERIC;
|
||||||
String msg = getErrorMessage(x);
|
String msg = getErrorMessage(x);
|
||||||
LOG.warn("Error in REST API: " + msg, x);
|
LOG.warn("Error in REST API: " + msg, x);
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -20,7 +20,7 @@
|
|||||||
package org.airsonic.player.security;
|
package org.airsonic.player.security;
|
||||||
|
|
||||||
import org.airsonic.player.controller.JAXBWriter;
|
import org.airsonic.player.controller.JAXBWriter;
|
||||||
import org.airsonic.player.controller.RESTController;
|
import org.airsonic.player.controller.SubsonicRESTController;
|
||||||
import org.airsonic.player.domain.User;
|
import org.airsonic.player.domain.User;
|
||||||
import org.airsonic.player.domain.Version;
|
import org.airsonic.player.domain.Version;
|
||||||
import org.airsonic.player.service.SecurityService;
|
import org.airsonic.player.service.SecurityService;
|
||||||
@@ -100,7 +100,7 @@ public class RESTRequestParameterProcessingFilter implements Filter {
|
|||||||
String version = StringUtils.trimToNull(httpRequest.getParameter("v"));
|
String version = StringUtils.trimToNull(httpRequest.getParameter("v"));
|
||||||
String client = StringUtils.trimToNull(httpRequest.getParameter("c"));
|
String client = StringUtils.trimToNull(httpRequest.getParameter("c"));
|
||||||
|
|
||||||
RESTController.ErrorCode errorCode = null;
|
SubsonicRESTController.ErrorCode errorCode = null;
|
||||||
|
|
||||||
// The username and credentials parameters are not required if the user
|
// The username and credentials parameters are not required if the user
|
||||||
// was previously authenticated, for example using Basic Auth.
|
// was previously authenticated, for example using Basic Auth.
|
||||||
@@ -108,7 +108,7 @@ public class RESTRequestParameterProcessingFilter implements Filter {
|
|||||||
Authentication previousAuth = SecurityContextHolder.getContext().getAuthentication();
|
Authentication previousAuth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
boolean missingCredentials = previousAuth == null && (username == null || !passwordOrTokenPresent);
|
boolean missingCredentials = previousAuth == null && (username == null || !passwordOrTokenPresent);
|
||||||
if (missingCredentials || version == null || client == null) {
|
if (missingCredentials || version == null || client == null) {
|
||||||
errorCode = RESTController.ErrorCode.MISSING_PARAMETER;
|
errorCode = SubsonicRESTController.ErrorCode.MISSING_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorCode == null) {
|
if (errorCode == null) {
|
||||||
@@ -127,21 +127,21 @@ public class RESTRequestParameterProcessingFilter implements Filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RESTController.ErrorCode checkAPIVersion(String version) {
|
private SubsonicRESTController.ErrorCode checkAPIVersion(String version) {
|
||||||
Version serverVersion = new Version(jaxbWriter.getRestProtocolVersion());
|
Version serverVersion = new Version(jaxbWriter.getRestProtocolVersion());
|
||||||
Version clientVersion = new Version(version);
|
Version clientVersion = new Version(version);
|
||||||
|
|
||||||
if (serverVersion.getMajor() > clientVersion.getMajor()) {
|
if (serverVersion.getMajor() > clientVersion.getMajor()) {
|
||||||
return RESTController.ErrorCode.PROTOCOL_MISMATCH_CLIENT_TOO_OLD;
|
return SubsonicRESTController.ErrorCode.PROTOCOL_MISMATCH_CLIENT_TOO_OLD;
|
||||||
} else if (serverVersion.getMajor() < clientVersion.getMajor()) {
|
} else if (serverVersion.getMajor() < clientVersion.getMajor()) {
|
||||||
return RESTController.ErrorCode.PROTOCOL_MISMATCH_SERVER_TOO_OLD;
|
return SubsonicRESTController.ErrorCode.PROTOCOL_MISMATCH_SERVER_TOO_OLD;
|
||||||
} else if (serverVersion.getMinor() < clientVersion.getMinor()) {
|
} else if (serverVersion.getMinor() < clientVersion.getMinor()) {
|
||||||
return RESTController.ErrorCode.PROTOCOL_MISMATCH_SERVER_TOO_OLD;
|
return SubsonicRESTController.ErrorCode.PROTOCOL_MISMATCH_SERVER_TOO_OLD;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RESTController.ErrorCode authenticate(HttpServletRequest httpRequest, String username, String password, String salt, String token, Authentication previousAuth) {
|
private SubsonicRESTController.ErrorCode authenticate(HttpServletRequest httpRequest, String username, String password, String salt, String token, Authentication previousAuth) {
|
||||||
|
|
||||||
// Previously authenticated and username not overridden?
|
// Previously authenticated and username not overridden?
|
||||||
if (username == null && previousAuth != null) {
|
if (username == null && previousAuth != null) {
|
||||||
@@ -151,11 +151,11 @@ public class RESTRequestParameterProcessingFilter implements Filter {
|
|||||||
if (salt != null && token != null) {
|
if (salt != null && token != null) {
|
||||||
User user = securityService.getUserByName(username);
|
User user = securityService.getUserByName(username);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
return RESTController.ErrorCode.NOT_AUTHENTICATED;
|
return SubsonicRESTController.ErrorCode.NOT_AUTHENTICATED;
|
||||||
}
|
}
|
||||||
String expectedToken = DigestUtils.md5Hex(user.getPassword() + salt);
|
String expectedToken = DigestUtils.md5Hex(user.getPassword() + salt);
|
||||||
if (!expectedToken.equals(token)) {
|
if (!expectedToken.equals(token)) {
|
||||||
return RESTController.ErrorCode.NOT_AUTHENTICATED;
|
return SubsonicRESTController.ErrorCode.NOT_AUTHENTICATED;
|
||||||
}
|
}
|
||||||
|
|
||||||
password = user.getPassword();
|
password = user.getPassword();
|
||||||
@@ -170,11 +170,11 @@ public class RESTRequestParameterProcessingFilter implements Filter {
|
|||||||
return null;
|
return null;
|
||||||
} catch (AuthenticationException x) {
|
} catch (AuthenticationException x) {
|
||||||
eventPublisher.publishEvent(new AuthenticationFailureBadCredentialsEvent(authRequest, x));
|
eventPublisher.publishEvent(new AuthenticationFailureBadCredentialsEvent(authRequest, x));
|
||||||
return RESTController.ErrorCode.NOT_AUTHENTICATED;
|
return SubsonicRESTController.ErrorCode.NOT_AUTHENTICATED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return RESTController.ErrorCode.MISSING_PARAMETER;
|
return SubsonicRESTController.ErrorCode.MISSING_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String decrypt(String s) {
|
public static String decrypt(String s) {
|
||||||
@@ -191,7 +191,7 @@ public class RESTRequestParameterProcessingFilter implements Filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendErrorXml(HttpServletRequest request, HttpServletResponse response, RESTController.ErrorCode errorCode) throws IOException {
|
private void sendErrorXml(HttpServletRequest request, HttpServletResponse response, SubsonicRESTController.ErrorCode errorCode) throws IOException {
|
||||||
try {
|
try {
|
||||||
jaxbWriter.writeErrorResponse(request, response, errorCode, errorCode.getMessage());
|
jaxbWriter.writeErrorResponse(request, response, errorCode, errorCode.getMessage());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package org.airsonic.player.service;
|
||||||
|
|
||||||
|
import org.airsonic.player.dao.BookmarkDao;
|
||||||
|
import org.airsonic.player.domain.Bookmark;
|
||||||
|
import org.airsonic.player.domain.MediaFile;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class BookmarkService {
|
||||||
|
|
||||||
|
private final BookmarkDao dao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public BookmarkService(BookmarkDao dao) {
|
||||||
|
this.dao = dao;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bookmark getBookmarkForUserAndMediaFile(String username, MediaFile mediaFile) {
|
||||||
|
return dao.getBookmarks(username)
|
||||||
|
.stream()
|
||||||
|
.filter(bookmark -> Objects.equals(mediaFile.getId(), bookmark.getMediaFileId()))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createOrUpdateBookmark(Bookmark bookmark) {
|
||||||
|
dao.createOrUpdateBookmark(bookmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteBookmark(String username, int mediaFileId) {
|
||||||
|
dao.deleteBookmark(username, mediaFileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Bookmark> getBookmarks(String username) {
|
||||||
|
return dao.getBookmarks(username);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user