Merge remote-tracking branch 'origin/pr/550'

master
Andrew DeMaria 7 years ago
commit 291e35f445
No known key found for this signature in database
GPG Key ID: 0A3F5E91F8364EDF
  1. 6
      airsonic-main/src/main/java/org/airsonic/player/controller/AvatarController.java
  2. 8
      airsonic-main/src/main/java/org/airsonic/player/controller/CoverArtController.java
  3. 7
      airsonic-main/src/main/java/org/airsonic/player/controller/DownloadController.java
  4. 11
      airsonic-main/src/main/java/org/airsonic/player/controller/HLSController.java
  5. 2
      airsonic-main/src/main/java/org/airsonic/player/controller/JAXBWriter.java
  6. 5
      airsonic-main/src/main/java/org/airsonic/player/controller/ProxyController.java
  7. 13
      airsonic-main/src/main/java/org/airsonic/player/controller/StreamController.java
  8. 69
      airsonic-main/src/main/java/org/airsonic/player/controller/SubsonicRESTController.java
  9. 4
      airsonic-main/src/main/java/org/airsonic/player/filter/RESTFilter.java
  10. 26
      airsonic-main/src/main/java/org/airsonic/player/security/RESTRequestParameterProcessingFilter.java
  11. 40
      airsonic-main/src/main/java/org/airsonic/player/service/BookmarkService.java

@ -28,7 +28,6 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.LastModified;
import javax.servlet.http.HttpServletRequest;
@ -60,17 +59,16 @@ public class AvatarController implements LastModified {
}
@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);
if (avatar == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return null;
return;
}
response.setContentType(avatar.getMimeType());
response.getOutputStream().write(avatar.getData());
return null;
}
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.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.LastModified;
import javax.annotation.PostConstruct;
@ -98,7 +97,7 @@ public class CoverArtController implements LastModified {
}
@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);
// 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.)
if (coverArtRequest == null) {
sendFallback(size, response);
return null;
return;
}
// Optimize if no scaling is required.
if (size == null && coverArtRequest.getCoverArt() != null) {
// LOG.info("sendUnscaled - " + coverArtRequest);
sendUnscaled(coverArtRequest, response);
return null;
return;
}
// Send cached image, creating it if necessary.
@ -128,7 +127,6 @@ public class CoverArtController implements LastModified {
sendFallback(size, response);
}
return null;
}
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.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.LastModified;
import javax.servlet.http.HttpServletRequest;
@ -89,7 +88,7 @@ public class DownloadController implements LastModified {
}
@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);
TransferStatus status = null;
@ -118,7 +117,7 @@ public class DownloadController implements LastModified {
if (!securityService.isFolderAccessAllowed(mediaFile, user.getUsername())) {
response.sendError(HttpServletResponse.SC_FORBIDDEN,
"Access to file " + mediaFile.getId() + " is forbidden for user " + user.getUsername());
return null;
return;
}
if (mediaFile.isFile()) {
@ -148,8 +147,6 @@ public class DownloadController implements LastModified {
securityService.updateUserByteCounts(user, 0L, status.getBytesTransfered(), 0L);
}
}
return null;
}
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.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.UriComponentsBuilder;
import javax.servlet.http.HttpServletRequest;
@ -68,7 +67,7 @@ public class HLSController {
private JWTSecurityService jwtSecurityService;
@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", "*");
@ -79,19 +78,19 @@ public class HLSController {
if (mediaFile == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Media file not found: " + id);
return null;
return;
}
if (username != null && !securityService.isFolderAccessAllowed(mediaFile, username)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN,
"Access to file " + mediaFile.getId() + " is forbidden for user " + username);
return null;
return;
}
Integer duration = mediaFile.getDurationSeconds();
if (duration == null || duration == 0) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unknown duration for media file: " + id);
return null;
return;
}
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);
}
return null;
return;
}
private List<Pair<Integer, Dimension>> parseBitRates(HttpServletRequest request) throws IllegalArgumentException {

@ -157,7 +157,7 @@ public class JAXBWriter {
}
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);
Error error = new Error();
res.setError(error);

@ -20,7 +20,6 @@
package org.airsonic.player.controller;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
@ -37,6 +36,8 @@ import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import static org.springframework.http.HttpStatus.*;
/**
* A proxy for external HTTP requests.
*
@ -61,7 +62,7 @@ public class ProxyController {
try (CloseableHttpClient client = HttpClients.createDefault()) {
try (CloseableHttpResponse resp = client.execute(method)) {
int statusCode = resp.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
if (statusCode != OK.value()) {
response.sendError(statusCode);
} else {
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.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -84,7 +83,7 @@ public class StreamController {
private SearchService searchService;
@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;
PlayQueueInputStream in = null;
@ -96,7 +95,7 @@ public class StreamController {
if (!(authentication instanceof JWTAuthenticationToken) && !user.isStreamRole()) {
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
@ -136,7 +135,7 @@ public class StreamController {
if (!(authentication instanceof JWTAuthenticationToken) && !securityService.isFolderAccessAllowed(file, user.getUsername())) {
response.sendError(HttpServletResponse.SC_FORBIDDEN,
"Access to file " + file.getId() + " is forbidden for user " + user.getUsername());
return null;
return;
}
// Update the index of the currently playing media file. At
@ -189,7 +188,7 @@ public class StreamController {
}
if (request.getMethod().equals("HEAD")) {
return null;
return;
}
// Terminate any other streams to this player.
@ -226,7 +225,7 @@ public class StreamController {
// Check if stream has been terminated.
if (status.terminated()) {
return null;
return;
}
if (player.getPlayQueue().getStatus() == PlayQueue.Status.STOPPED) {
@ -257,7 +256,7 @@ public class StreamController {
}
IOUtils.closeQuietly(in);
}
return null;
return;
}
private void setContentDuration(HttpServletResponse response, MediaFile file) {

@ -23,8 +23,12 @@ import org.airsonic.player.ajax.LyricsInfo;
import org.airsonic.player.ajax.LyricsService;
import org.airsonic.player.ajax.PlayQueueService;
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.Bookmark;
import org.airsonic.player.service.*;
import org.airsonic.player.util.Pair;
import org.airsonic.player.util.StringUtil;
@ -43,7 +47,6 @@ import org.springframework.web.servlet.ModelAndView;
import org.subsonic.restapi.*;
import org.subsonic.restapi.PodcastStatus;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
@ -65,9 +68,9 @@ import static org.springframework.web.bind.ServletRequestUtils.*;
*/
@Controller
@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
private SettingsService settingsService;
@ -124,7 +127,7 @@ public class RESTController {
@Autowired
private AlbumDao albumDao;
@Autowired
private BookmarkDao bookmarkDao;
private BookmarkService bookmarkService;
@Autowired
private PlayQueueDao playQueueDao;
@Autowired
@ -136,18 +139,6 @@ public class RESTController {
private static final String NOT_YET_IMPLEMENTED = "Not yet implemented";
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")
public void ping(HttpServletRequest request, HttpServletResponse response) throws Exception {
Response res = createResponse();
@ -1337,12 +1328,12 @@ public class RESTController {
}
@RequestMapping(value = "/download")
public ModelAndView download(HttpServletRequest request, HttpServletResponse response) throws Exception {
public void download(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
org.airsonic.player.domain.User user = securityService.getCurrentUser(request);
if (!user.isDownloadRole()) {
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to download files.");
return null;
return;
}
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
@ -1350,49 +1341,47 @@ public class RESTController {
if (ifModifiedSince != -1 && lastModified != -1 && lastModified <= ifModifiedSince) {
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return null;
return;
}
if (lastModified != -1) {
response.setDateHeader("Last-Modified", lastModified);
}
return downloadController.handleRequest(request, response);
downloadController.handleRequest(request, response);
}
@RequestMapping(value = "/stream")
public ModelAndView stream(HttpServletRequest request, HttpServletResponse response) throws Exception {
public void stream(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
org.airsonic.player.domain.User user = securityService.getCurrentUser(request);
if (!user.isStreamRole()) {
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to play files.");
return null;
return;
}
streamController.handleRequest(request, response);
return null;
}
@RequestMapping(value = "/hls")
public ModelAndView hls(HttpServletRequest request, HttpServletResponse response) throws Exception {
public void hls(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
org.airsonic.player.domain.User user = securityService.getCurrentUser(request);
if (!user.isStreamRole()) {
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to play files.");
return null;
return;
}
int id = getRequiredIntParameter(request, "id");
MediaFile video = mediaFileDao.getMediaFile(id);
if (video == null || video.isDirectory()) {
error(request, response, ErrorCode.NOT_FOUND, "Video not found.");
return null;
return;
}
if (!securityService.isFolderAccessAllowed(video, user.getUsername())) {
error(request, response, ErrorCode.NOT_AUTHORIZED, "Access denied");
return null;
return;
}
hlsController.handleRequest(request, response);
return null;
}
@RequestMapping(value = "/scrobble")
@ -1702,7 +1691,7 @@ public class RESTController {
String username = securityService.getCurrentUsername(request);
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();
result.getBookmark().add(b);
b.setPosition(bookmark.getPositionMillis());
@ -1729,9 +1718,8 @@ public class RESTController {
String comment = request.getParameter("comment");
Date now = new Date();
org.airsonic.player.domain.Bookmark bookmark = new org.airsonic.player.domain.Bookmark(0, mediaFileId, position, username, comment, now, now);
bookmarkDao.createOrUpdateBookmark(bookmark);
refreshBookmarkCache();
Bookmark bookmark = new Bookmark(0, mediaFileId, position, username, comment, now, now);
bookmarkService.createOrUpdateBookmark(bookmark);
writeEmptyResponse(request, response);
}
@ -1741,8 +1729,7 @@ public class RESTController {
String username = securityService.getCurrentUsername(request);
int mediaFileId = getRequiredIntParameter(request, "id");
bookmarkDao.deleteBookmark(username, mediaFileId);
refreshBookmarkCache();
bookmarkService.deleteBookmark(username, mediaFileId);
writeEmptyResponse(request, response);
}
@ -1954,15 +1941,15 @@ public class RESTController {
}
@RequestMapping(value = "/getCoverArt")
public ModelAndView getCoverArt(HttpServletRequest request, HttpServletResponse response) throws Exception {
public void getCoverArt(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
return coverArtController.handleRequest(request, response);
coverArtController.handleRequest(request, response);
}
@RequestMapping(value = "/getAvatar")
public ModelAndView getAvatar(HttpServletRequest request, HttpServletResponse response) throws Exception {
public void getAvatar(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
return avatarController.handleRequest(request, response);
avatarController.handleRequest(request, response);
}
@RequestMapping(value = "/changePassword")
@ -2237,7 +2224,7 @@ public class RESTController {
MediaFile mediaFile = this.mediaFileService.getMediaFile(id);
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;
}
AlbumNotes albumNotes = this.lastFmService.getAlbumNotes(mediaFile);
@ -2256,7 +2243,7 @@ public class RESTController {
Album album = this.albumDao.getAlbum(id);
if (album == null) {
error(request, response, RESTController.ErrorCode.NOT_FOUND, "Album not found.");
error(request, response, SubsonicRESTController.ErrorCode.NOT_FOUND, "Album not found.");
return;
}
AlbumNotes albumNotes = this.lastFmService.getAlbumNotes(album);

@ -20,7 +20,7 @@
package org.airsonic.player.filter;
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.LoggerFactory;
import org.springframework.web.bind.ServletRequestBindingException;
@ -61,7 +61,7 @@ public class RESTFilter implements Filter {
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);
LOG.warn("Error in REST API: " + msg, x);

@ -20,7 +20,7 @@
package org.airsonic.player.security;
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.Version;
import org.airsonic.player.service.SecurityService;
@ -100,7 +100,7 @@ public class RESTRequestParameterProcessingFilter implements Filter {
String version = StringUtils.trimToNull(httpRequest.getParameter("v"));
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
// was previously authenticated, for example using Basic Auth.
@ -108,7 +108,7 @@ public class RESTRequestParameterProcessingFilter implements Filter {
Authentication previousAuth = SecurityContextHolder.getContext().getAuthentication();
boolean missingCredentials = previousAuth == null && (username == null || !passwordOrTokenPresent);
if (missingCredentials || version == null || client == null) {
errorCode = RESTController.ErrorCode.MISSING_PARAMETER;
errorCode = SubsonicRESTController.ErrorCode.MISSING_PARAMETER;
}
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 clientVersion = new Version(version);
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()) {
return RESTController.ErrorCode.PROTOCOL_MISMATCH_SERVER_TOO_OLD;
return SubsonicRESTController.ErrorCode.PROTOCOL_MISMATCH_SERVER_TOO_OLD;
} else if (serverVersion.getMinor() < clientVersion.getMinor()) {
return RESTController.ErrorCode.PROTOCOL_MISMATCH_SERVER_TOO_OLD;
return SubsonicRESTController.ErrorCode.PROTOCOL_MISMATCH_SERVER_TOO_OLD;
}
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?
if (username == null && previousAuth != null) {
@ -151,11 +151,11 @@ public class RESTRequestParameterProcessingFilter implements Filter {
if (salt != null && token != null) {
User user = securityService.getUserByName(username);
if (user == null) {
return RESTController.ErrorCode.NOT_AUTHENTICATED;
return SubsonicRESTController.ErrorCode.NOT_AUTHENTICATED;
}
String expectedToken = DigestUtils.md5Hex(user.getPassword() + salt);
if (!expectedToken.equals(token)) {
return RESTController.ErrorCode.NOT_AUTHENTICATED;
return SubsonicRESTController.ErrorCode.NOT_AUTHENTICATED;
}
password = user.getPassword();
@ -170,11 +170,11 @@ public class RESTRequestParameterProcessingFilter implements Filter {
return null;
} catch (AuthenticationException 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) {
@ -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 {
jaxbWriter.writeErrorResponse(request, response, errorCode, errorCode.getMessage());
} 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);
}
}
Loading…
Cancel
Save