aboutsummaryrefslogtreecommitdiff
path: root/subsonic-main/src/main/java/net/sourceforge/subsonic/controller/RESTController.java
diff options
context:
space:
mode:
Diffstat (limited to 'subsonic-main/src/main/java/net/sourceforge/subsonic/controller/RESTController.java')
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/controller/RESTController.java1983
1 files changed, 1983 insertions, 0 deletions
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/controller/RESTController.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/controller/RESTController.java
new file mode 100644
index 00000000..2d4fa73c
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/controller/RESTController.java
@@ -0,0 +1,1983 @@
+/*
+ This file is part of Subsonic.
+
+ Subsonic is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Subsonic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
+
+ Copyright 2009 (C) Sindre Mehus
+ */
+package net.sourceforge.subsonic.controller;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+
+import net.sourceforge.subsonic.ajax.PlayQueueService;
+import net.sourceforge.subsonic.domain.Playlist;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.web.bind.ServletRequestBindingException;
+import org.springframework.web.bind.ServletRequestUtils;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
+
+import net.sourceforge.subsonic.Logger;
+import net.sourceforge.subsonic.ajax.ChatService;
+import net.sourceforge.subsonic.ajax.LyricsInfo;
+import net.sourceforge.subsonic.ajax.LyricsService;
+import net.sourceforge.subsonic.command.UserSettingsCommand;
+import net.sourceforge.subsonic.dao.AlbumDao;
+import net.sourceforge.subsonic.dao.ArtistDao;
+import net.sourceforge.subsonic.dao.MediaFileDao;
+import net.sourceforge.subsonic.domain.Album;
+import net.sourceforge.subsonic.domain.Artist;
+import net.sourceforge.subsonic.domain.MediaFile;
+import net.sourceforge.subsonic.domain.MusicFolder;
+import net.sourceforge.subsonic.domain.MusicIndex;
+import net.sourceforge.subsonic.domain.Player;
+import net.sourceforge.subsonic.domain.PlayerTechnology;
+import net.sourceforge.subsonic.domain.PlayQueue;
+import net.sourceforge.subsonic.domain.PodcastChannel;
+import net.sourceforge.subsonic.domain.PodcastEpisode;
+import net.sourceforge.subsonic.domain.RandomSearchCriteria;
+import net.sourceforge.subsonic.domain.SearchCriteria;
+import net.sourceforge.subsonic.domain.SearchResult;
+import net.sourceforge.subsonic.domain.Share;
+import net.sourceforge.subsonic.domain.TranscodeScheme;
+import net.sourceforge.subsonic.domain.TransferStatus;
+import net.sourceforge.subsonic.domain.User;
+import net.sourceforge.subsonic.domain.UserSettings;
+import net.sourceforge.subsonic.service.AudioScrobblerService;
+import net.sourceforge.subsonic.service.JukeboxService;
+import net.sourceforge.subsonic.service.MediaFileService;
+import net.sourceforge.subsonic.service.PlayerService;
+import net.sourceforge.subsonic.service.PlaylistService;
+import net.sourceforge.subsonic.service.PodcastService;
+import net.sourceforge.subsonic.service.RatingService;
+import net.sourceforge.subsonic.service.SearchService;
+import net.sourceforge.subsonic.service.SecurityService;
+import net.sourceforge.subsonic.service.SettingsService;
+import net.sourceforge.subsonic.service.ShareService;
+import net.sourceforge.subsonic.service.StatusService;
+import net.sourceforge.subsonic.service.TranscodingService;
+import net.sourceforge.subsonic.util.StringUtil;
+import net.sourceforge.subsonic.util.XMLBuilder;
+
+import static net.sourceforge.subsonic.security.RESTRequestParameterProcessingFilter.decrypt;
+import static net.sourceforge.subsonic.util.XMLBuilder.Attribute;
+import static net.sourceforge.subsonic.util.XMLBuilder.AttributeSet;
+
+/**
+ * Multi-controller used for the REST API.
+ * <p/>
+ * For documentation, please refer to api.jsp.
+ *
+ * @author Sindre Mehus
+ */
+public class RESTController extends MultiActionController {
+
+ private static final Logger LOG = Logger.getLogger(RESTController.class);
+
+ private SettingsService settingsService;
+ private SecurityService securityService;
+ private PlayerService playerService;
+ private MediaFileService mediaFileService;
+ private TranscodingService transcodingService;
+ private DownloadController downloadController;
+ private CoverArtController coverArtController;
+ private AvatarController avatarController;
+ private UserSettingsController userSettingsController;
+ private LeftController leftController;
+ private HomeController homeController;
+ private StatusService statusService;
+ private StreamController streamController;
+ private ShareService shareService;
+ private PlaylistService playlistService;
+ private ChatService chatService;
+ private LyricsService lyricsService;
+ private PlayQueueService playQueueService;
+ private JukeboxService jukeboxService;
+ private AudioScrobblerService audioScrobblerService;
+ private PodcastService podcastService;
+ private RatingService ratingService;
+ private SearchService searchService;
+ private MediaFileDao mediaFileDao;
+ private ArtistDao artistDao;
+ private AlbumDao albumDao;
+
+ public void ping(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getLicense(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ String email = settingsService.getLicenseEmail();
+ String key = settingsService.getLicenseCode();
+ Date date = settingsService.getLicenseDate();
+ boolean valid = settingsService.isLicenseValid();
+
+ AttributeSet attributes = new AttributeSet();
+ attributes.add("valid", valid);
+ if (valid) {
+ attributes.add("email", email);
+ attributes.add("key", key);
+ attributes.add("date", StringUtil.toISO8601(date));
+ }
+
+ builder.add("license", attributes, true);
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getMusicFolders(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.add("musicFolders", false);
+
+ for (MusicFolder musicFolder : settingsService.getAllMusicFolders()) {
+ AttributeSet attributes = new AttributeSet();
+ attributes.add("id", musicFolder.getId());
+ attributes.add("name", musicFolder.getName());
+ builder.add("musicFolder", attributes, true);
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getIndexes(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ long ifModifiedSince = ServletRequestUtils.getLongParameter(request, "ifModifiedSince", 0L);
+ long lastModified = leftController.getLastModified(request);
+
+ if (lastModified <= ifModifiedSince) {
+ builder.endAll();
+ response.getWriter().print(builder);
+ return;
+ }
+
+ builder.add("indexes", "lastModified", lastModified, false);
+
+ List<MusicFolder> musicFolders = settingsService.getAllMusicFolders();
+ Integer musicFolderId = ServletRequestUtils.getIntParameter(request, "musicFolderId");
+ if (musicFolderId != null) {
+ for (MusicFolder musicFolder : musicFolders) {
+ if (musicFolderId.equals(musicFolder.getId())) {
+ musicFolders = Arrays.asList(musicFolder);
+ break;
+ }
+ }
+ }
+
+ List<MediaFile> shortcuts = leftController.getShortcuts(musicFolders, settingsService.getShortcutsAsArray());
+ for (MediaFile shortcut : shortcuts) {
+ builder.add("shortcut", true,
+ new Attribute("name", shortcut.getName()),
+ new Attribute("id", shortcut.getId()));
+ }
+
+ SortedMap<MusicIndex, SortedSet<MusicIndex.Artist>> indexedArtists = leftController.getMusicFolderContent(musicFolders).getIndexedArtists();
+
+ for (Map.Entry<MusicIndex, SortedSet<MusicIndex.Artist>> entry : indexedArtists.entrySet()) {
+ builder.add("index", "name", entry.getKey().getIndex(), false);
+
+ for (MusicIndex.Artist artist : entry.getValue()) {
+ for (MediaFile mediaFile : artist.getMediaFiles()) {
+ if (mediaFile.isDirectory()) {
+ builder.add("artist", true,
+ new Attribute("name", artist.getName()),
+ new Attribute("id", mediaFile.getId()));
+ }
+ }
+ }
+ builder.end();
+ }
+
+ // Add children
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+ List<MediaFile> singleSongs = leftController.getSingleSongs(musicFolders);
+
+ for (MediaFile singleSong : singleSongs) {
+ builder.add("child", createAttributesForMediaFile(player, singleSong, username), true);
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getArtists(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ String username = securityService.getCurrentUsername(request);
+
+ builder.add("artists", false);
+
+ List<Artist> artists = artistDao.getAlphabetialArtists(0, Integer.MAX_VALUE);
+ for (Artist artist : artists) {
+ AttributeSet attributes = createAttributesForArtist(artist, username);
+ builder.add("artist", attributes, true);
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ private AttributeSet createAttributesForArtist(Artist artist, String username) {
+ AttributeSet attributes = new AttributeSet();
+ attributes.add("id", artist.getId());
+ attributes.add("name", artist.getName());
+ if (artist.getCoverArtPath() != null) {
+ attributes.add("coverArt", CoverArtController.ARTIST_COVERART_PREFIX + artist.getId());
+ }
+ attributes.add("albumCount", artist.getAlbumCount());
+ attributes.add("starred", StringUtil.toISO8601(artistDao.getArtistStarredDate(artist.getId(), username)));
+ return attributes;
+ }
+
+ public void getArtist(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ String username = securityService.getCurrentUsername(request);
+ Artist artist;
+ try {
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
+ artist = artistDao.getArtist(id);
+ if (artist == null) {
+ throw new Exception();
+ }
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.NOT_FOUND, "Artist not found.");
+ return;
+ }
+
+ builder.add("artist", createAttributesForArtist(artist, username), false);
+ for (Album album : albumDao.getAlbumsForArtist(artist.getName())) {
+ builder.add("album", createAttributesForAlbum(album, username), true);
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ private AttributeSet createAttributesForAlbum(Album album, String username) {
+ AttributeSet attributes;
+ attributes = new AttributeSet();
+ attributes.add("id", album.getId());
+ attributes.add("name", album.getName());
+ attributes.add("artist", album.getArtist());
+ if (album.getArtist() != null) {
+ Artist artist = artistDao.getArtist(album.getArtist());
+ if (artist != null) {
+ attributes.add("artistId", artist.getId());
+ }
+ }
+ if (album.getCoverArtPath() != null) {
+ attributes.add("coverArt", CoverArtController.ALBUM_COVERART_PREFIX + album.getId());
+ }
+ attributes.add("songCount", album.getSongCount());
+ attributes.add("duration", album.getDurationSeconds());
+ attributes.add("created", StringUtil.toISO8601(album.getCreated()));
+ attributes.add("starred", StringUtil.toISO8601(albumDao.getAlbumStarredDate(album.getId(), username)));
+
+ return attributes;
+ }
+
+ private AttributeSet createAttributesForPlaylist(Playlist playlist) {
+ AttributeSet attributes;
+ attributes = new AttributeSet();
+ attributes.add("id", playlist.getId());
+ attributes.add("name", playlist.getName());
+ attributes.add("comment", playlist.getComment());
+ attributes.add("owner", playlist.getUsername());
+ attributes.add("public", playlist.isPublic());
+ attributes.add("songCount", playlist.getFileCount());
+ attributes.add("duration", playlist.getDurationSeconds());
+ attributes.add("created", StringUtil.toISO8601(playlist.getCreated()));
+ return attributes;
+ }
+
+ public void getAlbum(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ Album album;
+ try {
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
+ album = albumDao.getAlbum(id);
+ if (album == null) {
+ throw new Exception();
+ }
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.NOT_FOUND, "Album not found.");
+ return;
+ }
+
+ builder.add("album", createAttributesForAlbum(album, username), false);
+ for (MediaFile mediaFile : mediaFileDao.getSongsForAlbum(album.getArtist(), album.getName())) {
+ builder.add("song", createAttributesForMediaFile(player, mediaFile, username) , true);
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getSong(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ MediaFile song;
+ try {
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
+ song = mediaFileDao.getMediaFile(id);
+ if (song == null || song.isDirectory()) {
+ throw new Exception();
+ }
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.NOT_FOUND, "Song not found.");
+ return;
+ }
+
+ builder.add("song", createAttributesForMediaFile(player, song, username), true);
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getMusicDirectory(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ MediaFile dir;
+ try {
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
+ dir = mediaFileService.getMediaFile(id);
+ if (dir == null) {
+ throw new Exception();
+ }
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.NOT_FOUND, "Directory not found");
+ return;
+ }
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.add("directory", false,
+ new Attribute("id", dir.getId()),
+ new Attribute("name", dir.getName()));
+
+ for (MediaFile child : mediaFileService.getChildrenOf(dir, true, true, true)) {
+ AttributeSet attributes = createAttributesForMediaFile(player, child, username);
+ builder.add("child", attributes, true);
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ @Deprecated
+ public void search(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ String any = request.getParameter("any");
+ String artist = request.getParameter("artist");
+ String album = request.getParameter("album");
+ String title = request.getParameter("title");
+
+ StringBuilder query = new StringBuilder();
+ if (any != null) {
+ query.append(any).append(" ");
+ }
+ if (artist != null) {
+ query.append(artist).append(" ");
+ }
+ if (album != null) {
+ query.append(album).append(" ");
+ }
+ if (title != null) {
+ query.append(title);
+ }
+
+ SearchCriteria criteria = new SearchCriteria();
+ criteria.setQuery(query.toString().trim());
+ criteria.setCount(ServletRequestUtils.getIntParameter(request, "count", 20));
+ criteria.setOffset(ServletRequestUtils.getIntParameter(request, "offset", 0));
+
+ SearchResult result = searchService.search(criteria, SearchService.IndexType.SONG);
+ builder.add("searchResult", false,
+ new Attribute("offset", result.getOffset()),
+ new Attribute("totalHits", result.getTotalHits()));
+
+ for (MediaFile mediaFile : result.getMediaFiles()) {
+ AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
+ builder.add("match", attributes, true);
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void search2(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ builder.add("searchResult2", false);
+
+ String query = request.getParameter("query");
+ SearchCriteria criteria = new SearchCriteria();
+ criteria.setQuery(StringUtils.trimToEmpty(query));
+ criteria.setCount(ServletRequestUtils.getIntParameter(request, "artistCount", 20));
+ criteria.setOffset(ServletRequestUtils.getIntParameter(request, "artistOffset", 0));
+ SearchResult artists = searchService.search(criteria, SearchService.IndexType.ARTIST);
+ for (MediaFile mediaFile : artists.getMediaFiles()) {
+ builder.add("artist", true,
+ new Attribute("name", mediaFile.getName()),
+ new Attribute("id", mediaFile.getId()));
+ }
+
+ criteria.setCount(ServletRequestUtils.getIntParameter(request, "albumCount", 20));
+ criteria.setOffset(ServletRequestUtils.getIntParameter(request, "albumOffset", 0));
+ SearchResult albums = searchService.search(criteria, SearchService.IndexType.ALBUM);
+ for (MediaFile mediaFile : albums.getMediaFiles()) {
+ AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
+ builder.add("album", attributes, true);
+ }
+
+ criteria.setCount(ServletRequestUtils.getIntParameter(request, "songCount", 20));
+ criteria.setOffset(ServletRequestUtils.getIntParameter(request, "songOffset", 0));
+ SearchResult songs = searchService.search(criteria, SearchService.IndexType.SONG);
+ for (MediaFile mediaFile : songs.getMediaFiles()) {
+ AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
+ builder.add("song", attributes, true);
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void search3(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ builder.add("searchResult3", false);
+
+ String query = request.getParameter("query");
+ SearchCriteria criteria = new SearchCriteria();
+ criteria.setQuery(StringUtils.trimToEmpty(query));
+ criteria.setCount(ServletRequestUtils.getIntParameter(request, "artistCount", 20));
+ criteria.setOffset(ServletRequestUtils.getIntParameter(request, "artistOffset", 0));
+ SearchResult searchResult = searchService.search(criteria, SearchService.IndexType.ARTIST_ID3);
+ for (Artist artist : searchResult.getArtists()) {
+ builder.add("artist", createAttributesForArtist(artist, username), true);
+ }
+
+ criteria.setCount(ServletRequestUtils.getIntParameter(request, "albumCount", 20));
+ criteria.setOffset(ServletRequestUtils.getIntParameter(request, "albumOffset", 0));
+ searchResult = searchService.search(criteria, SearchService.IndexType.ALBUM_ID3);
+ for (Album album : searchResult.getAlbums()) {
+ builder.add("album", createAttributesForAlbum(album, username), true);
+ }
+
+ criteria.setCount(ServletRequestUtils.getIntParameter(request, "songCount", 20));
+ criteria.setOffset(ServletRequestUtils.getIntParameter(request, "songOffset", 0));
+ searchResult = searchService.search(criteria, SearchService.IndexType.SONG);
+ for (MediaFile song : searchResult.getMediaFiles()) {
+ builder.add("song", createAttributesForMediaFile(player, song, username), true);
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getPlaylists(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ User user = securityService.getCurrentUser(request);
+ String authenticatedUsername = user.getUsername();
+ String requestedUsername = request.getParameter("username");
+
+ if (requestedUsername == null) {
+ requestedUsername = authenticatedUsername;
+ } else if (!user.isAdminRole()) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, authenticatedUsername + " is not authorized to get playlists for " + requestedUsername);
+ return;
+ }
+
+ builder.add("playlists", false);
+
+ for (Playlist playlist : playlistService.getReadablePlaylistsForUser(requestedUsername)) {
+ List<String> sharedUsers = playlistService.getPlaylistUsers(playlist.getId());
+ builder.add("playlist", createAttributesForPlaylist(playlist), sharedUsers.isEmpty());
+ if (!sharedUsers.isEmpty()) {
+ for (String username : sharedUsers) {
+ builder.add("allowedUser", (Iterable<Attribute>) null, username, true);
+ }
+ builder.end();
+ }
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getPlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ try {
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
+
+ Playlist playlist = playlistService.getPlaylist(id);
+ if (playlist == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "Playlist not found: " + id);
+ return;
+ }
+ if (!playlistService.isReadAllowed(playlist, username)) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, "Permission denied for playlist " + id);
+ return;
+ }
+ builder.add("playlist", createAttributesForPlaylist(playlist), false);
+ for (String allowedUser : playlistService.getPlaylistUsers(playlist.getId())) {
+ builder.add("allowedUser", (Iterable<Attribute>) null, allowedUser, true);
+ }
+ for (MediaFile mediaFile : playlistService.getFilesInPlaylist(id)) {
+ AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
+ builder.add("entry", attributes, true);
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void jukeboxControl(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request, true);
+
+ User user = securityService.getCurrentUser(request);
+ if (!user.isJukeboxRole()) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to use jukebox.");
+ return;
+ }
+
+ try {
+ boolean returnPlaylist = false;
+ String action = ServletRequestUtils.getRequiredStringParameter(request, "action");
+ if ("start".equals(action)) {
+ playQueueService.doStart(request, response);
+ } else if ("stop".equals(action)) {
+ playQueueService.doStop(request, response);
+ } else if ("skip".equals(action)) {
+ int index = ServletRequestUtils.getRequiredIntParameter(request, "index");
+ int offset = ServletRequestUtils.getIntParameter(request, "offset", 0);
+ playQueueService.doSkip(request, response, index, offset);
+ } else if ("add".equals(action)) {
+ int[] ids = ServletRequestUtils.getIntParameters(request, "id");
+ playQueueService.doAdd(request, response, ids);
+ } else if ("set".equals(action)) {
+ int[] ids = ServletRequestUtils.getIntParameters(request, "id");
+ playQueueService.doSet(request, response, ids);
+ } else if ("clear".equals(action)) {
+ playQueueService.doClear(request, response);
+ } else if ("remove".equals(action)) {
+ int index = ServletRequestUtils.getRequiredIntParameter(request, "index");
+ playQueueService.doRemove(request, response, index);
+ } else if ("shuffle".equals(action)) {
+ playQueueService.doShuffle(request, response);
+ } else if ("setGain".equals(action)) {
+ float gain = ServletRequestUtils.getRequiredFloatParameter(request, "gain");
+ jukeboxService.setGain(gain);
+ } else if ("get".equals(action)) {
+ returnPlaylist = true;
+ } else if ("status".equals(action)) {
+ // No action necessary.
+ } else {
+ throw new Exception("Unknown jukebox action: '" + action + "'.");
+ }
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+ Player jukeboxPlayer = jukeboxService.getPlayer();
+ boolean controlsJukebox = jukeboxPlayer != null && jukeboxPlayer.getId().equals(player.getId());
+ PlayQueue playQueue = player.getPlayQueue();
+
+ List<Attribute> attrs = new ArrayList<Attribute>(Arrays.asList(
+ new Attribute("currentIndex", controlsJukebox && !playQueue.isEmpty() ? playQueue.getIndex() : -1),
+ new Attribute("playing", controlsJukebox && !playQueue.isEmpty() && playQueue.getStatus() == PlayQueue.Status.PLAYING),
+ new Attribute("gain", jukeboxService.getGain()),
+ new Attribute("position", controlsJukebox && !playQueue.isEmpty() ? jukeboxService.getPosition() : 0)));
+
+ if (returnPlaylist) {
+ builder.add("jukeboxPlaylist", attrs, false);
+ List<MediaFile> result;
+ synchronized (playQueue) {
+ result = playQueue.getFiles();
+ }
+ for (MediaFile mediaFile : result) {
+ AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
+ builder.add("entry", attributes, true);
+ }
+ } else {
+ builder.add("jukeboxStatus", attrs, false);
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void createPlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request, true);
+ String username = securityService.getCurrentUsername(request);
+
+ try {
+
+ Integer playlistId = ServletRequestUtils.getIntParameter(request, "playlistId");
+ String name = request.getParameter("name");
+ if (playlistId == null && name == null) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, "Playlist ID or name must be specified.");
+ return;
+ }
+
+ Playlist playlist;
+ if (playlistId != null) {
+ playlist = playlistService.getPlaylist(playlistId);
+ if (playlist == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "Playlist not found: " + playlistId);
+ return;
+ }
+ if (!playlistService.isWriteAllowed(playlist, username)) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, "Permission denied for playlist " + playlistId);
+ return;
+ }
+ } else {
+ playlist = new Playlist();
+ playlist.setName(name);
+ playlist.setCreated(new Date());
+ playlist.setChanged(new Date());
+ playlist.setPublic(false);
+ playlist.setUsername(username);
+ playlistService.createPlaylist(playlist);
+ }
+
+ List<MediaFile> songs = new ArrayList<MediaFile>();
+ for (int id : ServletRequestUtils.getIntParameters(request, "songId")) {
+ MediaFile song = mediaFileService.getMediaFile(id);
+ if (song != null) {
+ songs.add(song);
+ }
+ }
+ playlistService.setFilesInPlaylist(playlist.getId(), songs);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.endAll();
+ response.getWriter().print(builder);
+
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void updatePlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request, true);
+ String username = securityService.getCurrentUsername(request);
+
+ try {
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "playlistId");
+ Playlist playlist = playlistService.getPlaylist(id);
+ if (playlist == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "Playlist not found: " + id);
+ return;
+ }
+ if (!playlistService.isWriteAllowed(playlist, username)) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, "Permission denied for playlist " + id);
+ return;
+ }
+
+ String name = request.getParameter("name");
+ if (name != null) {
+ playlist.setName(name);
+ }
+ String comment = request.getParameter("comment");
+ if (comment != null) {
+ playlist.setComment(comment);
+ }
+ Boolean isPublic = ServletRequestUtils.getBooleanParameter(request, "public");
+ if (isPublic != null) {
+ playlist.setPublic(isPublic);
+ }
+ playlistService.updatePlaylist(playlist);
+
+ // TODO: Add later
+// for (String usernameToAdd : ServletRequestUtils.getStringParameters(request, "usernameToAdd")) {
+// if (securityService.getUserByName(usernameToAdd) != null) {
+// playlistService.addPlaylistUser(id, usernameToAdd);
+// }
+// }
+// for (String usernameToRemove : ServletRequestUtils.getStringParameters(request, "usernameToRemove")) {
+// if (securityService.getUserByName(usernameToRemove) != null) {
+// playlistService.deletePlaylistUser(id, usernameToRemove);
+// }
+// }
+ List<MediaFile> songs = playlistService.getFilesInPlaylist(id);
+ boolean songsChanged = false;
+
+ SortedSet<Integer> tmp = new TreeSet<Integer>();
+ for (int songIndexToRemove : ServletRequestUtils.getIntParameters(request, "songIndexToRemove")) {
+ tmp.add(songIndexToRemove);
+ }
+ List<Integer> songIndexesToRemove = new ArrayList<Integer>(tmp);
+ Collections.reverse(songIndexesToRemove);
+ for (Integer songIndexToRemove : songIndexesToRemove) {
+ songs.remove(songIndexToRemove.intValue());
+ songsChanged = true;
+ }
+ for (int songToAdd : ServletRequestUtils.getIntParameters(request, "songIdToAdd")) {
+ MediaFile song = mediaFileService.getMediaFile(songToAdd);
+ if (song != null) {
+ songs.add(song);
+ songsChanged = true;
+ }
+ }
+ if (songsChanged) {
+ playlistService.setFilesInPlaylist(id, songs);
+ }
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.endAll();
+ response.getWriter().print(builder);
+
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void deletePlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request, true);
+ String username = securityService.getCurrentUsername(request);
+
+ try {
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
+ Playlist playlist = playlistService.getPlaylist(id);
+ if (playlist == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "Playlist not found: " + id);
+ return;
+ }
+ if (!playlistService.isWriteAllowed(playlist, username)) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, "Permission denied for playlist " + id);
+ return;
+ }
+ playlistService.deletePlaylist(id);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.endAll();
+ response.getWriter().print(builder);
+
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void getAlbumList(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.add("albumList", false);
+
+ try {
+ int size = ServletRequestUtils.getIntParameter(request, "size", 10);
+ int offset = ServletRequestUtils.getIntParameter(request, "offset", 0);
+ size = Math.max(0, Math.min(size, 500));
+ String type = ServletRequestUtils.getRequiredStringParameter(request, "type");
+
+ List<HomeController.Album> albums;
+ if ("highest".equals(type)) {
+ albums = homeController.getHighestRated(offset, size);
+ } else if ("frequent".equals(type)) {
+ albums = homeController.getMostFrequent(offset, size);
+ } else if ("recent".equals(type)) {
+ albums = homeController.getMostRecent(offset, size);
+ } else if ("newest".equals(type)) {
+ albums = homeController.getNewest(offset, size);
+ } else if ("starred".equals(type)) {
+ albums = homeController.getStarred(offset, size, username);
+ } else if ("alphabeticalByArtist".equals(type)) {
+ albums = homeController.getAlphabetical(offset, size, true);
+ } else if ("alphabeticalByName".equals(type)) {
+ albums = homeController.getAlphabetical(offset, size, false);
+ } else if ("random".equals(type)) {
+ albums = homeController.getRandom(size);
+ } else {
+ throw new Exception("Invalid list type: " + type);
+ }
+
+ for (HomeController.Album album : albums) {
+ MediaFile mediaFile = mediaFileService.getMediaFile(album.getPath());
+ AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
+ builder.add("album", attributes, true);
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void getAlbumList2(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.add("albumList2", false);
+
+ try {
+ int size = ServletRequestUtils.getIntParameter(request, "size", 10);
+ int offset = ServletRequestUtils.getIntParameter(request, "offset", 0);
+ size = Math.max(0, Math.min(size, 500));
+ String type = ServletRequestUtils.getRequiredStringParameter(request, "type");
+ String username = securityService.getCurrentUsername(request);
+
+ List<Album> albums;
+ if ("frequent".equals(type)) {
+ albums = albumDao.getMostFrequentlyPlayedAlbums(offset, size);
+ } else if ("recent".equals(type)) {
+ albums = albumDao.getMostRecentlyPlayedAlbums(offset, size);
+ } else if ("newest".equals(type)) {
+ albums = albumDao.getNewestAlbums(offset, size);
+ } else if ("alphabeticalByArtist".equals(type)) {
+ albums = albumDao.getAlphabetialAlbums(offset, size, true);
+ } else if ("alphabeticalByName".equals(type)) {
+ albums = albumDao.getAlphabetialAlbums(offset, size, false);
+ } else if ("starred".equals(type)) {
+ albums = albumDao.getStarredAlbums(offset, size, securityService.getCurrentUser(request).getUsername());
+ } else if ("random".equals(type)) {
+ albums = searchService.getRandomAlbumsId3(size);
+ } else {
+ throw new Exception("Invalid list type: " + type);
+ }
+ for (Album album : albums) {
+ builder.add("album", createAttributesForAlbum(album, username), true);
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void getRandomSongs(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.add("randomSongs", false);
+
+ try {
+ int size = ServletRequestUtils.getIntParameter(request, "size", 10);
+ size = Math.max(0, Math.min(size, 500));
+ String genre = ServletRequestUtils.getStringParameter(request, "genre");
+ Integer fromYear = ServletRequestUtils.getIntParameter(request, "fromYear");
+ Integer toYear = ServletRequestUtils.getIntParameter(request, "toYear");
+ Integer musicFolderId = ServletRequestUtils.getIntParameter(request, "musicFolderId");
+ RandomSearchCriteria criteria = new RandomSearchCriteria(size, genre, fromYear, toYear, musicFolderId);
+
+ for (MediaFile mediaFile : searchService.getRandomSongs(criteria)) {
+ AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
+ builder.add("song", attributes, true);
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void getVideos(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.add("videos", false);
+ try {
+ int size = ServletRequestUtils.getIntParameter(request, "size", Integer.MAX_VALUE);
+ int offset = ServletRequestUtils.getIntParameter(request, "offset", 0);
+
+ for (MediaFile mediaFile : mediaFileDao.getVideos(size, offset)) {
+ builder.add("video", createAttributesForMediaFile(player, mediaFile, username), true);
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void getNowPlaying(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.add("nowPlaying", false);
+
+ for (TransferStatus status : statusService.getAllStreamStatuses()) {
+
+ Player player = status.getPlayer();
+ File file = status.getFile();
+ if (player != null && player.getUsername() != null && file != null) {
+
+ String username = player.getUsername();
+ UserSettings userSettings = settingsService.getUserSettings(username);
+ if (!userSettings.isNowPlayingAllowed()) {
+ continue;
+ }
+
+ MediaFile mediaFile = mediaFileService.getMediaFile(file);
+
+ long minutesAgo = status.getMillisSinceLastUpdate() / 1000L / 60L;
+ if (minutesAgo < 60) {
+ AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
+ attributes.add("username", username);
+ attributes.add("playerId", player.getId());
+ attributes.add("playerName", player.getName());
+ attributes.add("minutesAgo", minutesAgo);
+ builder.add("entry", attributes, true);
+ }
+ }
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ private AttributeSet createAttributesForMediaFile(Player player, MediaFile mediaFile, String username) {
+ MediaFile parent = mediaFileService.getParentOf(mediaFile);
+ AttributeSet attributes = new AttributeSet();
+ attributes.add("id", mediaFile.getId());
+ try {
+ if (!mediaFileService.isRoot(parent)) {
+ attributes.add("parent", parent.getId());
+ }
+ } catch (SecurityException x) {
+ // Ignored.
+ }
+ attributes.add("title", mediaFile.getName());
+ attributes.add("album", mediaFile.getAlbumName());
+ attributes.add("artist", mediaFile.getArtist());
+ attributes.add("isDir", mediaFile.isDirectory());
+ attributes.add("coverArt", findCoverArt(mediaFile, parent));
+ attributes.add("created", StringUtil.toISO8601(mediaFile.getCreated()));
+ attributes.add("starred", StringUtil.toISO8601(mediaFileDao.getMediaFileStarredDate(mediaFile.getId(), username)));
+ attributes.add("userRating", ratingService.getRatingForUser(username, mediaFile));
+ attributes.add("averageRating", ratingService.getAverageRating(mediaFile));
+
+ if (mediaFile.isFile()) {
+ attributes.add("duration", mediaFile.getDurationSeconds());
+ attributes.add("bitRate", mediaFile.getBitRate());
+ attributes.add("track", mediaFile.getTrackNumber());
+ attributes.add("discNumber", mediaFile.getDiscNumber());
+ attributes.add("year", mediaFile.getYear());
+ attributes.add("genre", mediaFile.getGenre());
+ attributes.add("size", mediaFile.getFileSize());
+ String suffix = mediaFile.getFormat();
+ attributes.add("suffix", suffix);
+ attributes.add("contentType", StringUtil.getMimeType(suffix));
+ attributes.add("isVideo", mediaFile.isVideo());
+ attributes.add("path", getRelativePath(mediaFile));
+
+ if (mediaFile.getArtist() != null && mediaFile.getAlbumName() != null) {
+ Album album = albumDao.getAlbum(mediaFile.getAlbumArtist(), mediaFile.getAlbumName());
+ if (album != null) {
+ attributes.add("albumId", album.getId());
+ }
+ }
+ if (mediaFile.getArtist() != null) {
+ Artist artist = artistDao.getArtist(mediaFile.getArtist());
+ if (artist != null) {
+ attributes.add("artistId", artist.getId());
+ }
+ }
+ switch (mediaFile.getMediaType()) {
+ case MUSIC:
+ attributes.add("type", "music");
+ break;
+ case PODCAST:
+ attributes.add("type", "podcast");
+ break;
+ case AUDIOBOOK:
+ attributes.add("type", "audiobook");
+ break;
+ default:
+ break;
+ }
+
+ if (transcodingService.isTranscodingRequired(mediaFile, player)) {
+ String transcodedSuffix = transcodingService.getSuffix(player, mediaFile, null);
+ attributes.add("transcodedSuffix", transcodedSuffix);
+ attributes.add("transcodedContentType", StringUtil.getMimeType(transcodedSuffix));
+ }
+ }
+ return attributes;
+ }
+
+ private Integer findCoverArt(MediaFile mediaFile, MediaFile parent) {
+ MediaFile dir = mediaFile.isDirectory() ? mediaFile : parent;
+ if (dir != null && dir.getCoverArtPath() != null) {
+ return dir.getId();
+ }
+ return null;
+ }
+
+ private String getRelativePath(MediaFile musicFile) {
+
+ String filePath = musicFile.getPath();
+
+ // Convert slashes.
+ filePath = filePath.replace('\\', '/');
+
+ String filePathLower = filePath.toLowerCase();
+
+ List<MusicFolder> musicFolders = settingsService.getAllMusicFolders(false, true);
+ for (MusicFolder musicFolder : musicFolders) {
+ String folderPath = musicFolder.getPath().getPath();
+ folderPath = folderPath.replace('\\', '/');
+ String folderPathLower = folderPath.toLowerCase();
+
+ if (filePathLower.startsWith(folderPathLower)) {
+ String relativePath = filePath.substring(folderPath.length());
+ return relativePath.startsWith("/") ? relativePath.substring(1) : relativePath;
+ }
+ }
+
+ return null;
+ }
+
+ public ModelAndView download(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ User user = securityService.getCurrentUser(request);
+ if (!user.isDownloadRole()) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to download files.");
+ return null;
+ }
+
+ long ifModifiedSince = request.getDateHeader("If-Modified-Since");
+ long lastModified = downloadController.getLastModified(request);
+
+ if (ifModifiedSince != -1 && lastModified != -1 && lastModified <= ifModifiedSince) {
+ response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
+ return null;
+ }
+
+ if (lastModified != -1) {
+ response.setDateHeader("Last-Modified", lastModified);
+ }
+
+ return downloadController.handleRequest(request, response);
+ }
+
+ public ModelAndView stream(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ User user = securityService.getCurrentUser(request);
+ if (!user.isStreamRole()) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to play files.");
+ return null;
+ }
+
+ streamController.handleRequest(request, response);
+ return null;
+ }
+
+ public void scrobble(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ Player player = playerService.getPlayer(request, response);
+
+ if (!settingsService.getUserSettings(player.getUsername()).isLastFmEnabled()) {
+ error(request, response, ErrorCode.GENERIC, "Scrobbling is not enabled for " + player.getUsername() + ".");
+ return;
+ }
+
+ try {
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
+ MediaFile file = mediaFileService.getMediaFile(id);
+ if (file == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "File not found: " + id);
+ return;
+ }
+ boolean submission = ServletRequestUtils.getBooleanParameter(request, "submission", true);
+ audioScrobblerService.register(file, player.getUsername(), submission);
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ return;
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void star(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ starOrUnstar(request, response, true);
+ }
+
+ public void unstar(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ starOrUnstar(request, response, false);
+ }
+
+ private void starOrUnstar(HttpServletRequest request, HttpServletResponse response, boolean star) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ try {
+ String username = securityService.getCurrentUser(request).getUsername();
+ for (int id : ServletRequestUtils.getIntParameters(request, "id")) {
+ MediaFile mediaFile = mediaFileDao.getMediaFile(id);
+ if (mediaFile == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "Media file not found: " + id);
+ return;
+ }
+ if (star) {
+ mediaFileDao.starMediaFile(id, username);
+ } else {
+ mediaFileDao.unstarMediaFile(id, username);
+ }
+ }
+ for (int albumId : ServletRequestUtils.getIntParameters(request, "albumId")) {
+ Album album = albumDao.getAlbum(albumId);
+ if (album == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "Album not found: " + albumId);
+ return;
+ }
+ if (star) {
+ albumDao.starAlbum(albumId, username);
+ } else {
+ albumDao.unstarAlbum(albumId, username);
+ }
+ }
+ for (int artistId : ServletRequestUtils.getIntParameters(request, "artistId")) {
+ Artist artist = artistDao.getArtist(artistId);
+ if (artist == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "Artist not found: " + artistId);
+ return;
+ }
+ if (star) {
+ artistDao.starArtist(artistId, username);
+ } else {
+ artistDao.unstarArtist(artistId, username);
+ }
+ }
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ return;
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getStarred(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.add("starred", false);
+ for (MediaFile artist : mediaFileDao.getStarredDirectories(0, Integer.MAX_VALUE, username)) {
+ builder.add("artist", true,
+ new Attribute("name", artist.getName()),
+ new Attribute("id", artist.getId()));
+ }
+ for (MediaFile album : mediaFileDao.getStarredAlbums(0, Integer.MAX_VALUE, username)) {
+ builder.add("album", createAttributesForMediaFile(player, album, username), true);
+ }
+ for (MediaFile song : mediaFileDao.getStarredFiles(0, Integer.MAX_VALUE, username)) {
+ builder.add("song", createAttributesForMediaFile(player, song, username), true);
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getStarred2(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.add("starred2", false);
+ for (Artist artist : artistDao.getStarredArtists(0, Integer.MAX_VALUE, username)) {
+ builder.add("artist", createAttributesForArtist(artist, username), true);
+ }
+ for (Album album : albumDao.getStarredAlbums(0, Integer.MAX_VALUE, username)) {
+ builder.add("album", createAttributesForAlbum(album, username), true);
+ }
+ for (MediaFile song : mediaFileDao.getStarredFiles(0, Integer.MAX_VALUE, username)) {
+ builder.add("song", createAttributesForMediaFile(player, song, username), true);
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getPodcasts(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ builder.add("podcasts", false);
+
+ for (PodcastChannel channel : podcastService.getAllChannels()) {
+ AttributeSet channelAttrs = new AttributeSet();
+ channelAttrs.add("id", channel.getId());
+ channelAttrs.add("url", channel.getUrl());
+ channelAttrs.add("status", channel.getStatus().toString().toLowerCase());
+ channelAttrs.add("title", channel.getTitle());
+ channelAttrs.add("description", channel.getDescription());
+ channelAttrs.add("errorMessage", channel.getErrorMessage());
+ builder.add("channel", channelAttrs, false);
+
+ List<PodcastEpisode> episodes = podcastService.getEpisodes(channel.getId(), false);
+ for (PodcastEpisode episode : episodes) {
+ AttributeSet episodeAttrs = new AttributeSet();
+
+ String path = episode.getPath();
+ if (path != null) {
+ MediaFile mediaFile = mediaFileService.getMediaFile(path);
+ episodeAttrs.addAll(createAttributesForMediaFile(player, mediaFile, username));
+ episodeAttrs.add("streamId", mediaFile.getId());
+ }
+
+ episodeAttrs.add("id", episode.getId()); // Overwrites the previous "id" attribute.
+ episodeAttrs.add("status", episode.getStatus().toString().toLowerCase());
+ episodeAttrs.add("title", episode.getTitle());
+ episodeAttrs.add("description", episode.getDescription());
+ episodeAttrs.add("publishDate", episode.getPublishDate());
+
+ builder.add("episode", episodeAttrs, true);
+ }
+
+ builder.end(); // <channel>
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void getShares(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ User user = securityService.getCurrentUser(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ builder.add("shares", false);
+ for (Share share : shareService.getSharesForUser(user)) {
+ builder.add("share", createAttributesForShare(share), false);
+
+ for (MediaFile mediaFile : shareService.getSharedFiles(share.getId())) {
+ AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
+ builder.add("entry", attributes, true);
+ }
+
+ builder.end();
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void createShare(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ Player player = playerService.getPlayer(request, response);
+ String username = securityService.getCurrentUsername(request);
+
+ User user = securityService.getCurrentUser(request);
+ if (!user.isShareRole()) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to share media.");
+ return;
+ }
+
+ if (!settingsService.isUrlRedirectionEnabled()) {
+ error(request, response, ErrorCode.GENERIC, "Sharing is only supported for *.subsonic.org domain names.");
+ return;
+ }
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ try {
+
+ List<MediaFile> files = new ArrayList<MediaFile>();
+ for (int id : ServletRequestUtils.getRequiredIntParameters(request, "id")) {
+ files.add(mediaFileService.getMediaFile(id));
+ }
+
+ // TODO: Update api.jsp
+
+ Share share = shareService.createShare(request, files);
+ share.setDescription(request.getParameter("description"));
+ long expires = ServletRequestUtils.getLongParameter(request, "expires", 0L);
+ if (expires != 0) {
+ share.setExpires(new Date(expires));
+ }
+ shareService.updateShare(share);
+
+ builder.add("shares", false);
+ builder.add("share", createAttributesForShare(share), false);
+
+ for (MediaFile mediaFile : shareService.getSharedFiles(share.getId())) {
+ AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
+ builder.add("entry", attributes, true);
+ }
+
+ builder.endAll();
+ response.getWriter().print(builder);
+
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void deleteShare(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ try {
+ request = wrapRequest(request);
+ User user = securityService.getCurrentUser(request);
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
+
+ Share share = shareService.getShareById(id);
+ if (share == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "Shared media not found.");
+ return;
+ }
+ if (!user.isAdminRole() && !share.getUsername().equals(user.getUsername())) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, "Not authorized to delete shared media.");
+ return;
+ }
+
+ shareService.deleteShare(id);
+ XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
+ response.getWriter().print(builder);
+
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void updateShare(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ try {
+ request = wrapRequest(request);
+ User user = securityService.getCurrentUser(request);
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
+
+ Share share = shareService.getShareById(id);
+ if (share == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "Shared media not found.");
+ return;
+ }
+ if (!user.isAdminRole() && !share.getUsername().equals(user.getUsername())) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, "Not authorized to modify shared media.");
+ return;
+ }
+
+ share.setDescription(request.getParameter("description"));
+ String expiresString = request.getParameter("expires");
+ if (expiresString != null) {
+ long expires = Long.parseLong(expiresString);
+ share.setExpires(expires == 0L ? null : new Date(expires));
+ }
+ shareService.updateShare(share);
+ XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
+ response.getWriter().print(builder);
+
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ private List<Attribute> createAttributesForShare(Share share) {
+ List<Attribute> attributes = new ArrayList<Attribute>();
+
+ attributes.add(new Attribute("id", share.getId()));
+ attributes.add(new Attribute("url", shareService.getShareUrl(share)));
+ attributes.add(new Attribute("username", share.getUsername()));
+ attributes.add(new Attribute("created", StringUtil.toISO8601(share.getCreated())));
+ attributes.add(new Attribute("visitCount", share.getVisitCount()));
+ attributes.add(new Attribute("description", share.getDescription()));
+ attributes.add(new Attribute("expires", StringUtil.toISO8601(share.getExpires())));
+ attributes.add(new Attribute("lastVisited", StringUtil.toISO8601(share.getLastVisited())));
+
+ return attributes;
+ }
+
+ public ModelAndView videoPlayer(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+
+ Map<String, Object> map = new HashMap<String, Object>();
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
+ MediaFile file = mediaFileService.getMediaFile(id);
+
+ int timeOffset = ServletRequestUtils.getIntParameter(request, "timeOffset", 0);
+ timeOffset = Math.max(0, timeOffset);
+ Integer duration = file.getDurationSeconds();
+ if (duration != null) {
+ map.put("skipOffsets", VideoPlayerController.createSkipOffsets(duration));
+ timeOffset = Math.min(duration, timeOffset);
+ duration -= timeOffset;
+ }
+
+ map.put("id", request.getParameter("id"));
+ map.put("u", request.getParameter("u"));
+ map.put("p", request.getParameter("p"));
+ map.put("c", request.getParameter("c"));
+ map.put("v", request.getParameter("v"));
+ map.put("video", file);
+ map.put("maxBitRate", ServletRequestUtils.getIntParameter(request, "maxBitRate", VideoPlayerController.DEFAULT_BIT_RATE));
+ map.put("duration", duration);
+ map.put("timeOffset", timeOffset);
+ map.put("bitRates", VideoPlayerController.BIT_RATES);
+ map.put("autoplay", ServletRequestUtils.getBooleanParameter(request, "autoplay", true));
+
+ ModelAndView result = new ModelAndView("rest/videoPlayer");
+ result.addObject("model", map);
+ return result;
+ }
+
+ public ModelAndView getCoverArt(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ return coverArtController.handleRequest(request, response);
+ }
+
+ public ModelAndView getAvatar(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ return avatarController.handleRequest(request, response);
+ }
+
+ public void changePassword(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ try {
+
+ String username = ServletRequestUtils.getRequiredStringParameter(request, "username");
+ String password = decrypt(ServletRequestUtils.getRequiredStringParameter(request, "password"));
+
+ User authUser = securityService.getCurrentUser(request);
+ if (!authUser.isAdminRole() && !username.equals(authUser.getUsername())) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, authUser.getUsername() + " is not authorized to change password for " + username);
+ return;
+ }
+
+ User user = securityService.getUserByName(username);
+ user.setPassword(password);
+ securityService.updateUser(user);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
+ response.getWriter().print(builder);
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void getUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+
+ String username;
+ try {
+ username = ServletRequestUtils.getRequiredStringParameter(request, "username");
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ return;
+ }
+
+ User currentUser = securityService.getCurrentUser(request);
+ if (!username.equals(currentUser.getUsername()) && !currentUser.isAdminRole()) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, currentUser.getUsername() + " is not authorized to get details for other users.");
+ return;
+ }
+
+ User requestedUser = securityService.getUserByName(username);
+ if (requestedUser == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "No such user: " + username);
+ return;
+ }
+
+ UserSettings userSettings = settingsService.getUserSettings(username);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ List<Attribute> attributes = Arrays.asList(
+ new Attribute("username", requestedUser.getUsername()),
+ new Attribute("email", requestedUser.getEmail()),
+ new Attribute("scrobblingEnabled", userSettings.isLastFmEnabled()),
+ new Attribute("adminRole", requestedUser.isAdminRole()),
+ new Attribute("settingsRole", requestedUser.isSettingsRole()),
+ new Attribute("downloadRole", requestedUser.isDownloadRole()),
+ new Attribute("uploadRole", requestedUser.isUploadRole()),
+ new Attribute("playlistRole", true), // Since 1.8.0
+ new Attribute("coverArtRole", requestedUser.isCoverArtRole()),
+ new Attribute("commentRole", requestedUser.isCommentRole()),
+ new Attribute("podcastRole", requestedUser.isPodcastRole()),
+ new Attribute("streamRole", requestedUser.isStreamRole()),
+ new Attribute("jukeboxRole", requestedUser.isJukeboxRole()),
+ new Attribute("shareRole", requestedUser.isShareRole())
+ );
+
+ builder.add("user", attributes, true);
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void createUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ User user = securityService.getCurrentUser(request);
+ if (!user.isAdminRole()) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to create new users.");
+ return;
+ }
+
+ try {
+ UserSettingsCommand command = new UserSettingsCommand();
+ command.setUsername(ServletRequestUtils.getRequiredStringParameter(request, "username"));
+ command.setPassword(decrypt(ServletRequestUtils.getRequiredStringParameter(request, "password")));
+ command.setEmail(ServletRequestUtils.getRequiredStringParameter(request, "email"));
+ command.setLdapAuthenticated(ServletRequestUtils.getBooleanParameter(request, "ldapAuthenticated", false));
+ command.setAdminRole(ServletRequestUtils.getBooleanParameter(request, "adminRole", false));
+ command.setCommentRole(ServletRequestUtils.getBooleanParameter(request, "commentRole", false));
+ command.setCoverArtRole(ServletRequestUtils.getBooleanParameter(request, "coverArtRole", false));
+ command.setDownloadRole(ServletRequestUtils.getBooleanParameter(request, "downloadRole", false));
+ command.setStreamRole(ServletRequestUtils.getBooleanParameter(request, "streamRole", true));
+ command.setUploadRole(ServletRequestUtils.getBooleanParameter(request, "uploadRole", false));
+ command.setJukeboxRole(ServletRequestUtils.getBooleanParameter(request, "jukeboxRole", false));
+ command.setPodcastRole(ServletRequestUtils.getBooleanParameter(request, "podcastRole", false));
+ command.setSettingsRole(ServletRequestUtils.getBooleanParameter(request, "settingsRole", true));
+ command.setTranscodeSchemeName(ServletRequestUtils.getStringParameter(request, "transcodeScheme", TranscodeScheme.OFF.name()));
+ command.setShareRole(ServletRequestUtils.getBooleanParameter(request, "shareRole", false));
+
+ userSettingsController.createUser(command);
+ XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
+ response.getWriter().print(builder);
+
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void deleteUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ User user = securityService.getCurrentUser(request);
+ if (!user.isAdminRole()) {
+ error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to delete users.");
+ return;
+ }
+
+ try {
+ String username = ServletRequestUtils.getRequiredStringParameter(request, "username");
+ securityService.deleteUser(username);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
+ response.getWriter().print(builder);
+
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ } catch (Exception x) {
+ LOG.warn("Error in REST API.", x);
+ error(request, response, ErrorCode.GENERIC, getErrorMessage(x));
+ }
+ }
+
+ public void getChatMessages(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+
+ long since = ServletRequestUtils.getLongParameter(request, "since", 0L);
+
+ builder.add("chatMessages", false);
+
+ for (ChatService.Message message : chatService.getMessages(0L).getMessages()) {
+ long time = message.getDate().getTime();
+ if (time > since) {
+ builder.add("chatMessage", true, new Attribute("username", message.getUsername()),
+ new Attribute("time", time), new Attribute("message", message.getContent()));
+ }
+ }
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void addChatMessage(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ try {
+ chatService.doAddMessage(ServletRequestUtils.getRequiredStringParameter(request, "message"), request);
+ XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
+ response.getWriter().print(builder);
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ }
+ }
+
+ public void getLyrics(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ String artist = request.getParameter("artist");
+ String title = request.getParameter("title");
+ LyricsInfo lyrics = lyricsService.getLyrics(artist, title);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true);
+ AttributeSet attributes = new AttributeSet();
+ attributes.add("artist", lyrics.getArtist());
+ attributes.add("title", lyrics.getTitle());
+ builder.add("lyrics", attributes, lyrics.getLyrics(), true);
+
+ builder.endAll();
+ response.getWriter().print(builder);
+ }
+
+ public void setRating(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ request = wrapRequest(request);
+ try {
+ Integer rating = ServletRequestUtils.getRequiredIntParameter(request, "rating");
+ if (rating == 0) {
+ rating = null;
+ }
+
+ int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
+ MediaFile mediaFile = mediaFileService.getMediaFile(id);
+ if (mediaFile == null) {
+ error(request, response, ErrorCode.NOT_FOUND, "File not found: " + id);
+ return;
+ }
+
+ String username = securityService.getCurrentUsername(request);
+ ratingService.setRatingForUser(username, mediaFile, rating);
+
+ XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
+ response.getWriter().print(builder);
+ } catch (ServletRequestBindingException x) {
+ error(request, response, ErrorCode.MISSING_PARAMETER, getErrorMessage(x));
+ }
+ }
+
+ private HttpServletRequest wrapRequest(HttpServletRequest request) {
+ return wrapRequest(request, false);
+ }
+
+ private HttpServletRequest wrapRequest(final HttpServletRequest request, boolean jukebox) {
+ final String playerId = createPlayerIfNecessary(request, jukebox);
+ return new HttpServletRequestWrapper(request) {
+ @Override
+ public String getParameter(String name) {
+ // Returns the correct player to be used in PlayerService.getPlayer()
+ if ("player".equals(name)) {
+ return playerId;
+ }
+
+ // Support old style ID parameters.
+ if ("id".equals(name)) {
+ return mapId(request.getParameter("id"));
+ }
+
+ return super.getParameter(name);
+ }
+ };
+ }
+
+ private String mapId(String id) {
+ if (id == null || id.startsWith(CoverArtController.ALBUM_COVERART_PREFIX) ||
+ id.startsWith(CoverArtController.ARTIST_COVERART_PREFIX) || StringUtils.isNumeric(id)) {
+ return id;
+ }
+
+ try {
+ String path = StringUtil.utf8HexDecode(id);
+ MediaFile mediaFile = mediaFileService.getMediaFile(path);
+ return String.valueOf(mediaFile.getId());
+ } catch (Exception x) {
+ return id;
+ }
+ }
+
+ private String getErrorMessage(Exception x) {
+ if (x.getMessage() != null) {
+ return x.getMessage();
+ }
+ return x.getClass().getSimpleName();
+ }
+
+ private void error(HttpServletRequest request, HttpServletResponse response, ErrorCode code, String message) throws IOException {
+ XMLBuilder builder = createXMLBuilder(request, response, false);
+ builder.add("error", true,
+ new XMLBuilder.Attribute("code", code.getCode()),
+ new XMLBuilder.Attribute("message", message));
+ builder.end();
+ response.getWriter().print(builder);
+ }
+
+ private XMLBuilder createXMLBuilder(HttpServletRequest request, HttpServletResponse response, boolean ok) throws IOException {
+ String format = ServletRequestUtils.getStringParameter(request, "f", "xml");
+ boolean json = "json".equals(format);
+ boolean jsonp = "jsonp".equals(format);
+ XMLBuilder builder;
+
+ response.setCharacterEncoding(StringUtil.ENCODING_UTF8);
+
+ if (json) {
+ builder = XMLBuilder.createJSONBuilder();
+ response.setContentType("application/json");
+ } else if (jsonp) {
+ builder = XMLBuilder.createJSONPBuilder(request.getParameter("callback"));
+ response.setContentType("text/javascript");
+ } else {
+ builder = XMLBuilder.createXMLBuilder();
+ response.setContentType("text/xml");
+ }
+
+ builder.preamble(StringUtil.ENCODING_UTF8);
+ builder.add("subsonic-response", false,
+ new Attribute("xmlns", "http://subsonic.org/restapi"),
+ new Attribute("status", ok ? "ok" : "failed"),
+ new Attribute("version", StringUtil.getRESTProtocolVersion()));
+ return builder;
+ }
+
+ private String createPlayerIfNecessary(HttpServletRequest request, boolean jukebox) {
+ String username = request.getRemoteUser();
+ String clientId = request.getParameter("c");
+ if (jukebox) {
+ clientId += "-jukebox";
+ }
+
+ List<Player> players = playerService.getPlayersForUserAndClientId(username, clientId);
+
+ // If not found, create it.
+ if (players.isEmpty()) {
+ Player player = new Player();
+ player.setIpAddress(request.getRemoteAddr());
+ player.setUsername(username);
+ player.setClientId(clientId);
+ player.setName(clientId);
+ player.setTechnology(jukebox ? PlayerTechnology.JUKEBOX : PlayerTechnology.EXTERNAL_WITH_PLAYLIST);
+ playerService.createPlayer(player);
+ players = playerService.getPlayersForUserAndClientId(username, clientId);
+ }
+
+ // Return the player ID.
+ return !players.isEmpty() ? players.get(0).getId() : null;
+ }
+
+ public void setSettingsService(SettingsService settingsService) {
+ this.settingsService = settingsService;
+ }
+
+ public void setSecurityService(SecurityService securityService) {
+ this.securityService = securityService;
+ }
+
+ public void setPlayerService(PlayerService playerService) {
+ this.playerService = playerService;
+ }
+
+ public void setTranscodingService(TranscodingService transcodingService) {
+ this.transcodingService = transcodingService;
+ }
+
+ public void setDownloadController(DownloadController downloadController) {
+ this.downloadController = downloadController;
+ }
+
+ public void setCoverArtController(CoverArtController coverArtController) {
+ this.coverArtController = coverArtController;
+ }
+
+ public void setUserSettingsController(UserSettingsController userSettingsController) {
+ this.userSettingsController = userSettingsController;
+ }
+
+ public void setLeftController(LeftController leftController) {
+ this.leftController = leftController;
+ }
+
+ public void setStatusService(StatusService statusService) {
+ this.statusService = statusService;
+ }
+
+ public void setPlaylistService(PlaylistService playlistService) {
+ this.playlistService = playlistService;
+ }
+
+ public void setStreamController(StreamController streamController) {
+ this.streamController = streamController;
+ }
+
+ public void setChatService(ChatService chatService) {
+ this.chatService = chatService;
+ }
+
+ public void setHomeController(HomeController homeController) {
+ this.homeController = homeController;
+ }
+
+ public void setLyricsService(LyricsService lyricsService) {
+ this.lyricsService = lyricsService;
+ }
+
+ public void setPlayQueueService(PlayQueueService playQueueService) {
+ this.playQueueService = playQueueService;
+ }
+
+ public void setJukeboxService(JukeboxService jukeboxService) {
+ this.jukeboxService = jukeboxService;
+ }
+
+ public void setAudioScrobblerService(AudioScrobblerService audioScrobblerService) {
+ this.audioScrobblerService = audioScrobblerService;
+ }
+
+ public void setPodcastService(PodcastService podcastService) {
+ this.podcastService = podcastService;
+ }
+
+ public void setRatingService(RatingService ratingService) {
+ this.ratingService = ratingService;
+ }
+
+ public void setSearchService(SearchService searchService) {
+ this.searchService = searchService;
+ }
+
+ public void setShareService(ShareService shareService) {
+ this.shareService = shareService;
+ }
+
+ public void setMediaFileService(MediaFileService mediaFileService) {
+ this.mediaFileService = mediaFileService;
+ }
+
+ public void setAvatarController(AvatarController avatarController) {
+ this.avatarController = avatarController;
+ }
+
+ public void setArtistDao(ArtistDao artistDao) {
+ this.artistDao = artistDao;
+ }
+
+ public void setAlbumDao(AlbumDao albumDao) {
+ this.albumDao = albumDao;
+ }
+
+ public void setMediaFileDao(MediaFileDao mediaFileDao) {
+ this.mediaFileDao = mediaFileDao;
+ }
+
+ public static enum ErrorCode {
+
+ GENERIC(0, "A generic error."),
+ MISSING_PARAMETER(10, "Required parameter is missing."),
+ PROTOCOL_MISMATCH_CLIENT_TOO_OLD(20, "Incompatible Subsonic REST protocol version. Client must upgrade."),
+ PROTOCOL_MISMATCH_SERVER_TOO_OLD(30, "Incompatible Subsonic REST protocol version. Server must upgrade."),
+ NOT_AUTHENTICATED(40, "Wrong username or password."),
+ NOT_AUTHORIZED(50, "User is not authorized for the given operation."),
+ NOT_LICENSED(60, "The trial period for the Subsonic server is over. Please donate to get a license key. Visit subsonic.org for details."),
+ NOT_FOUND(70, "Requested data was not found.");
+
+ private final int code;
+ private final String message;
+
+ ErrorCode(int code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+ }
+}