/*
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.Random;
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 org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
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.ajax.PlayQueueService;
import net.sourceforge.subsonic.command.UserSettingsCommand;
import net.sourceforge.subsonic.controller.ShareSettingsController.ShareInfo;
import net.sourceforge.subsonic.dao.AlbumDao;
import net.sourceforge.subsonic.dao.ArtistDao;
import net.sourceforge.subsonic.dao.BookmarkDao;
import net.sourceforge.subsonic.dao.MediaFileDao;
import net.sourceforge.subsonic.domain.Album;
import net.sourceforge.subsonic.domain.Artist;
import net.sourceforge.subsonic.domain.Bookmark;
import net.sourceforge.subsonic.domain.InternetRadio;
import net.sourceforge.subsonic.domain.MediaFile;
import net.sourceforge.subsonic.domain.MusicFolder;
import net.sourceforge.subsonic.domain.MusicIndex;
import net.sourceforge.subsonic.domain.PlayQueue;
import net.sourceforge.subsonic.domain.Player;
import net.sourceforge.subsonic.domain.PlayerTechnology;
import net.sourceforge.subsonic.domain.Playlist;
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.HotService;
import net.sourceforge.subsonic.service.JukeboxService;
import net.sourceforge.subsonic.service.MediaFileService;
import net.sourceforge.subsonic.service.MediaScannerService;
import net.sourceforge.subsonic.service.MusicIndexService;
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.Pair;
import net.sourceforge.subsonic.util.StringUtil;
import net.sourceforge.subsonic.util.XMLBuilder;
import static net.sourceforge.subsonic.domain.MediaFile.MediaType.MUSIC;
import java.util.concurrent.ConcurrentHashMap;
import static net.sourceforge.subsonic.security.RESTRequestParameterProcessingFilter.decrypt;
import static net.sourceforge.subsonic.util.XMLBuilder.Attribute;
import static net.sourceforge.subsonic.util.XMLBuilder.AttributeSet;
import static org.springframework.web.bind.ServletRequestUtils.*;
/**
* Multi-controller used for the REST API.
* <p/>
* For documentation, please refer to api.jsp.
* <p/>
* Note: Exceptions thrown from the methods are intercepted by RESTFilter.
*
* @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 MediaScannerService mediaScannerService;
private MediaFileService mediaFileService;
private MusicIndexService musicIndexService;
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 HLSController hlsController;
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 HotService hotService;
private MediaFileDao mediaFileDao;
private ArtistDao artistDao;
private AlbumDao albumDao;
private BookmarkDao bookmarkDao;
private final Map<BookmarkKey, Bookmark> bookmarkCache = new ConcurrentHashMap<BookmarkKey, Bookmark>();
public void init() {
refreshBookmarkCache();
}
private void refreshBookmarkCache() {
bookmarkCache.clear();
for (Bookmark bookmark : bookmarkDao.getBookmarks()) {
bookmarkCache.put(BookmarkKey.forBookmark(bookmark), bookmark);
}
}
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);
int userGroupId = securityService.getCurrentUserGroupId(request);
for (MusicFolder musicFolder : settingsService.getAllMusicFolders(userGroupId, settingsService.isSortMediaFileFolder())) {
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);
String username = securityService.getCurrentUser(request).getUsername();
int userGroupId = securityService.getCurrentUserGroupId(request);
long ifModifiedSince = getLongParameter(request, "ifModifiedSince", 0L);
long lastModified = leftController.getLastModified(request);
if (lastModified <= ifModifiedSince) {
builder.endAll();
response.getWriter().print(builder);
return;
}
builder.add("indexes", false, new Attribute("lastModified", lastModified),
new Attribute("ignoredArticles", settingsService.getIgnoredArticles()));
List<MusicFolder> musicFolders = settingsService.getAllMusicFolders(userGroupId, settingsService.isSortMediaFileFolder());
Integer musicFolderId = 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", createAttributesForArtist(shortcut, username), true);
}
SortedMap<MusicIndex, SortedSet<MusicIndex.SortableArtistforGenre>> indexedArtists =
leftController.getMusicFolderContent(musicFolders,null, false).getIndexedArtists();
for (Map.Entry<MusicIndex, SortedSet<MusicIndex.SortableArtistforGenre>> entry : indexedArtists.entrySet()) {
builder.add("index", "name", entry.getKey().getIndex(), false);
for (MusicIndex.SortableArtistforGenre artist : entry.getValue()) {
for (MediaFile mediaFile : artist.getMediaFiles()) {
if (mediaFile.isDirectory()) {
Date starredDate = mediaFileDao.getMediaFileStarredDate(mediaFile.getId(), username);
builder.add("artist", true,
new Attribute("name", artist.getName()),
new Attribute("id", mediaFile.getId()),
new Attribute("starred", StringUtil.toISO8601(starredDate)));
}
}
}
builder.end();
}
// Add children
Player player = playerService.getPlayer(request, response);
List<MediaFile> singleSongs = leftController.getSingleSongs(musicFolders, false);
for (MediaFile singleSong : singleSongs) {
builder.add("child", createAttributesForMediaFile(player, singleSong, username), true);
}
builder.endAll();
response.getWriter().print(builder);
}
public void getGenres(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("genres", false);
for (String genre : mediaFileDao.getGenres()) {
genre = StringEscapeUtils.escapeXml(genre);
builder.add("genre", (Iterable<Attribute>) null, genre, true);
}
builder.endAll();
response.getWriter().print(builder);
}
public void getArtistGenres(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("artistsgenres", false);
for (String genre : mediaFileDao.getArtistGenres()) {
genre = StringEscapeUtils.escapeXml(genre);
builder.add("genre", (Iterable<Attribute>) null, genre, true);
}
builder.endAll();
response.getWriter().print(builder);
}
public void getArtistsByGenre(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("artistsByGenre", false);
String genre = getRequiredStringParameter(request, "genre");
int offset = getIntParameter(request, "offset", 0);
int count = getIntParameter(request, "count", 10);
count = Math.max(0, Math.min(count, 500));
for (MediaFile mediaFile : mediaFileDao.getArtistsByGenre(genre, offset, count)) {
AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
builder.add("song", attributes, true);
}
builder.endAll();
response.getWriter().print(builder);
}
public void getSongsByGenre(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("songsByGenre", false);
String genre = getRequiredStringParameter(request, "genre");
int offset = getIntParameter(request, "offset", 0);
int count = getIntParameter(request, "count", 10);
count = Math.max(0, Math.min(count, 500));
for (MediaFile mediaFile : mediaFileDao.getSongsByGenre(genre, offset, count)) {
AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
builder.add("song", attributes, 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", "ignoredArticles", settingsService.getIgnoredArticles(), false);
List<Artist> artists = artistDao.getAlphabetialArtists(0, Integer.MAX_VALUE);
SortedMap<MusicIndex, SortedSet<MusicIndex.SortableArtistWithArtist>> indexedArtists = musicIndexService.getIndexedArtists(artists, 1); //TODO: fixed Default Index
for (Map.Entry<MusicIndex, SortedSet<MusicIndex.SortableArtistWithArtist>> entry : indexedArtists.entrySet()) {
builder.add("index", "name", entry.getKey().getIndex(), false);
for (MusicIndex.SortableArtistWithArtist sortableArtist : entry.getValue()) {
AttributeSet attributes = createAttributesForArtist(sortableArtist.getArtist(), username);
builder.add("artist", attributes, true);
}
builder.end();
}
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());
attributes.add("genre", artist.getGenre());
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;
}
private AttributeSet createAttributesForArtist(MediaFile artist, String username) {
AttributeSet attributes = new AttributeSet();
attributes.add("id", artist.getId());
attributes.add("name", artist.getName());
attributes.add("starred", StringUtil.toISO8601(mediaFileDao.getMediaFileStarredDate(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);
int id = getRequiredIntParameter(request, "id");
Artist artist = artistDao.getArtist(id);
if (artist == null) {
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)));
attributes.add("year", album.getYear());
attributes.add("genre", album.getGenre());
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);
int id = getRequiredIntParameter(request, "id");
Album album = albumDao.getAlbum(id);
if (album == null) {
error(request, response, ErrorCode.NOT_FOUND, "Album not found.");
return;
}
builder.add("album", createAttributesForAlbum(album, username), false);
//TODO:REST albumname
for (MediaFile mediaFile : mediaFileDao.getSongsForAlbum(album.getArtist(), album.getNameid3())) {
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);
int id = getRequiredIntParameter(request, "id");
MediaFile song = mediaFileDao.getMediaFile(id);
if (song == null || song.isDirectory()) {
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);
int userGroupId = securityService.getCurrentUserGroupId(request);
int id = getRequiredIntParameter(request, "id");
MediaFile dir = mediaFileService.getMediaFile(id, userGroupId);
if (dir == null) {
error(request, response, ErrorCode.NOT_FOUND, "Directory not found");
return;
}
MediaFile parent = mediaFileService.getParentOf(dir);
AttributeSet attributes = new AttributeSet();
attributes.add("id", id);
try {
if (!mediaFileService.isRoot(parent)) {
attributes.add("parent", parent.getId());
}
} catch (SecurityException x) {
// Ignored.
}
attributes.add("name", dir.getName());
attributes.add("starred", StringUtil.toISO8601(mediaFileDao.getMediaFileStarredDate(id, username)));
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("directory", attributes, false);
for (MediaFile child : mediaFileService.getChildrenOf(dir, true, true, true)) {
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);
int userGroupId = securityService.getCurrentUserGroupId(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(getIntParameter(request, "count", 20));
criteria.setOffset(getIntParameter(request, "offset", 0));
SearchResult result = searchService.search(criteria, SearchService.IndexType.SONG, userGroupId);
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);
int userGroupId = securityService.getCurrentUserGroupId(request);
builder.add("searchResult2", false);
String query = request.getParameter("query");
SearchCriteria criteria = new SearchCriteria();
criteria.setQuery(StringUtils.trimToEmpty(query));
criteria.setCount(getIntParameter(request, "artistCount", 20));
criteria.setOffset(getIntParameter(request, "artistOffset", 0));
SearchResult artists = searchService.search(criteria, SearchService.IndexType.ARTIST, userGroupId);
for (MediaFile mediaFile : artists.getMediaFiles()) {
builder.add("artist", createAttributesForArtist(mediaFile, username), true);
}
criteria.setCount(getIntParameter(request, "albumCount", 20));
criteria.setOffset(getIntParameter(request, "albumOffset", 0));
SearchResult albums = searchService.search(criteria, SearchService.IndexType.ALBUM, userGroupId);
for (MediaFile mediaFile : albums.getMediaFiles()) {
AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
builder.add("album", attributes, true);
}
criteria.setCount(getIntParameter(request, "songCount", 20));
criteria.setOffset(getIntParameter(request, "songOffset", 0));
SearchResult songs = searchService.search(criteria, SearchService.IndexType.SONG, userGroupId);
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);
int userGroupId = securityService.getCurrentUserGroupId(request);
builder.add("searchResult3", false);
String query = request.getParameter("query");
SearchCriteria criteria = new SearchCriteria();
criteria.setQuery(StringUtils.trimToEmpty(query));
criteria.setCount(getIntParameter(request, "artistCount", 20));
criteria.setOffset(getIntParameter(request, "artistOffset", 0));
SearchResult searchResult = searchService.search(criteria, SearchService.IndexType.ARTIST_ID3, userGroupId);
for (Artist artist : searchResult.getArtists()) {
builder.add("artist", createAttributesForArtist(artist, username), true);
}
criteria.setCount(getIntParameter(request, "albumCount", 20));
criteria.setOffset(getIntParameter(request, "albumOffset", 0));
searchResult = searchService.search(criteria, SearchService.IndexType.ALBUM_ID3, userGroupId);
for (Album album : searchResult.getAlbums()) {
builder.add("album", createAttributesForAlbum(album, username), true);
}
criteria.setCount(getIntParameter(request, "songCount", 20));
criteria.setOffset(getIntParameter(request, "songOffset", 0));
searchResult = searchService.search(criteria, SearchService.IndexType.SONG, userGroupId);
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);
int id = 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);
}
public void scanstatus(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request, true);
User user = securityService.getCurrentUser(request);
if (!user.isAdminRole()) {
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to get Status.");
return;
}
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("Scan", null, false);
int Counter = mediaScannerService.getScanCount();
if (mediaScannerService.isScanning() == true) {
builder.add("Status", false, new Attribute("started", true), new Attribute("count", Counter) );}
else { builder.add("Status", false, new Attribute("started", false) ); }
builder.endAll();
response.getWriter().print(builder);
}
public void startRescan(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request, true);
User user = securityService.getCurrentUser(request);
if (!user.isAdminRole()) {
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to rescan.");
return;
}
Date lastScan = settingsService.getLastScanned();
mediaScannerService.scanLibrary();
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("Scan", null, false);
if (mediaScannerService.isScanning() == true) {
AttributeSet attributes = new AttributeSet();
attributes.add("running", mediaScannerService.isScanning());
attributes.add("count", mediaScannerService.getScanCount());
builder.add("Status", attributes, false);
builder.end();
AttributeSet attributesStatus = new AttributeSet();
builder.add("Scan", attributesStatus, false);
//FIXME: ScanDate
// attributes.add("lastScan", "xxxxx"); ///lastScan.toString());
// attributes.add("activeScan", mediaFileService.(mediaFileDao) "14.01.2013 12:30");
} else
{ builder.add("Status", false, new Attribute("started", false) ); }
builder.endAll();
response.getWriter().print(builder);
}
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;
}
boolean returnPlaylist = false;
String action = 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 = getRequiredIntParameter(request, "index");
int offset = getIntParameter(request, "offset", 0);
playQueueService.doSkip(request, response, index, offset);
} else if ("add".equals(action)) {
int[] ids = getIntParameters(request, "id");
playQueueService.doAdd(request, response, ids, null);
} else if ("set".equals(action)) {
int[] ids = getIntParameters(request, "id");
playQueueService.doSet(request, response, ids);
} else if ("clear".equals(action)) {
playQueueService.doClear(request, response);
} else if ("remove".equals(action)) {
int index = getRequiredIntParameter(request, "index");
playQueueService.doRemove(request, response, index);
} else if ("shuffle".equals(action)) {
playQueueService.doShuffle(request, response);
} else if ("setGain".equals(action)) {
float gain = 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);
}
public void createPlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request, true);
String username = securityService.getCurrentUsername(request);
Integer playlistId = 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 : 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);
}
public void updatePlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request, true);
String username = securityService.getCurrentUsername(request);
int id = 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 = 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 : 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 : 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);
}
public void deletePlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request, true);
String username = securityService.getCurrentUsername(request);
int id = 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);
}
public void getAlbumList(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
Player player = playerService.getPlayer(request, response);
String username = securityService.getCurrentUsername(request);
int userGroupId = securityService.getCurrentUserGroupId(request);
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("albumList", false);
int size = getIntParameter(request, "size", 10);
int offset = getIntParameter(request, "offset", 0);
size = Math.max(0, Math.min(size, 500));
String type = getRequiredStringParameter(request, "type");
List<MediaFile> albums = Collections.emptyList();
// List<net.sourceforge.subsonic.controller.HomeController.Album> HCalbums;
if ("highest".equals(type)) {
albums = ratingService.getHighestRated(offset, size, userGroupId);
} else if ("frequent".equals(type)) {
albums = mediaFileService.getMostFrequentlyPlayedAlbums(offset, size, userGroupId );
} else if ("recent".equals(type)) {
albums = mediaFileService.getMostRecentlyPlayedAlbums(offset, size, userGroupId);
} else if ("newest".equals(type)) {
albums = mediaFileService.getNewestAlbums(null, offset, size, userGroupId);
} else if ("tip".equals(type)) {
albums = hotService.getRandomHotRated(0, size, userGroupId );
} else if ("hot".equals(type)) {
albums = hotService.getHotRated(null, offset, size, userGroupId);
} else if ("starred".equals(type)) {
albums = mediaFileService.getStarredAlbums(offset, size, username);
} else if ("alphabeticalByArtist".equals(type)) {
albums = mediaFileService.getAlphabetialAlbums(offset, size, true, userGroupId);
} else if ("alphabeticalByName".equals(type)) {
albums = mediaFileService.getAlphabetialAlbums(offset, size, false, userGroupId);
} else if ("byGenre".equals(type)) {
albums = mediaFileService.getAlbumsByGenre(offset, size, getRequiredStringParameter(request, "genre"), userGroupId);
} else if ("byYear".equals(type)) {
albums = mediaFileService.getAlbumsByYear(offset, size, getRequiredIntParameter(request, "fromYear"), getRequiredIntParameter(request, "toYear"), userGroupId);
} else if ("allArtist".equals(type)) {
albums = mediaFileService.getArtists(offset, size, username, userGroupId);
} else if ("starredArtist".equals(type)) {
albums = mediaFileService.getStarredArtists(offset, size, username);
} else if ("random".equals(type)) {
albums = searchService.getRandomAlbums(null, size, userGroupId);
} else {
throw new Exception("Invalid list type: " + type);
}
for (MediaFile album : albums) {
// MediaFile mediaFile = mediaFileService.getMediaFile(album.getPath());
AttributeSet attributes = createAttributesForMediaFile(player, album, username);
builder.add("album", attributes, true);
}
builder.endAll();
response.getWriter().print(builder);
}
public void getAlbumList2(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("albumList2", false);
int size = getIntParameter(request, "size", 10);
int offset = getIntParameter(request, "offset", 0);
size = Math.max(0, Math.min(size, 500));
String type = 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 ("byGenre".equals(type)) {
albums = albumDao.getAlbumsByGenre(offset, size, getRequiredStringParameter(request, "genre"));
} else if ("byYear".equals(type)) {
albums = albumDao.getAlbumsByYear(offset, size, getRequiredIntParameter(request, "fromYear"),
getRequiredIntParameter(request, "toYear"));
} 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);
}
public void getRandomSongs(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
Player player = playerService.getPlayer(request, response);
String username = securityService.getCurrentUsername(request);
int userGroupId = securityService.getCurrentUserGroupId(request);
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("randomSongs", false);
int size = getIntParameter(request, "size", 20);
size = Math.max(0, Math.min(size, 500));
String genre = getStringParameter(request, "genre");
Integer fromYear = getIntParameter(request, "fromYear");
Integer toYear = getIntParameter(request, "toYear");
Integer musicFolderId = getIntParameter(request, "musicFolderId");
if (musicFolderId == null){
List<MusicFolder> musicFolders = settingsService.getAllMusicFolders(userGroupId, settingsService.isSortMediaFileFolder());
Random generator = new Random();
int index = generator.nextInt (musicFolders.size());
if ( index >-1 ) { musicFolderId = musicFolders.get(index).getId(); }
}
RandomSearchCriteria criteria = new RandomSearchCriteria(size, genre, fromYear, toYear, musicFolderId);
List <MediaFile> resultList;
int loop = 0;
do {
resultList = searchService.getRandomSongs(criteria);
loop++;
List<MusicFolder> musicFolders = settingsService.getAllMusicFolders(userGroupId, settingsService.isSortMediaFileFolder());
Random generator = new Random();
int index = generator.nextInt (musicFolders.size());
if ( index >-1 ) { musicFolderId = musicFolders.get(index).getId(); }
RandomSearchCriteria tmpCriteria = new RandomSearchCriteria(size, genre, fromYear, toYear, musicFolderId);
resultList = searchService.getRandomSongs(tmpCriteria);
if ( loop>10 ){
//System.out.println("BREAK");
break; }
} while (resultList.size()<1);
for (MediaFile mediaFile : resultList) {
AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
builder.add("song", attributes, true);
}
builder.endAll();
response.getWriter().print(builder);
}
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);
int size = getIntParameter(request, "size", Integer.MAX_VALUE);
int offset = 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);
}
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.
}
if (mediaFile.isAlbum() || mediaFile.isAlbumSet())
{
if (settingsService.isShowAlbumsYearApi()) {
attributes.add("title", "[" + mediaFile.getYear() + "] " + mediaFile.getAlbumName());
attributes.add("album", mediaFile.getAlbumSetName());
}else
{
attributes.add("title", mediaFile.getAlbumName());
attributes.add("album", mediaFile.getAlbumSetName());
attributes.add("path", mediaFile.getPath().substring(mediaFile.getPath().lastIndexOf('\\') + 1) );
}
}
else {
attributes.add("title", mediaFile.getName());
attributes.add("album", parent.getAlbumSetName());
}
attributes.add("artist", mediaFile.getArtist());
attributes.add("isDir", mediaFile.isDirectory());
attributes.add("coverArt", findCoverArt(mediaFile, parent));
attributes.add("year", mediaFile.getYear());
attributes.add("genre", mediaFile.getGenre());
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.isSingleArtist() || mediaFile.isMultiArtist())
{
attributes.add("isArtist", true);
attributes.add("title", mediaFile.getArtist());
attributes.add("artist", mediaFile.getGenre());
}
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("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));
Bookmark bookmark = bookmarkCache.get(new BookmarkKey(username, mediaFile.getId()));
if (bookmark != null) {
attributes.add("bookmarkPosition", bookmark.getPositionMillis());
}
if (mediaFile.getAlbumArtist() != 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 (!folderPathLower.endsWith("/")) {
folderPathLower += "/";
}
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 ModelAndView hls(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;
}
hlsController.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;
}
boolean submission = getBooleanParameter(request, "submission", true);
int[] ids = getRequiredIntParameters(request, "id");
long[] times = getLongParameters(request, "time");
if (times.length > 0 && times.length != ids.length) {
error(request, response, ErrorCode.GENERIC, "Wrong number of timestamps: " + times.length);
return;
}
for (int i = 0; i < ids.length; i++) {
int id = ids[i];
MediaFile file = mediaFileService.getMediaFile(id);
if (file == null) {
LOG.warn("File to scrobble not found: " + id);
continue;
}
Date time = times.length == 0 ? null : new Date(times[i]);
audioScrobblerService.register(file, player.getUsername(), submission, time);
}
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);
String username = securityService.getCurrentUser(request).getUsername();
for (int id : 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 : 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 : 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);
}
}
builder.endAll();
response.getWriter().print(builder);
}
public void getNewaddedSongs(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
Player player = playerService.getPlayer(request, response);
String username = securityService.getCurrentUsername(request);
int userGroupId = securityService.getCurrentUserGroupId(request);
int size = getIntParameter(request, "size", 50);
int offset = getIntParameter(request, "offset", 0);
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("newadded", false);
for (MediaFile song : mediaFileDao.getHistory(offset, size, userGroupId, MUSIC.name())) {
builder.add("song", createAttributesForMediaFile(player, song, username), true);
}
builder.endAll();
response.getWriter().print(builder);
}
public void getLastplayedSongs(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
Player player = playerService.getPlayer(request, response);
String username = securityService.getCurrentUsername(request);
int size = getIntParameter(request, "size", 50);
int offset = getIntParameter(request, "offset", 0);
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("lastplayed", false);
for (MediaFile song : mediaFileDao.getLastPlayedCountForUser(offset, size, username)) {
builder.add("song", createAttributesForMediaFile(player, song, username), true);
}
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", createAttributesForArtist(artist, username), true);
}
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);
boolean includeEpisodes = getBooleanParameter(request, "includeEpisodes", true);
Integer channelId = getIntParameter(request, "id");
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("podcasts", false);
for (PodcastChannel channel : podcastService.getAllChannels()) {
if (channelId == null || channelId.equals(channel.getId())) {
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);
if (includeEpisodes) {
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 refreshPodcasts(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
User user = securityService.getCurrentUser(request);
if (!user.isPodcastRole()) {
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to administrate podcasts.");
return;
}
podcastService.refreshAllChannels(true);
XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
response.getWriter().print(builder);
}
public void createPodcastChannel(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
User user = securityService.getCurrentUser(request);
if (!user.isPodcastRole()) {
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to administrate podcasts.");
return;
}
String url = getRequiredStringParameter(request, "url");
podcastService.createChannel(url);
XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
response.getWriter().print(builder);
}
public void deletePodcastChannel(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
User user = securityService.getCurrentUser(request);
if (!user.isPodcastRole()) {
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to administrate podcasts.");
return;
}
int id = getRequiredIntParameter(request, "id");
podcastService.deleteChannel(id);
XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
response.getWriter().print(builder);
}
public void deletePodcastEpisode(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
User user = securityService.getCurrentUser(request);
if (!user.isPodcastRole()) {
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to administrate podcasts.");
return;
}
int id = getRequiredIntParameter(request, "id");
podcastService.deleteEpisode(id, true);
XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
response.getWriter().print(builder);
}
public void downloadPodcastEpisode(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
User user = securityService.getCurrentUser(request);
if (!user.isPodcastRole()) {
error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to administrate podcasts.");
return;
}
int id = getRequiredIntParameter(request, "id");
PodcastEpisode episode = podcastService.getEpisode(id, true);
if (episode == null) {
error(request, response, ErrorCode.NOT_FOUND, "Podcast episode " + id + " not found.");
return;
}
podcastService.downloadEpisode(episode);
XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
response.getWriter().print(builder);
}
public void getInternetRadioStations(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("internetRadioStations", false);
for (InternetRadio radio : settingsService.getAllInternetRadios()) {
AttributeSet attrs = new AttributeSet();
attrs.add("id", radio.getId());
attrs.add("name", radio.getName());
attrs.add("streamUrl", radio.getStreamUrl());
attrs.add("homePageUrl", radio.getHomepageUrl());
builder.add("internetRadioStation", attrs, true);
}
builder.endAll();
response.getWriter().print(builder);
}
public void getBookmarks(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("bookmarks", false);
for (Bookmark bookmark : bookmarkDao.getBookmarks(username)) {
builder.add("bookmark", createAttributesForBookmark(bookmark), false);
MediaFile mediaFile = mediaFileService.getMediaFile(bookmark.getMediaFileId());
AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
builder.add("entry", attributes, true);
builder.end();
}
builder.endAll();
response.getWriter().print(builder);
}
public void createBookmark(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
String username = securityService.getCurrentUsername(request);
int mediaFileId = getRequiredIntParameter(request, "id");
long position = getRequiredLongParameter(request, "position");
String comment = request.getParameter("comment");
Date now = new Date();
Bookmark bookmark = new Bookmark(0, mediaFileId, position, username, comment, now, now);
bookmarkDao.createOrUpdateBookmark(bookmark);
refreshBookmarkCache();
XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
response.getWriter().print(builder);
}
public void deleteBookmark(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
XMLBuilder builder = createXMLBuilder(request, response, true);
String username = securityService.getCurrentUsername(request);
int mediaFileId = getRequiredIntParameter(request, "id");
bookmarkDao.deleteBookmark(username, mediaFileId);
refreshBookmarkCache();
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 getSharedFiles(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
Player player = playerService.getPlayer(request, response);
String username = securityService.getCurrentUsername(request);
String shareName = getRequiredStringParameter(request, "share");
Share share = shareService.getShareByName(shareName);
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("sharedFiles", false);
for (MediaFile mediaFile : shareService.getSharedFiles(share.getId())) {
if ( mediaFile.isDirectory() ) {
for(MediaFile child : mediaFileService.getChildrenOf(mediaFile, true, false, false)){
builder.add("song", createAttributesForMediaFile(player, child, username), true);
};
}
else {
builder.add("song", createAttributesForMediaFile(player, mediaFile, username), true);
}
}
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);
List<MediaFile> files = new ArrayList<MediaFile>();
for (int id : getRequiredIntParameters(request, "id")) {
files.add(mediaFileService.getMediaFile(id));
}
Share share = shareService.createShare(request, files);
share.setDescription(request.getParameter("description"));
long expires = 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);
}
public void deleteShare(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
User user = securityService.getCurrentUser(request);
int id = 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);
}
public void updateShare(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
User user = securityService.getCurrentUser(request);
int id = 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);
}
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;
}
private List<Attribute> createAttributesForBookmark(Bookmark bookmark) {
List<Attribute> attributes = new ArrayList<Attribute>();
attributes.add(new Attribute("position", bookmark.getPositionMillis()));
attributes.add(new Attribute("username", bookmark.getUsername()));
attributes.add(new Attribute("comment", bookmark.getComment()));
attributes.add(new Attribute("created", StringUtil.toISO8601(bookmark.getCreated())));
attributes.add(new Attribute("changed", StringUtil.toISO8601(bookmark.getChanged())));
return attributes;
}
public ModelAndView videoPlayer(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
Map<String, Object> map = new HashMap<String, Object>();
int id = getRequiredIntParameter(request, "id");
MediaFile file = mediaFileService.getMediaFile(id);
int timeOffset = 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", getIntParameter(request, "maxBitRate", VideoPlayerController.DEFAULT_BIT_RATE));
map.put("duration", duration);
map.put("timeOffset", timeOffset);
map.put("bitRates", VideoPlayerController.BIT_RATES);
map.put("autoplay", getBooleanParameter(request, "autoplay", true));
ModelAndView result = new ModelAndView();
if (settingsService.isHTML5PlayerEnabled()) {
result.setViewName("rest/videoPlayerH5");
} else {
result.setViewName("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);
String username = getRequiredStringParameter(request, "username");
String password = decrypt(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);
}
public void getUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
String username = getRequiredStringParameter(request, "username");
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 = createAttributesForUser(requestedUser, userSettings);
builder.add("user", attributes, true);
builder.endAll();
response.getWriter().print(builder);
}
public void getUsers(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
User currentUser = securityService.getCurrentUser(request);
if (!currentUser.isAdminRole()) {
error(request, response, ErrorCode.NOT_AUTHORIZED, currentUser.getUsername() + " is not authorized to get details for other users.");
return;
}
XMLBuilder builder = createXMLBuilder(request, response, true);
builder.add("users", false);
for (User user : securityService.getAllUsers()) {
UserSettings userSettings = settingsService.getUserSettings(user.getUsername());
List<Attribute> attributes = createAttributesForUser(user, userSettings);
builder.add("user", attributes, true);
}
builder.endAll();
response.getWriter().print(builder);
}
private List<Attribute> createAttributesForUser(User user, UserSettings userSettings) {
return Arrays.asList(
new Attribute("username", user.getUsername()),
new Attribute("email", user.getEmail()),
new Attribute("scrobblingEnabled", userSettings.isLastFmEnabled()),
new Attribute("adminRole", user.isAdminRole()),
new Attribute("settingsRole", user.isSettingsRole()),
new Attribute("downloadRole", user.isDownloadRole()),
new Attribute("uploadRole", user.isUploadRole()),
new Attribute("playlistRole", true), // Since 1.8.0
new Attribute("coverArtRole", user.isCoverArtRole()),
new Attribute("commentRole", user.isCommentRole()),
new Attribute("podcastRole", user.isPodcastRole()),
new Attribute("streamRole", user.isStreamRole()),
new Attribute("jukeboxRole", user.isJukeboxRole()),
new Attribute("shareRole", user.isShareRole())
);
}
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;
}
UserSettingsCommand command = new UserSettingsCommand();
command.setUsername(getRequiredStringParameter(request, "username"));
command.setPassword(decrypt(getRequiredStringParameter(request, "password")));
command.setEmail(getRequiredStringParameter(request, "email"));
command.setLdapAuthenticated(getBooleanParameter(request, "ldapAuthenticated", false));
command.setAdminRole(getBooleanParameter(request, "adminRole", false));
command.setCommentRole(getBooleanParameter(request, "commentRole", false));
command.setCoverArtRole(getBooleanParameter(request, "coverArtRole", false));
command.setDownloadRole(getBooleanParameter(request, "downloadRole", false));
command.setStreamRole(getBooleanParameter(request, "streamRole", true));
command.setUploadRole(getBooleanParameter(request, "uploadRole", false));
command.setJukeboxRole(getBooleanParameter(request, "jukeboxRole", false));
command.setPodcastRole(getBooleanParameter(request, "podcastRole", false));
command.setSettingsRole(getBooleanParameter(request, "settingsRole", true));
command.setShareRole(getBooleanParameter(request, "shareRole", false));
command.setSearchRole(getBooleanParameter(request, "searchRole", false));
command.setTranscodeSchemeName(getStringParameter(request, "transcodeScheme", TranscodeScheme.OFF.name()));
command.setGroupId(getIntParameter(request, "groupId", 0));
userSettingsController.createUser(command);
XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
response.getWriter().print(builder);
}
public void updateUser(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 update users.");
return;
}
String username = getRequiredStringParameter(request, "username");
User u = securityService.getUserByName(username);
UserSettings s = settingsService.getUserSettings(username);
if (u == null) {
error(request, response, ErrorCode.NOT_FOUND, "No such user: " + username);
return;
} else if (User.USERNAME_ADMIN.equals(username)) {
error(request, response, ErrorCode.NOT_AUTHORIZED, "Not allowed to change admin user");
return;
}
UserSettingsCommand command = new UserSettingsCommand();
command.setUsername(username);
command.setEmail(getStringParameter(request, "email", u.getEmail()));
command.setLdapAuthenticated(getBooleanParameter(request, "ldapAuthenticated", u.isLdapAuthenticated()));
command.setAdminRole(getBooleanParameter(request, "adminRole", u.isAdminRole()));
command.setCommentRole(getBooleanParameter(request, "commentRole", u.isCommentRole()));
command.setCoverArtRole(getBooleanParameter(request, "coverArtRole", u.isCoverArtRole()));
command.setDownloadRole(getBooleanParameter(request, "downloadRole", u.isDownloadRole()));
command.setStreamRole(getBooleanParameter(request, "streamRole", u.isDownloadRole()));
command.setUploadRole(getBooleanParameter(request, "uploadRole", u.isUploadRole()));
command.setJukeboxRole(getBooleanParameter(request, "jukeboxRole", u.isJukeboxRole()));
command.setPodcastRole(getBooleanParameter(request, "podcastRole", u.isPodcastRole()));
command.setSettingsRole(getBooleanParameter(request, "settingsRole", u.isSettingsRole()));
command.setShareRole(getBooleanParameter(request, "shareRole", u.isShareRole()));
command.setTranscodeSchemeName(s.getTranscodeScheme().name());
if (hasParameter(request, "password")) {
command.setPassword(decrypt(getRequiredStringParameter(request, "password")));
command.setPasswordChange(true);
}
userSettingsController.updateUser(command);
XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
response.getWriter().print(builder);
}
private boolean hasParameter(HttpServletRequest request, String name) {
return request.getParameter(name) != null;
}
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;
}
String username = getRequiredStringParameter(request, "username");
if (User.USERNAME_ADMIN.equals(username)) {
error(request, response, ErrorCode.NOT_AUTHORIZED, "Not allowed to delete admin user");
return;
}
securityService.deleteUser(username);
XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
response.getWriter().print(builder);
}
public void getChatMessages(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
XMLBuilder builder = createXMLBuilder(request, response, true);
long since = 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);
chatService.doAddMessage(getRequiredStringParameter(request, "message"), request);
XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
response.getWriter().print(builder);
}
public void getLyrics(HttpServletRequest request, HttpServletResponse response) throws Exception {
request = wrapRequest(request);
String artist = request.getParameter("artist");
String title = request.getParameter("title");
String id = request.getParameter("id");
LyricsInfo lyrics = new LyricsInfo();
if (id != null) {
lyrics = lyricsService.getLyrics(id, artist, title);
} else {
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);
Integer rating = getRequiredIntParameter(request, "rating");
if (rating == 0) {
rating = null;
}
int id = 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);
}
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;
}
}
public static 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 static XMLBuilder createXMLBuilder(HttpServletRequest request, HttpServletResponse response, boolean ok) throws IOException {
String format = 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://madsonic.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 setHlsController(HLSController hlsController) {
this.hlsController = hlsController;
}
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 setHotService(HotService hotService) {
this.hotService = hotService;
}
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 void setMusicIndexService(MusicIndexService musicIndexService) {
this.musicIndexService = musicIndexService;
}
public void setMediaScannerService(MediaScannerService mediaScannerService) {
this.mediaScannerService = mediaScannerService;
}
public void setBookmarkDao(BookmarkDao bookmarkDao) {
this.bookmarkDao = bookmarkDao;
}
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 upgrade to Subsonic Premium. 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;
}
}
private static class BookmarkKey extends Pair<String, Integer> {
private BookmarkKey(String username, int mediaFileId) {
super(username, mediaFileId);
}
static BookmarkKey forBookmark(Bookmark b) {
return new BookmarkKey(b.getUsername(), b.getMediaFileId());
}
}
}