/*
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 .
Copyright 2009 (C) Sindre Mehus
*/
package net.sourceforge.subsonic.ajax;
import java.io.IOException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sourceforge.subsonic.dao.MediaFileDao;
import net.sourceforge.subsonic.domain.Playlist;
import net.sourceforge.subsonic.service.*;
import net.sourceforge.subsonic.service.PlaylistService;
import org.directwebremoting.WebContextFactory;
import org.springframework.web.servlet.support.RequestContextUtils;
import net.sourceforge.subsonic.domain.MediaFile;
import net.sourceforge.subsonic.domain.Player;
import net.sourceforge.subsonic.domain.PlayQueue;
import net.sourceforge.subsonic.util.StringUtil;
/**
* Provides AJAX-enabled services for manipulating the play queue of a player.
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
*
* @author Sindre Mehus
*/
public class PlayQueueService {
private PlayerService playerService;
private JukeboxService jukeboxService;
private TranscodingService transcodingService;
private SettingsService settingsService;
private MediaFileService mediaFileService;
private SecurityService securityService;
private MediaFileDao mediaFileDao;
private net.sourceforge.subsonic.service.PlaylistService playlistService;
/**
* Returns the play queue for the player of the current user.
*
* @return The play queue.
*/
public PlayQueueInfo getPlayQueue() throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
return convert(request, player, false);
}
public PlayQueueInfo start() throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
return doStart(request, response);
}
public PlayQueueInfo doStart(HttpServletRequest request, HttpServletResponse response) throws Exception {
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().setStatus(PlayQueue.Status.PLAYING);
return convert(request, player, true);
}
public PlayQueueInfo stop() throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
return doStop(request, response);
}
public PlayQueueInfo doStop(HttpServletRequest request, HttpServletResponse response) throws Exception {
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().setStatus(PlayQueue.Status.STOPPED);
return convert(request, player, true);
}
public PlayQueueInfo skip(int index) throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
return doSkip(request, response, index, 0);
}
public PlayQueueInfo doSkip(HttpServletRequest request, HttpServletResponse response, int index, int offset) throws Exception {
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().setIndex(index);
boolean serverSidePlaylist = !player.isExternalWithPlaylist();
return convert(request, player, serverSidePlaylist, offset);
}
public PlayQueueInfo play(int id) throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
MediaFile file = mediaFileService.getMediaFile(id);
List files = mediaFileService.getDescendantsOf(file, true);
return doPlay(request, player, files);
}
public PlayQueueInfo playPlaylist(int id) throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
List files = playlistService.getFilesInPlaylist(id);
Player player = getCurrentPlayer(request, response);
return doPlay(request, player, files);
}
private PlayQueueInfo doPlay(HttpServletRequest request, Player player, List files) throws Exception {
if (player.isWeb()) {
removeVideoFiles(files);
}
player.getPlayQueue().addFiles(false, files);
player.getPlayQueue().setRandomSearchCriteria(null);
return convert(request, player, true);
}
public PlayQueueInfo playRandom(int id, int count) throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
MediaFile file = mediaFileService.getMediaFile(id);
List randomFiles = getRandomChildren(file, count);
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().addFiles(false, randomFiles);
player.getPlayQueue().setRandomSearchCriteria(null);
return convert(request, player, true);
}
public PlayQueueInfo add(int id) throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
return doAdd(request, response, new int[]{id});
}
public PlayQueueInfo doAdd(HttpServletRequest request, HttpServletResponse response, int[] ids) throws Exception {
Player player = getCurrentPlayer(request, response);
List files = new ArrayList(ids.length);
for (int id : ids) {
MediaFile ancestor = mediaFileService.getMediaFile(id);
files.addAll(mediaFileService.getDescendantsOf(ancestor, true));
}
if (player.isWeb()) {
removeVideoFiles(files);
}
player.getPlayQueue().addFiles(true, files);
player.getPlayQueue().setRandomSearchCriteria(null);
return convert(request, player, false);
}
public PlayQueueInfo doSet(HttpServletRequest request, HttpServletResponse response, int[] ids) throws Exception {
Player player = getCurrentPlayer(request, response);
PlayQueue playQueue = player.getPlayQueue();
MediaFile currentFile = playQueue.getCurrentFile();
PlayQueue.Status status = playQueue.getStatus();
playQueue.clear();
PlayQueueInfo result = doAdd(request, response, ids);
int index = currentFile == null ? -1 : playQueue.getFiles().indexOf(currentFile);
playQueue.setIndex(index);
playQueue.setStatus(status);
return result;
}
public PlayQueueInfo clear() throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
return doClear(request, response);
}
public PlayQueueInfo doClear(HttpServletRequest request, HttpServletResponse response) throws Exception {
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().clear();
boolean serverSidePlaylist = !player.isExternalWithPlaylist();
return convert(request, player, serverSidePlaylist);
}
public PlayQueueInfo shuffle() throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
return doShuffle(request, response);
}
public PlayQueueInfo doShuffle(HttpServletRequest request, HttpServletResponse response) throws Exception {
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().shuffle();
return convert(request, player, false);
}
public PlayQueueInfo remove(int index) throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
return doRemove(request, response, index);
}
public PlayQueueInfo toggleStar(int index) throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
MediaFile file = player.getPlayQueue().getFile(index);
String username = securityService.getCurrentUsername(request);
boolean starred = mediaFileDao.getMediaFileStarredDate(file.getId(), username) != null;
if (starred) {
mediaFileDao.unstarMediaFile(file.getId(), username);
} else {
mediaFileDao.starMediaFile(file.getId(), username);
}
return convert(request, player, false);
}
public PlayQueueInfo doRemove(HttpServletRequest request, HttpServletResponse response, int index) throws Exception {
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().removeFileAt(index);
return convert(request, player, false);
}
public PlayQueueInfo removeMany(int[] indexes) throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
for (int i = indexes.length - 1; i >= 0; i--) {
player.getPlayQueue().removeFileAt(indexes[i]);
}
return convert(request, player, false);
}
public PlayQueueInfo up(int index) throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().moveUp(index);
return convert(request, player, false);
}
public PlayQueueInfo down(int index) throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().moveDown(index);
return convert(request, player, false);
}
public PlayQueueInfo toggleRepeat() throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().setRepeatEnabled(!player.getPlayQueue().isRepeatEnabled());
return convert(request, player, false);
}
public PlayQueueInfo undo() throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().undo();
boolean serverSidePlaylist = !player.isExternalWithPlaylist();
return convert(request, player, serverSidePlaylist);
}
public PlayQueueInfo sortByTrack() throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().sort(PlayQueue.SortOrder.TRACK);
return convert(request, player, false);
}
public PlayQueueInfo sortByArtist() throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().sort(PlayQueue.SortOrder.ARTIST);
return convert(request, player, false);
}
public PlayQueueInfo sortByAlbum() throws Exception {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
player.getPlayQueue().sort(PlayQueue.SortOrder.ALBUM);
return convert(request, player, false);
}
public void setGain(float gain) {
jukeboxService.setGain(gain);
}
public String savePlaylist() {
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
Player player = getCurrentPlayer(request, response);
Locale locale = settingsService.getLocale();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, locale);
Date now = new Date();
Playlist playlist = new Playlist();
playlist.setUsername(securityService.getCurrentUsername(request));
playlist.setCreated(now);
playlist.setChanged(now);
playlist.setPublic(false);
playlist.setName(dateFormat.format(now));
playlistService.createPlaylist(playlist);
playlistService.setFilesInPlaylist(playlist.getId(), player.getPlayQueue().getFiles());
return playlist.getName();
}
private List getRandomChildren(MediaFile file, int count) throws IOException {
List children = mediaFileService.getDescendantsOf(file, false);
removeVideoFiles(children);
if (children.isEmpty()) {
return children;
}
Collections.shuffle(children);
return children.subList(0, Math.min(count, children.size()));
}
private void removeVideoFiles(List files) {
Iterator iterator = files.iterator();
while (iterator.hasNext()) {
MediaFile file = iterator.next();
if (file.isVideo()) {
iterator.remove();
}
}
}
private PlayQueueInfo convert(HttpServletRequest request, Player player, boolean sendM3U) throws Exception {
return convert(request, player, sendM3U, 0);
}
private PlayQueueInfo convert(HttpServletRequest request, Player player, boolean sendM3U, int offset) throws Exception {
String url = request.getRequestURL().toString();
if (sendM3U && player.isJukebox()) {
jukeboxService.updateJukebox(player, offset);
}
boolean isCurrentPlayer = player.getIpAddress() != null && player.getIpAddress().equals(request.getRemoteAddr());
boolean m3uSupported = player.isExternal() || player.isExternalWithPlaylist();
sendM3U = player.isAutoControlEnabled() && m3uSupported && isCurrentPlayer && sendM3U;
Locale locale = RequestContextUtils.getLocale(request);
List entries = new ArrayList();
PlayQueue playQueue = player.getPlayQueue();
for (MediaFile file : playQueue.getFiles()) {
String albumUrl = url.replaceFirst("/dwr/.*", "/main.view?id=" + file.getId());
String streamUrl = url.replaceFirst("/dwr/.*", "/stream?player=" + player.getId() + "&id=" + file.getId());
// Rewrite URLs in case we're behind a proxy.
if (settingsService.isRewriteUrlEnabled()) {
String referer = request.getHeader("referer");
albumUrl = StringUtil.rewriteUrl(albumUrl, referer);
streamUrl = StringUtil.rewriteUrl(streamUrl, referer);
}
String format = formatFormat(player, file);
String username = securityService.getCurrentUsername(request);
boolean starred = mediaFileService.getMediaFileStarredDate(file.getId(), username) != null;
entries.add(new PlayQueueInfo.Entry(file.getId(), file.getTrackNumber(), file.getTitle(), file.getArtist(),
file.getAlbumName(), file.getGenre(), file.getYear(), formatBitRate(file),
file.getDurationSeconds(), file.getDurationString(), format, formatContentType(format),
formatFileSize(file.getFileSize(), locale), starred, albumUrl, streamUrl));
}
boolean isStopEnabled = playQueue.getStatus() == PlayQueue.Status.PLAYING && !player.isExternalWithPlaylist();
float gain = jukeboxService.getGain();
return new PlayQueueInfo(entries, playQueue.getIndex(), isStopEnabled, playQueue.isRepeatEnabled(), sendM3U, gain);
}
private String formatFileSize(Long fileSize, Locale locale) {
if (fileSize == null) {
return null;
}
return StringUtil.formatBytes(fileSize, locale);
}
private String formatFormat(Player player, MediaFile file) {
return transcodingService.getSuffix(player, file, null);
}
private String formatContentType(String format) {
return StringUtil.getMimeType(format);
}
private String formatBitRate(MediaFile mediaFile) {
if (mediaFile.getBitRate() == null) {
return null;
}
if (mediaFile.isVariableBitRate()) {
return mediaFile.getBitRate() + " Kbps vbr";
}
return mediaFile.getBitRate() + " Kbps";
}
private Player getCurrentPlayer(HttpServletRequest request, HttpServletResponse response) {
return playerService.getPlayer(request, response);
}
public void setPlayerService(PlayerService playerService) {
this.playerService = playerService;
}
public void setMediaFileService(MediaFileService mediaFileService) {
this.mediaFileService = mediaFileService;
}
public void setJukeboxService(JukeboxService jukeboxService) {
this.jukeboxService = jukeboxService;
}
public void setTranscodingService(TranscodingService transcodingService) {
this.transcodingService = transcodingService;
}
public void setSettingsService(SettingsService settingsService) {
this.settingsService = settingsService;
}
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
public void setMediaFileDao(MediaFileDao mediaFileDao) {
this.mediaFileDao = mediaFileDao;
}
public void setPlaylistService(PlaylistService playlistService) {
this.playlistService = playlistService;
}
}