PageRenderTime 114ms CodeModel.GetById 3ms app.highlight 98ms RepoModel.GetById 1ms app.codeStats 1ms

/subsonic-main/src/main/java/net/sourceforge/subsonic/controller/RESTController.java

https://github.com/edikabe/subsonic
Java | 1875 lines | 1546 code | 282 blank | 47 comment | 281 complexity | c4351b6ba54363b61d0e6cde6ab97497 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 This file is part of Subsonic.
   3
   4 Subsonic is free software: you can redistribute it and/or modify
   5 it under the terms of the GNU General Public License as published by
   6 the Free Software Foundation, either version 3 of the License, or
   7 (at your option) any later version.
   8
   9 Subsonic is distributed in the hope that it will be useful,
  10 but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 GNU General Public License for more details.
  13
  14 You should have received a copy of the GNU General Public License
  15 along with Subsonic.  If not, see <http://www.gnu.org/licenses/>.
  16
  17 Copyright 2009 (C) Sindre Mehus
  18 */
  19package net.sourceforge.subsonic.controller;
  20
  21import java.io.File;
  22import java.io.IOException;
  23import java.util.ArrayList;
  24import java.util.Arrays;
  25import java.util.Collections;
  26import java.util.Date;
  27import java.util.HashMap;
  28import java.util.List;
  29import java.util.Map;
  30import java.util.SortedMap;
  31import java.util.SortedSet;
  32import java.util.TreeSet;
  33
  34import javax.servlet.http.HttpServletRequest;
  35import javax.servlet.http.HttpServletRequestWrapper;
  36import javax.servlet.http.HttpServletResponse;
  37
  38import org.apache.commons.lang.StringUtils;
  39import org.springframework.web.bind.ServletRequestUtils;
  40import org.springframework.web.servlet.ModelAndView;
  41import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
  42
  43import net.sourceforge.subsonic.Logger;
  44import net.sourceforge.subsonic.ajax.ChatService;
  45import net.sourceforge.subsonic.ajax.LyricsInfo;
  46import net.sourceforge.subsonic.ajax.LyricsService;
  47import net.sourceforge.subsonic.ajax.PlayQueueService;
  48import net.sourceforge.subsonic.command.UserSettingsCommand;
  49import net.sourceforge.subsonic.dao.AlbumDao;
  50import net.sourceforge.subsonic.dao.ArtistDao;
  51import net.sourceforge.subsonic.dao.MediaFileDao;
  52import net.sourceforge.subsonic.domain.Album;
  53import net.sourceforge.subsonic.domain.Artist;
  54import net.sourceforge.subsonic.domain.MediaFile;
  55import net.sourceforge.subsonic.domain.MusicFolder;
  56import net.sourceforge.subsonic.domain.MusicIndex;
  57import net.sourceforge.subsonic.domain.PlayQueue;
  58import net.sourceforge.subsonic.domain.Player;
  59import net.sourceforge.subsonic.domain.PlayerTechnology;
  60import net.sourceforge.subsonic.domain.Playlist;
  61import net.sourceforge.subsonic.domain.PodcastChannel;
  62import net.sourceforge.subsonic.domain.PodcastEpisode;
  63import net.sourceforge.subsonic.domain.RandomSearchCriteria;
  64import net.sourceforge.subsonic.domain.SearchCriteria;
  65import net.sourceforge.subsonic.domain.SearchResult;
  66import net.sourceforge.subsonic.domain.Share;
  67import net.sourceforge.subsonic.domain.TranscodeScheme;
  68import net.sourceforge.subsonic.domain.TransferStatus;
  69import net.sourceforge.subsonic.domain.User;
  70import net.sourceforge.subsonic.domain.UserSettings;
  71import net.sourceforge.subsonic.service.AudioScrobblerService;
  72import net.sourceforge.subsonic.service.JukeboxService;
  73import net.sourceforge.subsonic.service.MediaFileService;
  74import net.sourceforge.subsonic.service.MusicIndexService;
  75import net.sourceforge.subsonic.service.PlayerService;
  76import net.sourceforge.subsonic.service.PlaylistService;
  77import net.sourceforge.subsonic.service.PodcastService;
  78import net.sourceforge.subsonic.service.RatingService;
  79import net.sourceforge.subsonic.service.SearchService;
  80import net.sourceforge.subsonic.service.SecurityService;
  81import net.sourceforge.subsonic.service.SettingsService;
  82import net.sourceforge.subsonic.service.ShareService;
  83import net.sourceforge.subsonic.service.StatusService;
  84import net.sourceforge.subsonic.service.TranscodingService;
  85import net.sourceforge.subsonic.util.StringUtil;
  86import net.sourceforge.subsonic.util.XMLBuilder;
  87
  88import static net.sourceforge.subsonic.security.RESTRequestParameterProcessingFilter.decrypt;
  89import static net.sourceforge.subsonic.util.XMLBuilder.Attribute;
  90import static net.sourceforge.subsonic.util.XMLBuilder.AttributeSet;
  91
  92/**
  93 * Multi-controller used for the REST API.
  94 * <p/>
  95 * For documentation, please refer to api.jsp.
  96 * <p/>
  97 * Note: Exceptions thrown from the methods are intercepted by RESTFilter.
  98 *
  99 * @author Sindre Mehus
 100 */
 101public class RESTController extends MultiActionController {
 102
 103    private static final Logger LOG = Logger.getLogger(RESTController.class);
 104
 105    private SettingsService settingsService;
 106    private SecurityService securityService;
 107    private PlayerService playerService;
 108    private MediaFileService mediaFileService;
 109    private MusicIndexService musicIndexService;
 110    private TranscodingService transcodingService;
 111    private DownloadController downloadController;
 112    private CoverArtController coverArtController;
 113    private AvatarController avatarController;
 114    private UserSettingsController userSettingsController;
 115    private LeftController leftController;
 116    private HomeController homeController;
 117    private StatusService statusService;
 118    private StreamController streamController;
 119    private HLSController hlsController;
 120    private ShareService shareService;
 121    private PlaylistService playlistService;
 122    private ChatService chatService;
 123    private LyricsService lyricsService;
 124    private PlayQueueService playQueueService;
 125    private JukeboxService jukeboxService;
 126    private AudioScrobblerService audioScrobblerService;
 127    private PodcastService podcastService;
 128    private RatingService ratingService;
 129    private SearchService searchService;
 130    private MediaFileDao mediaFileDao;
 131    private ArtistDao artistDao;
 132    private AlbumDao albumDao;
 133
 134    public void ping(HttpServletRequest request, HttpServletResponse response) throws Exception {
 135        XMLBuilder builder = createXMLBuilder(request, response, true).endAll();
 136        response.getWriter().print(builder);
 137    }
 138
 139    public void getLicense(HttpServletRequest request, HttpServletResponse response) throws Exception {
 140        request = wrapRequest(request);
 141        XMLBuilder builder = createXMLBuilder(request, response, true);
 142
 143        String email = settingsService.getLicenseEmail();
 144        String key = settingsService.getLicenseCode();
 145        Date date = settingsService.getLicenseDate();
 146        boolean valid = settingsService.isLicenseValid();
 147
 148        AttributeSet attributes = new AttributeSet();
 149        attributes.add("valid", valid);
 150        if (valid) {
 151            attributes.add("email", email);
 152            attributes.add("key", key);
 153            attributes.add("date", StringUtil.toISO8601(date));
 154        }
 155
 156        builder.add("license", attributes, true);
 157        builder.endAll();
 158        response.getWriter().print(builder);
 159    }
 160
 161    public void getMusicFolders(HttpServletRequest request, HttpServletResponse response) throws Exception {
 162        request = wrapRequest(request);
 163        XMLBuilder builder = createXMLBuilder(request, response, true);
 164        builder.add("musicFolders", false);
 165
 166        for (MusicFolder musicFolder : settingsService.getAllMusicFolders()) {
 167            AttributeSet attributes = new AttributeSet();
 168            attributes.add("id", musicFolder.getId());
 169            attributes.add("name", musicFolder.getName());
 170            builder.add("musicFolder", attributes, true);
 171        }
 172        builder.endAll();
 173        response.getWriter().print(builder);
 174    }
 175
 176    public void getIndexes(HttpServletRequest request, HttpServletResponse response) throws Exception {
 177        request = wrapRequest(request);
 178        XMLBuilder builder = createXMLBuilder(request, response, true);
 179
 180        long ifModifiedSince = ServletRequestUtils.getLongParameter(request, "ifModifiedSince", 0L);
 181        long lastModified = leftController.getLastModified(request);
 182
 183        if (lastModified <= ifModifiedSince) {
 184            builder.endAll();
 185            response.getWriter().print(builder);
 186            return;
 187        }
 188
 189        builder.add("indexes", "lastModified", lastModified, false);
 190
 191        List<MusicFolder> musicFolders = settingsService.getAllMusicFolders();
 192        Integer musicFolderId = ServletRequestUtils.getIntParameter(request, "musicFolderId");
 193        if (musicFolderId != null) {
 194            for (MusicFolder musicFolder : musicFolders) {
 195                if (musicFolderId.equals(musicFolder.getId())) {
 196                    musicFolders = Arrays.asList(musicFolder);
 197                    break;
 198                }
 199            }
 200        }
 201
 202        List<MediaFile> shortcuts = leftController.getShortcuts(musicFolders, settingsService.getShortcutsAsArray());
 203        for (MediaFile shortcut : shortcuts) {
 204            builder.add("shortcut", true,
 205                    new Attribute("name", shortcut.getName()),
 206                    new Attribute("id", shortcut.getId()));
 207        }
 208
 209        SortedMap<MusicIndex, SortedSet<MusicIndex.SortableArtistWithMediaFiles>> indexedArtists =
 210                leftController.getMusicFolderContent(musicFolders, false).getIndexedArtists();
 211
 212        for (Map.Entry<MusicIndex, SortedSet<MusicIndex.SortableArtistWithMediaFiles>> entry : indexedArtists.entrySet()) {
 213            builder.add("index", "name", entry.getKey().getIndex(), false);
 214
 215            for (MusicIndex.SortableArtistWithMediaFiles artist : entry.getValue()) {
 216                for (MediaFile mediaFile : artist.getMediaFiles()) {
 217                    if (mediaFile.isDirectory()) {
 218                        builder.add("artist", true,
 219                                new Attribute("name", artist.getName()),
 220                                new Attribute("id", mediaFile.getId()));
 221                    }
 222                }
 223            }
 224            builder.end();
 225        }
 226
 227        // Add children
 228        Player player = playerService.getPlayer(request, response);
 229        String username = securityService.getCurrentUsername(request);
 230        List<MediaFile> singleSongs = leftController.getSingleSongs(musicFolders, false);
 231
 232        for (MediaFile singleSong : singleSongs) {
 233            builder.add("child", createAttributesForMediaFile(player, singleSong, username), true);
 234        }
 235
 236        builder.endAll();
 237        response.getWriter().print(builder);
 238    }
 239
 240    public void getArtists(HttpServletRequest request, HttpServletResponse response) throws Exception {
 241        request = wrapRequest(request);
 242        XMLBuilder builder = createXMLBuilder(request, response, true);
 243        String username = securityService.getCurrentUsername(request);
 244
 245        builder.add("artists", false);
 246
 247        List<Artist> artists = artistDao.getAlphabetialArtists(0, Integer.MAX_VALUE);
 248        SortedMap<MusicIndex, SortedSet<MusicIndex.SortableArtistWithArtist>> indexedArtists = musicIndexService.getIndexedArtists(artists);
 249        for (Map.Entry<MusicIndex, SortedSet<MusicIndex.SortableArtistWithArtist>> entry : indexedArtists.entrySet()) {
 250            builder.add("index", "name", entry.getKey().getIndex(), false);
 251            for (MusicIndex.SortableArtistWithArtist sortableArtist : entry.getValue()) {
 252                AttributeSet attributes = createAttributesForArtist(sortableArtist.getArtist(), username);
 253                builder.add("artist", attributes, true);
 254            }
 255            builder.end();
 256        }
 257
 258        builder.endAll();
 259        response.getWriter().print(builder);
 260    }
 261
 262    private AttributeSet createAttributesForArtist(Artist artist, String username) {
 263        AttributeSet attributes = new AttributeSet();
 264        attributes.add("id", artist.getId());
 265        attributes.add("name", artist.getName());
 266        if (artist.getCoverArtPath() != null) {
 267            attributes.add("coverArt", CoverArtController.ARTIST_COVERART_PREFIX + artist.getId());
 268        }
 269        attributes.add("albumCount", artist.getAlbumCount());
 270        attributes.add("starred", StringUtil.toISO8601(artistDao.getArtistStarredDate(artist.getId(), username)));
 271        return attributes;
 272    }
 273
 274    public void getArtist(HttpServletRequest request, HttpServletResponse response) throws Exception {
 275        request = wrapRequest(request);
 276        XMLBuilder builder = createXMLBuilder(request, response, true);
 277
 278        String username = securityService.getCurrentUsername(request);
 279        int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
 280        Artist artist = artistDao.getArtist(id);
 281        if (artist == null) {
 282            error(request, response, ErrorCode.NOT_FOUND, "Artist not found.");
 283            return;
 284        }
 285
 286        builder.add("artist", createAttributesForArtist(artist, username), false);
 287        for (Album album : albumDao.getAlbumsForArtist(artist.getName())) {
 288            builder.add("album", createAttributesForAlbum(album, username), true);
 289        }
 290
 291        builder.endAll();
 292        response.getWriter().print(builder);
 293    }
 294
 295    private AttributeSet createAttributesForAlbum(Album album, String username) {
 296        AttributeSet attributes;
 297        attributes = new AttributeSet();
 298        attributes.add("id", album.getId());
 299        attributes.add("name", album.getName());
 300        attributes.add("artist", album.getArtist());
 301        if (album.getArtist() != null) {
 302            Artist artist = artistDao.getArtist(album.getArtist());
 303            if (artist != null) {
 304                attributes.add("artistId", artist.getId());
 305            }
 306        }
 307        if (album.getCoverArtPath() != null) {
 308            attributes.add("coverArt", CoverArtController.ALBUM_COVERART_PREFIX + album.getId());
 309        }
 310        attributes.add("songCount", album.getSongCount());
 311        attributes.add("duration", album.getDurationSeconds());
 312        attributes.add("created", StringUtil.toISO8601(album.getCreated()));
 313        attributes.add("starred", StringUtil.toISO8601(albumDao.getAlbumStarredDate(album.getId(), username)));
 314
 315        return attributes;
 316    }
 317
 318    private AttributeSet createAttributesForPlaylist(Playlist playlist) {
 319        AttributeSet attributes;
 320        attributes = new AttributeSet();
 321        attributes.add("id", playlist.getId());
 322        attributes.add("name", playlist.getName());
 323        attributes.add("comment", playlist.getComment());
 324        attributes.add("owner", playlist.getUsername());
 325        attributes.add("public", playlist.isPublic());
 326        attributes.add("songCount", playlist.getFileCount());
 327        attributes.add("duration", playlist.getDurationSeconds());
 328        attributes.add("created", StringUtil.toISO8601(playlist.getCreated()));
 329        return attributes;
 330    }
 331
 332    public void getAlbum(HttpServletRequest request, HttpServletResponse response) throws Exception {
 333        request = wrapRequest(request);
 334        Player player = playerService.getPlayer(request, response);
 335        String username = securityService.getCurrentUsername(request);
 336        XMLBuilder builder = createXMLBuilder(request, response, true);
 337
 338        int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
 339        Album album = albumDao.getAlbum(id);
 340        if (album == null) {
 341            error(request, response, ErrorCode.NOT_FOUND, "Album not found.");
 342            return;
 343        }
 344
 345        builder.add("album", createAttributesForAlbum(album, username), false);
 346        for (MediaFile mediaFile : mediaFileDao.getSongsForAlbum(album.getArtist(), album.getName())) {
 347            builder.add("song", createAttributesForMediaFile(player, mediaFile, username), true);
 348        }
 349
 350        builder.endAll();
 351        response.getWriter().print(builder);
 352    }
 353
 354    public void getSong(HttpServletRequest request, HttpServletResponse response) throws Exception {
 355        request = wrapRequest(request);
 356        Player player = playerService.getPlayer(request, response);
 357        String username = securityService.getCurrentUsername(request);
 358        XMLBuilder builder = createXMLBuilder(request, response, true);
 359
 360        int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
 361        MediaFile song = mediaFileDao.getMediaFile(id);
 362        if (song == null || song.isDirectory()) {
 363            error(request, response, ErrorCode.NOT_FOUND, "Song not found.");
 364            return;
 365        }
 366        builder.add("song", createAttributesForMediaFile(player, song, username), true);
 367
 368        builder.endAll();
 369        response.getWriter().print(builder);
 370    }
 371
 372    public void getMusicDirectory(HttpServletRequest request, HttpServletResponse response) throws Exception {
 373        request = wrapRequest(request);
 374        Player player = playerService.getPlayer(request, response);
 375        String username = securityService.getCurrentUsername(request);
 376
 377        int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
 378        MediaFile dir = mediaFileService.getMediaFile(id);
 379        if (dir == null) {
 380            error(request, response, ErrorCode.NOT_FOUND, "Directory not found");
 381            return;
 382        }
 383
 384        XMLBuilder builder = createXMLBuilder(request, response, true);
 385        builder.add("directory", false,
 386                new Attribute("id", dir.getId()),
 387                new Attribute("name", dir.getName()));
 388
 389        for (MediaFile child : mediaFileService.getChildrenOf(dir, true, true, true)) {
 390            AttributeSet attributes = createAttributesForMediaFile(player, child, username);
 391            builder.add("child", attributes, true);
 392        }
 393        builder.endAll();
 394        response.getWriter().print(builder);
 395    }
 396
 397    @Deprecated
 398    public void search(HttpServletRequest request, HttpServletResponse response) throws Exception {
 399        request = wrapRequest(request);
 400        XMLBuilder builder = createXMLBuilder(request, response, true);
 401        Player player = playerService.getPlayer(request, response);
 402        String username = securityService.getCurrentUsername(request);
 403
 404        String any = request.getParameter("any");
 405        String artist = request.getParameter("artist");
 406        String album = request.getParameter("album");
 407        String title = request.getParameter("title");
 408
 409        StringBuilder query = new StringBuilder();
 410        if (any != null) {
 411            query.append(any).append(" ");
 412        }
 413        if (artist != null) {
 414            query.append(artist).append(" ");
 415        }
 416        if (album != null) {
 417            query.append(album).append(" ");
 418        }
 419        if (title != null) {
 420            query.append(title);
 421        }
 422
 423        SearchCriteria criteria = new SearchCriteria();
 424        criteria.setQuery(query.toString().trim());
 425        criteria.setCount(ServletRequestUtils.getIntParameter(request, "count", 20));
 426        criteria.setOffset(ServletRequestUtils.getIntParameter(request, "offset", 0));
 427
 428        SearchResult result = searchService.search(criteria, SearchService.IndexType.SONG);
 429        builder.add("searchResult", false,
 430                new Attribute("offset", result.getOffset()),
 431                new Attribute("totalHits", result.getTotalHits()));
 432
 433        for (MediaFile mediaFile : result.getMediaFiles()) {
 434            AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
 435            builder.add("match", attributes, true);
 436        }
 437        builder.endAll();
 438        response.getWriter().print(builder);
 439    }
 440
 441    public void search2(HttpServletRequest request, HttpServletResponse response) throws Exception {
 442        request = wrapRequest(request);
 443        XMLBuilder builder = createXMLBuilder(request, response, true);
 444        Player player = playerService.getPlayer(request, response);
 445        String username = securityService.getCurrentUsername(request);
 446
 447        builder.add("searchResult2", false);
 448
 449        String query = request.getParameter("query");
 450        SearchCriteria criteria = new SearchCriteria();
 451        criteria.setQuery(StringUtils.trimToEmpty(query));
 452        criteria.setCount(ServletRequestUtils.getIntParameter(request, "artistCount", 20));
 453        criteria.setOffset(ServletRequestUtils.getIntParameter(request, "artistOffset", 0));
 454        SearchResult artists = searchService.search(criteria, SearchService.IndexType.ARTIST);
 455        for (MediaFile mediaFile : artists.getMediaFiles()) {
 456            builder.add("artist", true,
 457                    new Attribute("name", mediaFile.getName()),
 458                    new Attribute("id", mediaFile.getId()));
 459        }
 460
 461        criteria.setCount(ServletRequestUtils.getIntParameter(request, "albumCount", 20));
 462        criteria.setOffset(ServletRequestUtils.getIntParameter(request, "albumOffset", 0));
 463        SearchResult albums = searchService.search(criteria, SearchService.IndexType.ALBUM);
 464        for (MediaFile mediaFile : albums.getMediaFiles()) {
 465            AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
 466            builder.add("album", attributes, true);
 467        }
 468
 469        criteria.setCount(ServletRequestUtils.getIntParameter(request, "songCount", 20));
 470        criteria.setOffset(ServletRequestUtils.getIntParameter(request, "songOffset", 0));
 471        SearchResult songs = searchService.search(criteria, SearchService.IndexType.SONG);
 472        for (MediaFile mediaFile : songs.getMediaFiles()) {
 473            AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
 474            builder.add("song", attributes, true);
 475        }
 476
 477        builder.endAll();
 478        response.getWriter().print(builder);
 479    }
 480
 481    public void search3(HttpServletRequest request, HttpServletResponse response) throws Exception {
 482        request = wrapRequest(request);
 483        XMLBuilder builder = createXMLBuilder(request, response, true);
 484        Player player = playerService.getPlayer(request, response);
 485        String username = securityService.getCurrentUsername(request);
 486
 487        builder.add("searchResult3", false);
 488
 489        String query = request.getParameter("query");
 490        SearchCriteria criteria = new SearchCriteria();
 491        criteria.setQuery(StringUtils.trimToEmpty(query));
 492        criteria.setCount(ServletRequestUtils.getIntParameter(request, "artistCount", 20));
 493        criteria.setOffset(ServletRequestUtils.getIntParameter(request, "artistOffset", 0));
 494        SearchResult searchResult = searchService.search(criteria, SearchService.IndexType.ARTIST_ID3);
 495        for (Artist artist : searchResult.getArtists()) {
 496            builder.add("artist", createAttributesForArtist(artist, username), true);
 497        }
 498
 499        criteria.setCount(ServletRequestUtils.getIntParameter(request, "albumCount", 20));
 500        criteria.setOffset(ServletRequestUtils.getIntParameter(request, "albumOffset", 0));
 501        searchResult = searchService.search(criteria, SearchService.IndexType.ALBUM_ID3);
 502        for (Album album : searchResult.getAlbums()) {
 503            builder.add("album", createAttributesForAlbum(album, username), true);
 504        }
 505
 506        criteria.setCount(ServletRequestUtils.getIntParameter(request, "songCount", 20));
 507        criteria.setOffset(ServletRequestUtils.getIntParameter(request, "songOffset", 0));
 508        searchResult = searchService.search(criteria, SearchService.IndexType.SONG);
 509        for (MediaFile song : searchResult.getMediaFiles()) {
 510            builder.add("song", createAttributesForMediaFile(player, song, username), true);
 511        }
 512
 513        builder.endAll();
 514        response.getWriter().print(builder);
 515    }
 516
 517    public void getPlaylists(HttpServletRequest request, HttpServletResponse response) throws Exception {
 518        request = wrapRequest(request);
 519        XMLBuilder builder = createXMLBuilder(request, response, true);
 520
 521        User user = securityService.getCurrentUser(request);
 522        String authenticatedUsername = user.getUsername();
 523        String requestedUsername = request.getParameter("username");
 524
 525        if (requestedUsername == null) {
 526            requestedUsername = authenticatedUsername;
 527        } else if (!user.isAdminRole()) {
 528            error(request, response, ErrorCode.NOT_AUTHORIZED, authenticatedUsername + " is not authorized to get playlists for " + requestedUsername);
 529            return;
 530        }
 531
 532        builder.add("playlists", false);
 533
 534        for (Playlist playlist : playlistService.getReadablePlaylistsForUser(requestedUsername)) {
 535            List<String> sharedUsers = playlistService.getPlaylistUsers(playlist.getId());
 536            builder.add("playlist", createAttributesForPlaylist(playlist), sharedUsers.isEmpty());
 537            if (!sharedUsers.isEmpty()) {
 538                for (String username : sharedUsers) {
 539                    builder.add("allowedUser", (Iterable<Attribute>) null, username, true);
 540                }
 541                builder.end();
 542            }
 543        }
 544
 545        builder.endAll();
 546        response.getWriter().print(builder);
 547    }
 548
 549    public void getPlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
 550        request = wrapRequest(request);
 551        Player player = playerService.getPlayer(request, response);
 552        String username = securityService.getCurrentUsername(request);
 553
 554        XMLBuilder builder = createXMLBuilder(request, response, true);
 555
 556        int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
 557
 558        Playlist playlist = playlistService.getPlaylist(id);
 559        if (playlist == null) {
 560            error(request, response, ErrorCode.NOT_FOUND, "Playlist not found: " + id);
 561            return;
 562        }
 563        if (!playlistService.isReadAllowed(playlist, username)) {
 564            error(request, response, ErrorCode.NOT_AUTHORIZED, "Permission denied for playlist " + id);
 565            return;
 566        }
 567        builder.add("playlist", createAttributesForPlaylist(playlist), false);
 568        for (String allowedUser : playlistService.getPlaylistUsers(playlist.getId())) {
 569            builder.add("allowedUser", (Iterable<Attribute>) null, allowedUser, true);
 570        }
 571        for (MediaFile mediaFile : playlistService.getFilesInPlaylist(id)) {
 572            AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
 573            builder.add("entry", attributes, true);
 574        }
 575
 576        builder.endAll();
 577        response.getWriter().print(builder);
 578    }
 579
 580    public void jukeboxControl(HttpServletRequest request, HttpServletResponse response) throws Exception {
 581        request = wrapRequest(request, true);
 582
 583        User user = securityService.getCurrentUser(request);
 584        if (!user.isJukeboxRole()) {
 585            error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to use jukebox.");
 586            return;
 587        }
 588
 589        boolean returnPlaylist = false;
 590        String action = ServletRequestUtils.getRequiredStringParameter(request, "action");
 591        if ("start".equals(action)) {
 592            playQueueService.doStart(request, response);
 593        } else if ("stop".equals(action)) {
 594            playQueueService.doStop(request, response);
 595        } else if ("skip".equals(action)) {
 596            int index = ServletRequestUtils.getRequiredIntParameter(request, "index");
 597            int offset = ServletRequestUtils.getIntParameter(request, "offset", 0);
 598            playQueueService.doSkip(request, response, index, offset);
 599        } else if ("add".equals(action)) {
 600            int[] ids = ServletRequestUtils.getIntParameters(request, "id");
 601            playQueueService.doAdd(request, response, ids);
 602        } else if ("set".equals(action)) {
 603            int[] ids = ServletRequestUtils.getIntParameters(request, "id");
 604            playQueueService.doSet(request, response, ids);
 605        } else if ("clear".equals(action)) {
 606            playQueueService.doClear(request, response);
 607        } else if ("remove".equals(action)) {
 608            int index = ServletRequestUtils.getRequiredIntParameter(request, "index");
 609            playQueueService.doRemove(request, response, index);
 610        } else if ("shuffle".equals(action)) {
 611            playQueueService.doShuffle(request, response);
 612        } else if ("setGain".equals(action)) {
 613            float gain = ServletRequestUtils.getRequiredFloatParameter(request, "gain");
 614            jukeboxService.setGain(gain);
 615        } else if ("get".equals(action)) {
 616            returnPlaylist = true;
 617        } else if ("status".equals(action)) {
 618            // No action necessary.
 619        } else {
 620            throw new Exception("Unknown jukebox action: '" + action + "'.");
 621        }
 622
 623        XMLBuilder builder = createXMLBuilder(request, response, true);
 624
 625        Player player = playerService.getPlayer(request, response);
 626        String username = securityService.getCurrentUsername(request);
 627        Player jukeboxPlayer = jukeboxService.getPlayer();
 628        boolean controlsJukebox = jukeboxPlayer != null && jukeboxPlayer.getId().equals(player.getId());
 629        PlayQueue playQueue = player.getPlayQueue();
 630
 631        List<Attribute> attrs = new ArrayList<Attribute>(Arrays.asList(
 632                new Attribute("currentIndex", controlsJukebox && !playQueue.isEmpty() ? playQueue.getIndex() : -1),
 633                new Attribute("playing", controlsJukebox && !playQueue.isEmpty() && playQueue.getStatus() == PlayQueue.Status.PLAYING),
 634                new Attribute("gain", jukeboxService.getGain()),
 635                new Attribute("position", controlsJukebox && !playQueue.isEmpty() ? jukeboxService.getPosition() : 0)));
 636
 637        if (returnPlaylist) {
 638            builder.add("jukeboxPlaylist", attrs, false);
 639            List<MediaFile> result;
 640            synchronized (playQueue) {
 641                result = playQueue.getFiles();
 642            }
 643            for (MediaFile mediaFile : result) {
 644                AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
 645                builder.add("entry", attributes, true);
 646            }
 647        } else {
 648            builder.add("jukeboxStatus", attrs, false);
 649        }
 650
 651        builder.endAll();
 652        response.getWriter().print(builder);
 653    }
 654
 655    public void createPlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
 656        request = wrapRequest(request, true);
 657        String username = securityService.getCurrentUsername(request);
 658
 659        Integer playlistId = ServletRequestUtils.getIntParameter(request, "playlistId");
 660        String name = request.getParameter("name");
 661        if (playlistId == null && name == null) {
 662            error(request, response, ErrorCode.MISSING_PARAMETER, "Playlist ID or name must be specified.");
 663            return;
 664        }
 665
 666        Playlist playlist;
 667        if (playlistId != null) {
 668            playlist = playlistService.getPlaylist(playlistId);
 669            if (playlist == null) {
 670                error(request, response, ErrorCode.NOT_FOUND, "Playlist not found: " + playlistId);
 671                return;
 672            }
 673            if (!playlistService.isWriteAllowed(playlist, username)) {
 674                error(request, response, ErrorCode.NOT_AUTHORIZED, "Permission denied for playlist " + playlistId);
 675                return;
 676            }
 677        } else {
 678            playlist = new Playlist();
 679            playlist.setName(name);
 680            playlist.setCreated(new Date());
 681            playlist.setChanged(new Date());
 682            playlist.setPublic(false);
 683            playlist.setUsername(username);
 684            playlistService.createPlaylist(playlist);
 685        }
 686
 687        List<MediaFile> songs = new ArrayList<MediaFile>();
 688        for (int id : ServletRequestUtils.getIntParameters(request, "songId")) {
 689            MediaFile song = mediaFileService.getMediaFile(id);
 690            if (song != null) {
 691                songs.add(song);
 692            }
 693        }
 694        playlistService.setFilesInPlaylist(playlist.getId(), songs);
 695
 696        XMLBuilder builder = createXMLBuilder(request, response, true);
 697        builder.endAll();
 698        response.getWriter().print(builder);
 699    }
 700
 701    public void updatePlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
 702        request = wrapRequest(request, true);
 703        String username = securityService.getCurrentUsername(request);
 704
 705        int id = ServletRequestUtils.getRequiredIntParameter(request, "playlistId");
 706        Playlist playlist = playlistService.getPlaylist(id);
 707        if (playlist == null) {
 708            error(request, response, ErrorCode.NOT_FOUND, "Playlist not found: " + id);
 709            return;
 710        }
 711        if (!playlistService.isWriteAllowed(playlist, username)) {
 712            error(request, response, ErrorCode.NOT_AUTHORIZED, "Permission denied for playlist " + id);
 713            return;
 714        }
 715
 716        String name = request.getParameter("name");
 717        if (name != null) {
 718            playlist.setName(name);
 719        }
 720        String comment = request.getParameter("comment");
 721        if (comment != null) {
 722            playlist.setComment(comment);
 723        }
 724        Boolean isPublic = ServletRequestUtils.getBooleanParameter(request, "public");
 725        if (isPublic != null) {
 726            playlist.setPublic(isPublic);
 727        }
 728        playlistService.updatePlaylist(playlist);
 729
 730        // TODO: Add later
 731//            for (String usernameToAdd : ServletRequestUtils.getStringParameters(request, "usernameToAdd")) {
 732//                if (securityService.getUserByName(usernameToAdd) != null) {
 733//                    playlistService.addPlaylistUser(id, usernameToAdd);
 734//                }
 735//            }
 736//            for (String usernameToRemove : ServletRequestUtils.getStringParameters(request, "usernameToRemove")) {
 737//                if (securityService.getUserByName(usernameToRemove) != null) {
 738//                    playlistService.deletePlaylistUser(id, usernameToRemove);
 739//                }
 740//            }
 741        List<MediaFile> songs = playlistService.getFilesInPlaylist(id);
 742        boolean songsChanged = false;
 743
 744        SortedSet<Integer> tmp = new TreeSet<Integer>();
 745        for (int songIndexToRemove : ServletRequestUtils.getIntParameters(request, "songIndexToRemove")) {
 746            tmp.add(songIndexToRemove);
 747        }
 748        List<Integer> songIndexesToRemove = new ArrayList<Integer>(tmp);
 749        Collections.reverse(songIndexesToRemove);
 750        for (Integer songIndexToRemove : songIndexesToRemove) {
 751            songs.remove(songIndexToRemove.intValue());
 752            songsChanged = true;
 753        }
 754        for (int songToAdd : ServletRequestUtils.getIntParameters(request, "songIdToAdd")) {
 755            MediaFile song = mediaFileService.getMediaFile(songToAdd);
 756            if (song != null) {
 757                songs.add(song);
 758                songsChanged = true;
 759            }
 760        }
 761        if (songsChanged) {
 762            playlistService.setFilesInPlaylist(id, songs);
 763        }
 764
 765        XMLBuilder builder = createXMLBuilder(request, response, true);
 766        builder.endAll();
 767        response.getWriter().print(builder);
 768    }
 769
 770    public void deletePlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
 771        request = wrapRequest(request, true);
 772        String username = securityService.getCurrentUsername(request);
 773
 774        int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
 775        Playlist playlist = playlistService.getPlaylist(id);
 776        if (playlist == null) {
 777            error(request, response, ErrorCode.NOT_FOUND, "Playlist not found: " + id);
 778            return;
 779        }
 780        if (!playlistService.isWriteAllowed(playlist, username)) {
 781            error(request, response, ErrorCode.NOT_AUTHORIZED, "Permission denied for playlist " + id);
 782            return;
 783        }
 784        playlistService.deletePlaylist(id);
 785
 786        XMLBuilder builder = createXMLBuilder(request, response, true);
 787        builder.endAll();
 788        response.getWriter().print(builder);
 789    }
 790
 791    public void getAlbumList(HttpServletRequest request, HttpServletResponse response) throws Exception {
 792        request = wrapRequest(request);
 793        Player player = playerService.getPlayer(request, response);
 794        String username = securityService.getCurrentUsername(request);
 795
 796        XMLBuilder builder = createXMLBuilder(request, response, true);
 797        builder.add("albumList", false);
 798
 799        int size = ServletRequestUtils.getIntParameter(request, "size", 10);
 800        int offset = ServletRequestUtils.getIntParameter(request, "offset", 0);
 801        size = Math.max(0, Math.min(size, 500));
 802        String type = ServletRequestUtils.getRequiredStringParameter(request, "type");
 803
 804        List<HomeController.Album> albums;
 805        if ("highest".equals(type)) {
 806            albums = homeController.getHighestRated(offset, size);
 807        } else if ("frequent".equals(type)) {
 808            albums = homeController.getMostFrequent(offset, size);
 809        } else if ("recent".equals(type)) {
 810            albums = homeController.getMostRecent(offset, size);
 811        } else if ("newest".equals(type)) {
 812            albums = homeController.getNewest(offset, size);
 813        } else if ("starred".equals(type)) {
 814            albums = homeController.getStarred(offset, size, username);
 815        } else if ("alphabeticalByArtist".equals(type)) {
 816            albums = homeController.getAlphabetical(offset, size, true);
 817        } else if ("alphabeticalByName".equals(type)) {
 818            albums = homeController.getAlphabetical(offset, size, false);
 819        } else if ("random".equals(type)) {
 820            albums = homeController.getRandom(size);
 821        } else {
 822            throw new Exception("Invalid list type: " + type);
 823        }
 824
 825        for (HomeController.Album album : albums) {
 826            MediaFile mediaFile = mediaFileService.getMediaFile(album.getPath());
 827            AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
 828            builder.add("album", attributes, true);
 829        }
 830        builder.endAll();
 831        response.getWriter().print(builder);
 832    }
 833
 834    public void getAlbumList2(HttpServletRequest request, HttpServletResponse response) throws Exception {
 835        request = wrapRequest(request);
 836
 837        XMLBuilder builder = createXMLBuilder(request, response, true);
 838        builder.add("albumList2", false);
 839
 840        int size = ServletRequestUtils.getIntParameter(request, "size", 10);
 841        int offset = ServletRequestUtils.getIntParameter(request, "offset", 0);
 842        size = Math.max(0, Math.min(size, 500));
 843        String type = ServletRequestUtils.getRequiredStringParameter(request, "type");
 844        String username = securityService.getCurrentUsername(request);
 845
 846        List<Album> albums;
 847        if ("frequent".equals(type)) {
 848            albums = albumDao.getMostFrequentlyPlayedAlbums(offset, size);
 849        } else if ("recent".equals(type)) {
 850            albums = albumDao.getMostRecentlyPlayedAlbums(offset, size);
 851        } else if ("newest".equals(type)) {
 852            albums = albumDao.getNewestAlbums(offset, size);
 853        } else if ("alphabeticalByArtist".equals(type)) {
 854            albums = albumDao.getAlphabetialAlbums(offset, size, true);
 855        } else if ("alphabeticalByName".equals(type)) {
 856            albums = albumDao.getAlphabetialAlbums(offset, size, false);
 857        } else if ("starred".equals(type)) {
 858            albums = albumDao.getStarredAlbums(offset, size, securityService.getCurrentUser(request).getUsername());
 859        } else if ("random".equals(type)) {
 860            albums = searchService.getRandomAlbumsId3(size);
 861        } else {
 862            throw new Exception("Invalid list type: " + type);
 863        }
 864        for (Album album : albums) {
 865            builder.add("album", createAttributesForAlbum(album, username), true);
 866        }
 867        builder.endAll();
 868        response.getWriter().print(builder);
 869    }
 870
 871    public void getRandomSongs(HttpServletRequest request, HttpServletResponse response) throws Exception {
 872        request = wrapRequest(request);
 873        Player player = playerService.getPlayer(request, response);
 874        String username = securityService.getCurrentUsername(request);
 875
 876        XMLBuilder builder = createXMLBuilder(request, response, true);
 877        builder.add("randomSongs", false);
 878
 879        int size = ServletRequestUtils.getIntParameter(request, "size", 10);
 880        size = Math.max(0, Math.min(size, 500));
 881        String genre = ServletRequestUtils.getStringParameter(request, "genre");
 882        Integer fromYear = ServletRequestUtils.getIntParameter(request, "fromYear");
 883        Integer toYear = ServletRequestUtils.getIntParameter(request, "toYear");
 884        Integer musicFolderId = ServletRequestUtils.getIntParameter(request, "musicFolderId");
 885        RandomSearchCriteria criteria = new RandomSearchCriteria(size, genre, fromYear, toYear, musicFolderId);
 886
 887        for (MediaFile mediaFile : searchService.getRandomSongs(criteria)) {
 888            AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
 889            builder.add("song", attributes, true);
 890        }
 891        builder.endAll();
 892        response.getWriter().print(builder);
 893    }
 894
 895    public void getVideos(HttpServletRequest request, HttpServletResponse response) throws Exception {
 896        request = wrapRequest(request);
 897        Player player = playerService.getPlayer(request, response);
 898        String username = securityService.getCurrentUsername(request);
 899
 900        XMLBuilder builder = createXMLBuilder(request, response, true);
 901        builder.add("videos", false);
 902        int size = ServletRequestUtils.getIntParameter(request, "size", Integer.MAX_VALUE);
 903        int offset = ServletRequestUtils.getIntParameter(request, "offset", 0);
 904
 905        for (MediaFile mediaFile : mediaFileDao.getVideos(size, offset)) {
 906            builder.add("video", createAttributesForMediaFile(player, mediaFile, username), true);
 907        }
 908        builder.endAll();
 909        response.getWriter().print(builder);
 910    }
 911
 912    public void getNowPlaying(HttpServletRequest request, HttpServletResponse response) throws Exception {
 913        request = wrapRequest(request);
 914        XMLBuilder builder = createXMLBuilder(request, response, true);
 915        builder.add("nowPlaying", false);
 916
 917        for (TransferStatus status : statusService.getAllStreamStatuses()) {
 918
 919            Player player = status.getPlayer();
 920            File file = status.getFile();
 921            if (player != null && player.getUsername() != null && file != null) {
 922
 923                String username = player.getUsername();
 924                UserSettings userSettings = settingsService.getUserSettings(username);
 925                if (!userSettings.isNowPlayingAllowed()) {
 926                    continue;
 927                }
 928
 929                MediaFile mediaFile = mediaFileService.getMediaFile(file);
 930
 931                long minutesAgo = status.getMillisSinceLastUpdate() / 1000L / 60L;
 932                if (minutesAgo < 60) {
 933                    AttributeSet attributes = createAttributesForMediaFile(player, mediaFile, username);
 934                    attributes.add("username", username);
 935                    attributes.add("playerId", player.getId());
 936                    attributes.add("playerName", player.getName());
 937                    attributes.add("minutesAgo", minutesAgo);
 938                    builder.add("entry", attributes, true);
 939                }
 940            }
 941        }
 942
 943        builder.endAll();
 944        response.getWriter().print(builder);
 945    }
 946
 947    private AttributeSet createAttributesForMediaFile(Player player, MediaFile mediaFile, String username) {
 948        MediaFile parent = mediaFileService.getParentOf(mediaFile);
 949        AttributeSet attributes = new AttributeSet();
 950        attributes.add("id", mediaFile.getId());
 951        try {
 952            if (!mediaFileService.isRoot(parent)) {
 953                attributes.add("parent", parent.getId());
 954            }
 955        } catch (SecurityException x) {
 956            // Ignored.
 957        }
 958        attributes.add("title", mediaFile.getName());
 959        attributes.add("album", mediaFile.getAlbumName());
 960        attributes.add("artist", mediaFile.getArtist());
 961        attributes.add("isDir", mediaFile.isDirectory());
 962        attributes.add("coverArt", findCoverArt(mediaFile, parent));
 963        attributes.add("created", StringUtil.toISO8601(mediaFile.getCreated()));
 964        attributes.add("starred", StringUtil.toISO8601(mediaFileDao.getMediaFileStarredDate(mediaFile.getId(), username)));
 965        attributes.add("userRating", ratingService.getRatingForUser(username, mediaFile));
 966        attributes.add("averageRating", ratingService.getAverageRating(mediaFile));
 967
 968        if (mediaFile.isFile()) {
 969            attributes.add("duration", mediaFile.getDurationSeconds());
 970            attributes.add("bitRate", mediaFile.getBitRate());
 971            attributes.add("track", mediaFile.getTrackNumber());
 972            attributes.add("discNumber", mediaFile.getDiscNumber());
 973            attributes.add("year", mediaFile.getYear());
 974            attributes.add("genre", mediaFile.getGenre());
 975            attributes.add("size", mediaFile.getFileSize());
 976            String suffix = mediaFile.getFormat();
 977            attributes.add("suffix", suffix);
 978            attributes.add("contentType", StringUtil.getMimeType(suffix));
 979            attributes.add("isVideo", mediaFile.isVideo());
 980            attributes.add("path", getRelativePath(mediaFile));
 981
 982            if (mediaFile.getArtist() != null && mediaFile.getAlbumName() != null) {
 983                Album album = albumDao.getAlbum(mediaFile.getAlbumArtist(), mediaFile.getAlbumName());
 984                if (album != null) {
 985                    attributes.add("albumId", album.getId());
 986                }
 987            }
 988            if (mediaFile.getArtist() != null) {
 989                Artist artist = artistDao.getArtist(mediaFile.getArtist());
 990                if (artist != null) {
 991                    attributes.add("artistId", artist.getId());
 992                }
 993            }
 994            switch (mediaFile.getMediaType()) {
 995                case MUSIC:
 996                    attributes.add("type", "music");
 997                    break;
 998                case PODCAST:
 999                    attributes.add("type", "podcast");
1000                    break;
1001                case AUDIOBOOK:
1002                    attributes.add("type", "audiobook");
1003                    break;
1004                default:
1005                    break;
1006            }
1007
1008            if (transcodingService.isTranscodingRequired(mediaFile, player)) {
1009                String transcodedSuffix = transcodingService.getSuffix(player, mediaFile, null);
1010                attributes.add("transcodedSuffix", transcodedSuffix);
1011                attributes.add("transcodedContentType", StringUtil.getMimeType(transcodedSuffix));
1012            }
1013        }
1014        return attributes;
1015    }
1016
1017    private Integer findCoverArt(MediaFile mediaFile, MediaFile parent) {
1018        MediaFile dir = mediaFile.isDirectory() ? mediaFile : parent;
1019        if (dir != null && dir.getCoverArtPath() != null) {
1020            return dir.getId();
1021        }
1022        return null;
1023    }
1024
1025    private String getRelativePath(MediaFile musicFile) {
1026
1027        String filePath = musicFile.getPath();
1028
1029        // Convert slashes.
1030        filePath = filePath.replace('\\', '/');
1031
1032        String filePathLower = filePath.toLowerCase();
1033
1034        List<MusicFolder> musicFolders = settingsService.getAllMusicFolders(false, true);
1035        for (MusicFolder musicFolder : musicFolders) {
1036            String folderPath = musicFolder.getPath().getPath();
1037            folderPath = folderPath.replace('\\', '/');
1038            String folderPathLower = folderPath.toLowerCase();
1039
1040            if (filePathLower.startsWith(folderPathLower)) {
1041                String relativePath = filePath.substring(folderPath.length());
1042                return relativePath.startsWith("/") ? relativePath.substring(1) : relativePath;
1043            }
1044        }
1045
1046        return null;
1047    }
1048
1049    public ModelAndView download(HttpServletRequest request, HttpServletResponse response) throws Exception {
1050        request = wrapRequest(request);
1051        User user = securityService.getCurrentUser(request);
1052        if (!user.isDownloadRole()) {
1053            error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to download files.");
1054            return null;
1055        }
1056
1057        long ifModifiedSince = request.getDateHeader("If-Modified-Since");
1058        long lastModified = downloadController.getLastModified(request);
1059
1060        if (ifModifiedSince != -1 && lastModified != -1 && lastModified <= ifModifiedSince) {
1061            response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
1062            return null;
1063        }
1064
1065        if (lastModified != -1) {
1066            response.setDateHeader("Last-Modified", lastModified);
1067        }
1068
1069        return downloadController.handleRequest(request, response);
1070    }
1071
1072    public ModelAndView stream(HttpServletRequest request, HttpServletResponse response) throws Exception {
1073        request = wrapRequest(request);
1074        User user = securityService.getCurrentUser(request);
1075        if (!user.isStreamRole

Large files files are truncated, but you can click here to view the full file