First commit
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import net.sourceforge.subsonic.domain.Version;
|
||||
import net.sourceforge.subsonic.service.*;
|
||||
import net.sourceforge.subsonic.util.*;
|
||||
import org.apache.commons.lang.exception.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Logger implementation which logs to SUBSONIC_HOME/subsonic.log.
|
||||
* <br/>
|
||||
* Note: Third party logging libraries (such as log4j and Commons logging) are intentionally not
|
||||
* used. These libraries causes a lot of headache when deploying to some application servers
|
||||
* (for instance Jetty and JBoss).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
* @version $Revision: 1.1 $ $Date: 2005/05/09 19:58:26 $
|
||||
*/
|
||||
public class Logger {
|
||||
|
||||
private String category;
|
||||
|
||||
private static List<Entry> entries = Collections.synchronizedList(new BoundedList<Entry>(50));
|
||||
private static PrintWriter writer;
|
||||
private static Boolean debugEnabled;
|
||||
|
||||
/**
|
||||
* Creates a logger for the given class.
|
||||
* @param clazz The class.
|
||||
* @return A logger for the class.
|
||||
*/
|
||||
public static Logger getLogger(Class clazz) {
|
||||
return new Logger(clazz.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a logger for the given namee.
|
||||
* @param name The name.
|
||||
* @return A logger for the name.
|
||||
*/
|
||||
public static Logger getLogger(String name) {
|
||||
return new Logger(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last few log entries.
|
||||
* @return The last few log entries.
|
||||
*/
|
||||
public static Entry[] getLatestLogEntries() {
|
||||
return entries.toArray(new Entry[entries.size()]);
|
||||
}
|
||||
|
||||
private Logger(String name) {
|
||||
int lastDot = name.lastIndexOf('.');
|
||||
if (lastDot == -1) {
|
||||
category = name;
|
||||
} else {
|
||||
category = name.substring(lastDot + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a debug message.
|
||||
* @param message The log message.
|
||||
*/
|
||||
public void debug(Object message) {
|
||||
debug(message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a debug message.
|
||||
* @param message The message.
|
||||
* @param error The optional exception.
|
||||
*/
|
||||
public void debug(Object message, Throwable error) {
|
||||
if (isDebugEnabled()) {
|
||||
add(Level.DEBUG, message, error);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDebugEnabled() {
|
||||
if (debugEnabled == null) {
|
||||
VersionService versionService = ServiceLocator.getVersionService();
|
||||
if (versionService == null) {
|
||||
return true; // versionService not yet available.
|
||||
}
|
||||
Version localVersion = versionService.getLocalVersion();
|
||||
debugEnabled = localVersion == null || localVersion.getBeta() != 0;
|
||||
}
|
||||
return debugEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an info message.
|
||||
* @param message The message.
|
||||
*/
|
||||
public void info(Object message) {
|
||||
info(message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an info message.
|
||||
* @param message The message.
|
||||
* @param error The optional exception.
|
||||
*/
|
||||
public void info(Object message, Throwable error) {
|
||||
add(Level.INFO, message, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a warning message.
|
||||
* @param message The message.
|
||||
*/
|
||||
public void warn(Object message) {
|
||||
warn(message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a warning message.
|
||||
* @param message The message.
|
||||
* @param error The optional exception.
|
||||
*/
|
||||
public void warn(Object message, Throwable error) {
|
||||
add(Level.WARN, message, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an error message.
|
||||
* @param message The message.
|
||||
*/
|
||||
public void error(Object message) {
|
||||
error(message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an error message.
|
||||
* @param message The message.
|
||||
* @param error The optional exception.
|
||||
*/
|
||||
public void error(Object message, Throwable error) {
|
||||
add(Level.ERROR, message, error);
|
||||
}
|
||||
|
||||
private void add(Level level, Object message, Throwable error) {
|
||||
Entry entry = new Entry(category, level, message, error);
|
||||
try {
|
||||
getPrintWriter().println(entry);
|
||||
} catch (IOException x) {
|
||||
System.err.println("Failed to write to subsonic.log. " + x);
|
||||
}
|
||||
entries.add(entry);
|
||||
}
|
||||
|
||||
private static synchronized PrintWriter getPrintWriter() throws IOException {
|
||||
if (writer == null) {
|
||||
writer = new PrintWriter(new FileWriter(getLogFile(), false), true);
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
public static File getLogFile() {
|
||||
File subsonicHome = SettingsService.getSubsonicHome();
|
||||
return new File(subsonicHome, "subsonic.log");
|
||||
}
|
||||
|
||||
/**
|
||||
* Log level.
|
||||
*/
|
||||
public enum Level {
|
||||
DEBUG, INFO, WARN, ERROR
|
||||
}
|
||||
|
||||
/**
|
||||
* Log entry.
|
||||
*/
|
||||
public static class Entry {
|
||||
private String category;
|
||||
private Date date;
|
||||
private Level level;
|
||||
private Object message;
|
||||
private Throwable error;
|
||||
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
|
||||
|
||||
public Entry(String category, Level level, Object message, Throwable error) {
|
||||
this.date = new Date();
|
||||
this.category = category;
|
||||
this.level = level;
|
||||
this.message = message;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public Level getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public Object getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Throwable getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append('[').append(DATE_FORMAT.format(date)).append("] ");
|
||||
builder.append(level).append(' ');
|
||||
builder.append(category).append(" - ");
|
||||
builder.append(message);
|
||||
|
||||
if (error != null) {
|
||||
builder.append('\n').append(ExceptionUtils.getFullStackTrace(error));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 2014 (C) Sindre Mehus
|
||||
*/
|
||||
|
||||
package net.sourceforge.subsonic.ajax;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.subsonic.domain.ArtistBio;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ArtistInfo {
|
||||
|
||||
private final List<SimilarArtist> similarArtists;
|
||||
private final ArtistBio artistBio;
|
||||
private final List<TopSong> topSongs;
|
||||
|
||||
public ArtistInfo(List<SimilarArtist> similarArtists, ArtistBio artistBio, List<TopSong> topSongs) {
|
||||
this.similarArtists = similarArtists;
|
||||
this.artistBio = artistBio;
|
||||
this.topSongs = topSongs;
|
||||
}
|
||||
|
||||
public List<SimilarArtist> getSimilarArtists() {
|
||||
return similarArtists;
|
||||
}
|
||||
|
||||
public ArtistBio getArtistBio() {
|
||||
return artistBio;
|
||||
}
|
||||
|
||||
public List<TopSong> getTopSongs() {
|
||||
return topSongs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.util.BoundedList;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.directwebremoting.WebContext;
|
||||
import org.directwebremoting.WebContextFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for the chatting.
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ChatService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(ChatService.class);
|
||||
private static final String CACHE_KEY = "1";
|
||||
private static final int MAX_MESSAGES = 10;
|
||||
private static final long TTL_MILLIS = 3L * 24L * 60L * 60L * 1000L; // 3 days.
|
||||
|
||||
private final LinkedList<Message> messages = new BoundedList<Message>(MAX_MESSAGES);
|
||||
private SecurityService securityService;
|
||||
|
||||
private long revision = System.identityHashCode(this);
|
||||
|
||||
/**
|
||||
* Invoked by Spring.
|
||||
*/
|
||||
public void init() {
|
||||
// Delete old messages every hour.
|
||||
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
removeOldMessages();
|
||||
}
|
||||
};
|
||||
executor.scheduleWithFixedDelay(runnable, 0L, 3600L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private synchronized void removeOldMessages() {
|
||||
long now = System.currentTimeMillis();
|
||||
for (Iterator<Message> iterator = messages.iterator(); iterator.hasNext();) {
|
||||
Message message = iterator.next();
|
||||
if (now - message.getDate().getTime() > TTL_MILLIS) {
|
||||
iterator.remove();
|
||||
revision++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void addMessage(String message) {
|
||||
WebContext webContext = WebContextFactory.get();
|
||||
doAddMessage(message, webContext.getHttpServletRequest());
|
||||
}
|
||||
|
||||
public synchronized void doAddMessage(String message, HttpServletRequest request) {
|
||||
|
||||
String user = securityService.getCurrentUsername(request);
|
||||
message = StringUtils.trimToNull(message);
|
||||
if (message != null && user != null) {
|
||||
messages.addFirst(new Message(message, user, new Date()));
|
||||
revision++;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void clearMessages() {
|
||||
messages.clear();
|
||||
revision++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all messages, but only if the given revision is different from the
|
||||
* current revision.
|
||||
*/
|
||||
public synchronized Messages getMessages(long revision) {
|
||||
if (this.revision != revision) {
|
||||
return new Messages(new ArrayList<Message>(messages), this.revision);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public static class Messages implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -752602719879818165L;
|
||||
private final List<Message> messages;
|
||||
private final long revision;
|
||||
|
||||
public Messages(List<Message> messages, long revision) {
|
||||
this.messages = messages;
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
public List<Message> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Message implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -1907101191518133712L;
|
||||
private final String content;
|
||||
private final String username;
|
||||
private final Date date;
|
||||
|
||||
public Message(String content, String username, Date date) {
|
||||
this.content = content;
|
||||
this.username = username;
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
/**
|
||||
* Contains info about cover art images for an album.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class CoverArtInfo {
|
||||
|
||||
private final String imagePreviewUrl;
|
||||
private final String imageDownloadUrl;
|
||||
|
||||
public CoverArtInfo(String imagePreviewUrl, String imageDownloadUrl) {
|
||||
this.imagePreviewUrl = imagePreviewUrl;
|
||||
this.imageDownloadUrl = imageDownloadUrl;
|
||||
}
|
||||
|
||||
public String getImagePreviewUrl() {
|
||||
return imagePreviewUrl;
|
||||
}
|
||||
|
||||
public String getImageDownloadUrl() {
|
||||
return imageDownloadUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for changing cover art images.
|
||||
* <p/>
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class CoverArtService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(CoverArtService.class);
|
||||
|
||||
private SecurityService securityService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
/**
|
||||
* Downloads and saves the cover art at the given URL.
|
||||
*
|
||||
* @param albumId ID of the album in question.
|
||||
* @param url The image URL.
|
||||
* @return The error string if something goes wrong, <code>null</code> otherwise.
|
||||
*/
|
||||
public String setCoverArtImage(int albumId, String url) {
|
||||
try {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(albumId);
|
||||
saveCoverArt(mediaFile.getPath(), url);
|
||||
return null;
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to save cover art for album " + albumId, x);
|
||||
return x.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCoverArt(String path, String url) throws Exception {
|
||||
InputStream input = null;
|
||||
OutputStream output = null;
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
|
||||
try {
|
||||
HttpConnectionParams.setConnectionTimeout(client.getParams(), 20 * 1000); // 20 seconds
|
||||
HttpConnectionParams.setSoTimeout(client.getParams(), 20 * 1000); // 20 seconds
|
||||
HttpGet method = new HttpGet(url);
|
||||
|
||||
HttpResponse response = client.execute(method);
|
||||
input = response.getEntity().getContent();
|
||||
|
||||
// Attempt to resolve proper suffix.
|
||||
String suffix = "jpg";
|
||||
if (url.toLowerCase().endsWith(".gif")) {
|
||||
suffix = "gif";
|
||||
} else if (url.toLowerCase().endsWith(".png")) {
|
||||
suffix = "png";
|
||||
}
|
||||
|
||||
// Check permissions.
|
||||
File newCoverFile = new File(path, "cover." + suffix);
|
||||
if (!securityService.isWriteAllowed(newCoverFile)) {
|
||||
throw new Exception("Permission denied: " + StringUtil.toHtml(newCoverFile.getPath()));
|
||||
}
|
||||
|
||||
// If file exists, create a backup.
|
||||
backup(newCoverFile, new File(path, "cover." + suffix + ".backup"));
|
||||
|
||||
// Write file.
|
||||
output = new FileOutputStream(newCoverFile);
|
||||
IOUtils.copy(input, output);
|
||||
|
||||
MediaFile dir = mediaFileService.getMediaFile(path);
|
||||
|
||||
// Refresh database.
|
||||
mediaFileService.refreshMediaFile(dir);
|
||||
dir = mediaFileService.getMediaFile(dir.getId());
|
||||
|
||||
// Rename existing cover files if new cover file is not the preferred.
|
||||
try {
|
||||
while (true) {
|
||||
File coverFile = mediaFileService.getCoverArt(dir);
|
||||
if (coverFile != null && !isMediaFile(coverFile) && !newCoverFile.equals(coverFile)) {
|
||||
if (!coverFile.renameTo(new File(coverFile.getCanonicalPath() + ".old"))) {
|
||||
LOG.warn("Unable to rename old image file " + coverFile);
|
||||
break;
|
||||
}
|
||||
LOG.info("Renamed old image file " + coverFile);
|
||||
|
||||
// Must refresh again.
|
||||
mediaFileService.refreshMediaFile(dir);
|
||||
dir = mediaFileService.getMediaFile(dir.getId());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to rename existing cover file.", x);
|
||||
}
|
||||
|
||||
} finally {
|
||||
IOUtils.closeQuietly(input);
|
||||
IOUtils.closeQuietly(output);
|
||||
client.getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMediaFile(File file) {
|
||||
return !mediaFileService.filterMediaFiles(new File[]{file}).isEmpty();
|
||||
}
|
||||
|
||||
private void backup(File newCoverFile, File backup) {
|
||||
if (newCoverFile.exists()) {
|
||||
if (backup.exists()) {
|
||||
backup.delete();
|
||||
}
|
||||
if (newCoverFile.renameTo(backup)) {
|
||||
LOG.info("Backed up old image file to " + backup);
|
||||
} else {
|
||||
LOG.warn("Failed to create image file backup " + backup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
/**
|
||||
* Contains lyrics info for a song.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class LyricsInfo {
|
||||
|
||||
private final String lyrics;
|
||||
private final String artist;
|
||||
private final String title;
|
||||
private boolean tryLater;
|
||||
|
||||
public LyricsInfo() {
|
||||
this(null, null, null);
|
||||
}
|
||||
|
||||
public LyricsInfo(String lyrics, String artist, String title) {
|
||||
this.lyrics = lyrics;
|
||||
this.artist = artist;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getLyrics() {
|
||||
return lyrics;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTryLater(boolean tryLater) {
|
||||
this.tryLater = tryLater;
|
||||
}
|
||||
|
||||
public boolean isTryLater() {
|
||||
return tryLater;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.SocketException;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.ResponseHandler;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.BasicResponseHandler;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.jdom.Document;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.Namespace;
|
||||
import org.jdom.input.SAXBuilder;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for retrieving song lyrics from chartlyrics.com.
|
||||
* <p/>
|
||||
* See http://www.chartlyrics.com/api.aspx for details.
|
||||
* <p/>
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class LyricsService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(LyricsService.class);
|
||||
|
||||
/**
|
||||
* Returns lyrics for the given song and artist.
|
||||
*
|
||||
* @param artist The artist.
|
||||
* @param song The song.
|
||||
* @return The lyrics, never <code>null</code> .
|
||||
*/
|
||||
public LyricsInfo getLyrics(String artist, String song) {
|
||||
LyricsInfo lyrics = new LyricsInfo();
|
||||
try {
|
||||
|
||||
artist = StringUtil.urlEncode(artist);
|
||||
song = StringUtil.urlEncode(song);
|
||||
|
||||
String url = "http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect?artist=" + artist + "&song=" + song;
|
||||
String xml = executeGetRequest(url);
|
||||
lyrics = parseSearchResult(xml);
|
||||
|
||||
} catch (SocketException x) {
|
||||
lyrics.setTryLater(true);
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to get lyrics for song '" + song + "'.", x);
|
||||
}
|
||||
return lyrics;
|
||||
}
|
||||
|
||||
private LyricsInfo parseSearchResult(String xml) throws Exception {
|
||||
SAXBuilder builder = new SAXBuilder();
|
||||
Document document = builder.build(new StringReader(xml));
|
||||
|
||||
Element root = document.getRootElement();
|
||||
Namespace ns = root.getNamespace();
|
||||
|
||||
String lyric = StringUtils.trimToNull(root.getChildText("Lyric", ns));
|
||||
String song = root.getChildText("LyricSong", ns);
|
||||
String artist = root.getChildText("LyricArtist", ns);
|
||||
|
||||
return new LyricsInfo(lyric, artist, song);
|
||||
}
|
||||
|
||||
private String executeGetRequest(String url) throws IOException {
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
HttpConnectionParams.setConnectionTimeout(client.getParams(), 15000);
|
||||
HttpConnectionParams.setSoTimeout(client.getParams(), 15000);
|
||||
HttpGet method = new HttpGet(url);
|
||||
try {
|
||||
|
||||
ResponseHandler<String> responseHandler = new BasicResponseHandler();
|
||||
return client.execute(method, responseHandler);
|
||||
|
||||
} finally {
|
||||
client.getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.directwebremoting.WebContextFactory;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.domain.ArtistBio;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.LastFmService;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.NetworkService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Provides miscellaneous AJAX-enabled services.
|
||||
* <p/>
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MultiService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MultiService.class);
|
||||
|
||||
private NetworkService networkService;
|
||||
private MediaFileService mediaFileService;
|
||||
private LastFmService lastFmService;
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
/**
|
||||
* Returns status for port forwarding and URL redirection.
|
||||
*/
|
||||
public NetworkStatus getNetworkStatus() {
|
||||
NetworkService.Status portForwardingStatus = networkService.getPortForwardingStatus();
|
||||
NetworkService.Status urlRedirectionStatus = networkService.getURLRedirecionStatus();
|
||||
return new NetworkStatus(portForwardingStatus.getText(),
|
||||
portForwardingStatus.getDate(),
|
||||
urlRedirectionStatus.getText(),
|
||||
urlRedirectionStatus.getDate());
|
||||
}
|
||||
|
||||
public ArtistInfo getArtistInfo(int mediaFileId, int maxSimilarArtists, int maxTopSongs) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(mediaFileId);
|
||||
List<SimilarArtist> similarArtists = getSimilarArtists(mediaFileId, maxSimilarArtists);
|
||||
ArtistBio artistBio = lastFmService.getArtistBio(mediaFile);
|
||||
List<TopSong> topSongs = getTopSongs(mediaFile, maxTopSongs);
|
||||
|
||||
return new ArtistInfo(similarArtists, artistBio, topSongs);
|
||||
}
|
||||
|
||||
private List<TopSong> getTopSongs(MediaFile mediaFile, int limit) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
|
||||
List<TopSong> result = new ArrayList<TopSong>();
|
||||
List<MediaFile> files = lastFmService.getTopSongs(mediaFile, limit, musicFolders);
|
||||
mediaFileService.populateStarredDate(files, username);
|
||||
for (MediaFile file : files) {
|
||||
result.add(new TopSong(file.getId(), file.getTitle(), file.getArtist(), file.getAlbumName(),
|
||||
file.getDurationString(), file.getStarredDate() != null));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<SimilarArtist> getSimilarArtists(int mediaFileId, int limit) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
|
||||
MediaFile artist = mediaFileService.getMediaFile(mediaFileId);
|
||||
List<MediaFile> similarArtists = lastFmService.getSimilarArtists(artist, limit, false, musicFolders);
|
||||
SimilarArtist[] result = new SimilarArtist[similarArtists.size()];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
MediaFile similarArtist = similarArtists.get(i);
|
||||
result[i] = new SimilarArtist(similarArtist.getId(), similarArtist.getName());
|
||||
}
|
||||
return Arrays.asList(result);
|
||||
}
|
||||
|
||||
public void setShowSideBar(boolean show) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
userSettings.setShowSideBar(show);
|
||||
userSettings.setChanged(new Date());
|
||||
settingsService.updateUserSettings(userSettings);
|
||||
}
|
||||
|
||||
public void setNetworkService(NetworkService networkService) {
|
||||
this.networkService = networkService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setLastFmService(LastFmService lastFmService) {
|
||||
this.lastFmService = lastFmService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class NetworkStatus {
|
||||
private final String portForwardingStatusText;
|
||||
private final Date portForwardingStatusDate;
|
||||
private final String urlRedirectionStatusText;
|
||||
private final Date urlRedirectionStatusDate;
|
||||
|
||||
public NetworkStatus(String portForwardingStatusText, Date portForwardingStatusDate,
|
||||
String urlRedirectionStatusText, Date urlRedirectionStatusDate) {
|
||||
this.portForwardingStatusText = portForwardingStatusText;
|
||||
this.portForwardingStatusDate = portForwardingStatusDate;
|
||||
this.urlRedirectionStatusText = urlRedirectionStatusText;
|
||||
this.urlRedirectionStatusDate = urlRedirectionStatusDate;
|
||||
}
|
||||
|
||||
public String getPortForwardingStatusText() {
|
||||
return portForwardingStatusText;
|
||||
}
|
||||
|
||||
public Date getPortForwardingStatusDate() {
|
||||
return portForwardingStatusDate;
|
||||
}
|
||||
|
||||
public String getUrlRedirectionStatusText() {
|
||||
return urlRedirectionStatusText;
|
||||
}
|
||||
|
||||
public Date getUrlRedirectionStatusDate() {
|
||||
return urlRedirectionStatusDate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
/**
|
||||
* Details about what a user is currently listening to.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class NowPlayingInfo {
|
||||
|
||||
private final String playerId;
|
||||
private final String username;
|
||||
private final String artist;
|
||||
private final String title;
|
||||
private final String tooltip;
|
||||
private final String streamUrl;
|
||||
private final String albumUrl;
|
||||
private final String lyricsUrl;
|
||||
private final String coverArtUrl;
|
||||
private final String avatarUrl;
|
||||
private final int minutesAgo;
|
||||
|
||||
public NowPlayingInfo(String playerId, String user, String artist, String title, String tooltip, String streamUrl, String albumUrl,
|
||||
String lyricsUrl, String coverArtUrl, String avatarUrl, int minutesAgo) {
|
||||
this.playerId = playerId;
|
||||
this.username = user;
|
||||
this.artist = artist;
|
||||
this.title = title;
|
||||
this.tooltip = tooltip;
|
||||
this.streamUrl = streamUrl;
|
||||
this.albumUrl = albumUrl;
|
||||
this.lyricsUrl = lyricsUrl;
|
||||
this.coverArtUrl = coverArtUrl;
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.minutesAgo = minutesAgo;
|
||||
}
|
||||
|
||||
public String getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getTooltip() {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
public String getStreamUrl() {
|
||||
return streamUrl;
|
||||
}
|
||||
|
||||
public String getAlbumUrl() {
|
||||
return albumUrl;
|
||||
}
|
||||
|
||||
public String getLyricsUrl() {
|
||||
return lyricsUrl;
|
||||
}
|
||||
|
||||
public String getCoverArtUrl() {
|
||||
return coverArtUrl;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
public int getMinutesAgo() {
|
||||
return minutesAgo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.directwebremoting.WebContext;
|
||||
import org.directwebremoting.WebContextFactory;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.domain.AvatarScheme;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.PlayStatus;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.MediaScannerService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.StatusService;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for retrieving the currently playing file and directory.
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class NowPlayingService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(NowPlayingService.class);
|
||||
|
||||
private PlayerService playerService;
|
||||
private StatusService statusService;
|
||||
private SettingsService settingsService;
|
||||
private MediaScannerService mediaScannerService;
|
||||
|
||||
/**
|
||||
* Returns details about what the current player is playing.
|
||||
*
|
||||
* @return Details about what the current player is playing, or <code>null</code> if not playing anything.
|
||||
*/
|
||||
public NowPlayingInfo getNowPlayingForCurrentPlayer() throws Exception {
|
||||
WebContext webContext = WebContextFactory.get();
|
||||
Player player = playerService.getPlayer(webContext.getHttpServletRequest(), webContext.getHttpServletResponse());
|
||||
|
||||
for (NowPlayingInfo info : getNowPlaying()) {
|
||||
if (player.getId().equals(info.getPlayerId())) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns details about what all users are currently playing.
|
||||
*
|
||||
* @return Details about what all users are currently playing.
|
||||
*/
|
||||
public List<NowPlayingInfo> getNowPlaying() throws Exception {
|
||||
try {
|
||||
return convert(statusService.getPlayStatuses());
|
||||
} catch (Throwable x) {
|
||||
LOG.error("Unexpected error in getNowPlaying: " + x, x);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns media folder scanning status.
|
||||
*/
|
||||
public ScanInfo getScanningStatus() {
|
||||
return new ScanInfo(mediaScannerService.isScanning(), mediaScannerService.getScanCount());
|
||||
}
|
||||
|
||||
private List<NowPlayingInfo> convert(List<PlayStatus> playStatuses) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String url = request.getRequestURL().toString();
|
||||
List<NowPlayingInfo> result = new ArrayList<NowPlayingInfo>();
|
||||
for (PlayStatus status : playStatuses) {
|
||||
|
||||
Player player = status.getPlayer();
|
||||
MediaFile mediaFile = status.getMediaFile();
|
||||
String username = player.getUsername();
|
||||
if (username == null) {
|
||||
continue;
|
||||
}
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
if (!userSettings.isNowPlayingAllowed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String artist = mediaFile.getArtist();
|
||||
String title = mediaFile.getTitle();
|
||||
String streamUrl = url.replaceFirst("/dwr/.*", "/stream?player=" + player.getId() + "&id=" + mediaFile.getId());
|
||||
String albumUrl = url.replaceFirst("/dwr/.*", "/main.view?id=" + mediaFile.getId());
|
||||
String lyricsUrl = null;
|
||||
if (!mediaFile.isVideo()) {
|
||||
lyricsUrl = url.replaceFirst("/dwr/.*", "/lyrics.view?artistUtf8Hex=" + StringUtil.utf8HexEncode(artist) +
|
||||
"&songUtf8Hex=" + StringUtil.utf8HexEncode(title));
|
||||
}
|
||||
String coverArtUrl = url.replaceFirst("/dwr/.*", "/coverArt.view?size=60&id=" + mediaFile.getId());
|
||||
|
||||
String avatarUrl = null;
|
||||
if (userSettings.getAvatarScheme() == AvatarScheme.SYSTEM) {
|
||||
avatarUrl = url.replaceFirst("/dwr/.*", "/avatar.view?id=" + userSettings.getSystemAvatarId());
|
||||
} else if (userSettings.getAvatarScheme() == AvatarScheme.CUSTOM && settingsService.getCustomAvatar(username) != null) {
|
||||
avatarUrl = url.replaceFirst("/dwr/.*", "/avatar.view?usernameUtf8Hex=" + StringUtil.utf8HexEncode(username));
|
||||
}
|
||||
|
||||
// Rewrite URLs in case we're behind a proxy.
|
||||
if (settingsService.isRewriteUrlEnabled()) {
|
||||
String referer = request.getHeader("referer");
|
||||
streamUrl = StringUtil.rewriteUrl(streamUrl, referer);
|
||||
albumUrl = StringUtil.rewriteUrl(albumUrl, referer);
|
||||
lyricsUrl = StringUtil.rewriteUrl(lyricsUrl, referer);
|
||||
coverArtUrl = StringUtil.rewriteUrl(coverArtUrl, referer);
|
||||
avatarUrl = StringUtil.rewriteUrl(avatarUrl, referer);
|
||||
}
|
||||
|
||||
String tooltip = StringUtil.toHtml(artist) + " – " + StringUtil.toHtml(title);
|
||||
|
||||
if (StringUtils.isNotBlank(player.getName())) {
|
||||
username += "@" + player.getName();
|
||||
}
|
||||
artist = StringUtil.toHtml(StringUtils.abbreviate(artist, 25));
|
||||
title = StringUtil.toHtml(StringUtils.abbreviate(title, 25));
|
||||
username = StringUtil.toHtml(StringUtils.abbreviate(username, 25));
|
||||
|
||||
long minutesAgo = status.getMinutesAgo();
|
||||
|
||||
if (minutesAgo < 60) {
|
||||
result.add(new NowPlayingInfo(player.getId(),username, artist, title, tooltip, streamUrl, albumUrl, lyricsUrl,
|
||||
coverArtUrl, avatarUrl, (int) minutesAgo));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setStatusService(StatusService statusService) {
|
||||
this.statusService = statusService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setMediaScannerService(MediaScannerService mediaScannerService) {
|
||||
this.mediaScannerService = mediaScannerService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
/**
|
||||
* The playlist of a player.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlayQueueInfo {
|
||||
|
||||
private final List<Entry> entries;
|
||||
private final boolean stopEnabled;
|
||||
private final boolean repeatEnabled;
|
||||
private final boolean sendM3U;
|
||||
private final float gain;
|
||||
private int startPlayerAt = -1;
|
||||
private long startPlayerAtPosition; // millis
|
||||
|
||||
public PlayQueueInfo(List<Entry> entries, boolean stopEnabled, boolean repeatEnabled, boolean sendM3U, float gain) {
|
||||
this.entries = entries;
|
||||
this.stopEnabled = stopEnabled;
|
||||
this.repeatEnabled = repeatEnabled;
|
||||
this.sendM3U = sendM3U;
|
||||
this.gain = gain;
|
||||
}
|
||||
|
||||
public List<Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public String getDurationAsString() {
|
||||
int durationSeconds = 0;
|
||||
for (Entry entry : entries) {
|
||||
if (entry.getDuration() != null) {
|
||||
durationSeconds += entry.getDuration();
|
||||
}
|
||||
}
|
||||
return StringUtil.formatDuration(durationSeconds);
|
||||
}
|
||||
|
||||
public boolean isStopEnabled() {
|
||||
return stopEnabled;
|
||||
}
|
||||
|
||||
public boolean isSendM3U() {
|
||||
return sendM3U;
|
||||
}
|
||||
|
||||
public boolean isRepeatEnabled() {
|
||||
return repeatEnabled;
|
||||
}
|
||||
|
||||
public float getGain() {
|
||||
return gain;
|
||||
}
|
||||
|
||||
public int getStartPlayerAt() {
|
||||
return startPlayerAt;
|
||||
}
|
||||
|
||||
public PlayQueueInfo setStartPlayerAt(int startPlayerAt) {
|
||||
this.startPlayerAt = startPlayerAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getStartPlayerAtPosition() {
|
||||
return startPlayerAtPosition;
|
||||
}
|
||||
|
||||
public PlayQueueInfo setStartPlayerAtPosition(long startPlayerAtPosition) {
|
||||
this.startPlayerAtPosition = startPlayerAtPosition;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class Entry {
|
||||
private final int id;
|
||||
private final Integer trackNumber;
|
||||
private final String title;
|
||||
private final String artist;
|
||||
private final String album;
|
||||
private final String genre;
|
||||
private final Integer year;
|
||||
private final String bitRate;
|
||||
private final Integer duration;
|
||||
private final String durationAsString;
|
||||
private final String format;
|
||||
private final String contentType;
|
||||
private final String fileSize;
|
||||
private final boolean starred;
|
||||
private final String albumUrl;
|
||||
private final String streamUrl;
|
||||
private final String remoteStreamUrl;
|
||||
private final String coverArtUrl;
|
||||
private final String remoteCoverArtUrl;
|
||||
|
||||
public Entry(int id, Integer trackNumber, String title, String artist, String album, String genre, Integer year,
|
||||
String bitRate, Integer duration, String durationAsString, String format, String contentType, String fileSize,
|
||||
boolean starred, String albumUrl, String streamUrl, String remoteStreamUrl, String coverArtUrl, String remoteCoverArtUrl) {
|
||||
this.id = id;
|
||||
this.trackNumber = trackNumber;
|
||||
this.title = title;
|
||||
this.artist = artist;
|
||||
this.album = album;
|
||||
this.genre = genre;
|
||||
this.year = year;
|
||||
this.bitRate = bitRate;
|
||||
this.duration = duration;
|
||||
this.durationAsString = durationAsString;
|
||||
this.format = format;
|
||||
this.contentType = contentType;
|
||||
this.fileSize = fileSize;
|
||||
this.starred = starred;
|
||||
this.albumUrl = albumUrl;
|
||||
this.streamUrl = streamUrl;
|
||||
this.remoteStreamUrl = remoteStreamUrl;
|
||||
this.coverArtUrl = coverArtUrl;
|
||||
this.remoteCoverArtUrl = remoteCoverArtUrl;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Integer getTrackNumber() {
|
||||
return trackNumber;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public String getGenre() {
|
||||
return genre;
|
||||
}
|
||||
|
||||
public Integer getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
public String getBitRate() {
|
||||
return bitRate;
|
||||
}
|
||||
|
||||
public String getDurationAsString() {
|
||||
return durationAsString;
|
||||
}
|
||||
|
||||
public Integer getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public String getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public boolean isStarred() {
|
||||
return starred;
|
||||
}
|
||||
|
||||
public String getAlbumUrl() {
|
||||
return albumUrl;
|
||||
}
|
||||
|
||||
public String getStreamUrl() {
|
||||
return streamUrl;
|
||||
}
|
||||
|
||||
public String getRemoteStreamUrl() {
|
||||
return remoteStreamUrl;
|
||||
}
|
||||
|
||||
public String getCoverArtUrl() {
|
||||
return coverArtUrl;
|
||||
}
|
||||
|
||||
public String getRemoteCoverArtUrl() {
|
||||
return remoteCoverArtUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,737 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
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 org.directwebremoting.WebContextFactory;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.sourceforge.subsonic.dao.MediaFileDao;
|
||||
import net.sourceforge.subsonic.dao.PlayQueueDao;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.PlayQueue;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.PodcastEpisode;
|
||||
import net.sourceforge.subsonic.domain.PodcastStatus;
|
||||
import net.sourceforge.subsonic.domain.SavedPlayQueue;
|
||||
import net.sourceforge.subsonic.domain.UrlRedirectType;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.JukeboxService;
|
||||
import net.sourceforge.subsonic.service.LastFmService;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.PlaylistService;
|
||||
import net.sourceforge.subsonic.service.PodcastService;
|
||||
import net.sourceforge.subsonic.service.RatingService;
|
||||
import net.sourceforge.subsonic.service.SearchService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.TranscodingService;
|
||||
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
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public class PlayQueueService {
|
||||
|
||||
private PlayerService playerService;
|
||||
private JukeboxService jukeboxService;
|
||||
private TranscodingService transcodingService;
|
||||
private SettingsService settingsService;
|
||||
private MediaFileService mediaFileService;
|
||||
private LastFmService lastFmService;
|
||||
private SecurityService securityService;
|
||||
private SearchService searchService;
|
||||
private RatingService ratingService;
|
||||
private PodcastService podcastService;
|
||||
private net.sourceforge.subsonic.service.PlaylistService playlistService;
|
||||
private MediaFileDao mediaFileDao;
|
||||
private PlayQueueDao playQueueDao;
|
||||
|
||||
/**
|
||||
* 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 void savePlayQueue(int currentSongIndex, long positionMillis) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
List<Integer> ids = MediaFile.toIdList(playQueue.getFiles());
|
||||
|
||||
Integer currentId = currentSongIndex == -1 ? null : playQueue.getFile(currentSongIndex).getId();
|
||||
SavedPlayQueue savedPlayQueue = new SavedPlayQueue(null, username, ids, currentId, positionMillis, new Date(), "Subsonic");
|
||||
playQueueDao.savePlayQueue(savedPlayQueue);
|
||||
}
|
||||
|
||||
public PlayQueueInfo loadPlayQueue() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
SavedPlayQueue savedPlayQueue = playQueueDao.getPlayQueue(username);
|
||||
|
||||
if (savedPlayQueue == null) {
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
playQueue.clear();
|
||||
for (Integer mediaFileId : savedPlayQueue.getMediaFileIds()) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(mediaFileId);
|
||||
if (mediaFile != null) {
|
||||
playQueue.addFiles(true, mediaFile);
|
||||
}
|
||||
}
|
||||
PlayQueueInfo result = convert(request, player, false);
|
||||
|
||||
Integer currentId = savedPlayQueue.getCurrentMediaFileId();
|
||||
int currentIndex = -1;
|
||||
long positionMillis = savedPlayQueue.getPositionMillis() == null ? 0L : savedPlayQueue.getPositionMillis();
|
||||
if (currentId != null) {
|
||||
MediaFile current = mediaFileService.getMediaFile(currentId);
|
||||
currentIndex = playQueue.getFiles().indexOf(current);
|
||||
if (currentIndex != -1) {
|
||||
result.setStartPlayerAt(currentIndex);
|
||||
result.setStartPlayerAtPosition(positionMillis);
|
||||
}
|
||||
}
|
||||
|
||||
boolean serverSidePlaylist = !player.isExternalWithPlaylist();
|
||||
if (serverSidePlaylist && currentIndex != -1) {
|
||||
doSkip(request, response, currentIndex, (int) (positionMillis / 1000L));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (file.isFile()) {
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean queueFollowingSongs = settingsService.getUserSettings(username).isQueueFollowingSongs();
|
||||
List<MediaFile> songs;
|
||||
if (queueFollowingSongs) {
|
||||
MediaFile dir = mediaFileService.getParentOf(file);
|
||||
songs = mediaFileService.getChildrenOf(dir, true, false, true);
|
||||
if (!songs.isEmpty()) {
|
||||
int index = songs.indexOf(file);
|
||||
songs = songs.subList(index, songs.size());
|
||||
}
|
||||
} else {
|
||||
songs = Arrays.asList(file);
|
||||
}
|
||||
return doPlay(request, player, songs).setStartPlayerAt(0);
|
||||
} else {
|
||||
List<MediaFile> songs = mediaFileService.getDescendantsOf(file, true);
|
||||
return doPlay(request, player, songs).setStartPlayerAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index Start playing at this index, or play whole playlist if {@code null}.
|
||||
*/
|
||||
public PlayQueueInfo playPlaylist(int id, Integer index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean queueFollowingSongs = settingsService.getUserSettings(username).isQueueFollowingSongs();
|
||||
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
if (!files.isEmpty() && index != null) {
|
||||
if (queueFollowingSongs) {
|
||||
files = files.subList(index, files.size());
|
||||
} else {
|
||||
files = Arrays.asList(files.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove non-present files
|
||||
Iterator<MediaFile> iterator = files.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
MediaFile file = iterator.next();
|
||||
if (!file.isPresent()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index Start playing at this index, or play all top songs if {@code null}.
|
||||
*/
|
||||
public PlayQueueInfo playTopSong(int id, Integer index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean queueFollowingSongs = settingsService.getUserSettings(username).isQueueFollowingSongs();
|
||||
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
List<MediaFile> files = lastFmService.getTopSongs(mediaFileService.getMediaFile(id), 50, musicFolders);
|
||||
if (!files.isEmpty() && index != null) {
|
||||
if (queueFollowingSongs) {
|
||||
files = files.subList(index, files.size());
|
||||
} else {
|
||||
files = Arrays.asList(files.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playPodcastChannel(int id) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
List<PodcastEpisode> episodes = podcastService.getEpisodes(id);
|
||||
List<MediaFile> files = new ArrayList<MediaFile>();
|
||||
for (PodcastEpisode episode : episodes) {
|
||||
if (episode.getStatus() == PodcastStatus.COMPLETED) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(episode.getMediaFileId());
|
||||
if (mediaFile != null && mediaFile.isPresent()) {
|
||||
files.add(mediaFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playPodcastEpisode(int id) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
PodcastEpisode episode = podcastService.getEpisode(id, false);
|
||||
List<PodcastEpisode> allEpisodes = podcastService.getEpisodes(episode.getChannelId());
|
||||
List<MediaFile> files = new ArrayList<MediaFile>();
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean queueFollowingSongs = settingsService.getUserSettings(username).isQueueFollowingSongs();
|
||||
|
||||
for (PodcastEpisode ep : allEpisodes) {
|
||||
if (ep.getStatus() == PodcastStatus.COMPLETED) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(ep.getMediaFileId());
|
||||
if (mediaFile != null && mediaFile.isPresent() &&
|
||||
(ep.getId().equals(episode.getId()) || queueFollowingSongs && !files.isEmpty())) {
|
||||
files.add(mediaFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playNewestPodcastEpisode(Integer index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
List<PodcastEpisode> episodes = podcastService.getNewestEpisodes(10);
|
||||
List<MediaFile> files = Lists.transform(episodes, new Function<PodcastEpisode, MediaFile>() {
|
||||
@Override
|
||||
public MediaFile apply(PodcastEpisode episode) {
|
||||
return mediaFileService.getMediaFile(episode.getMediaFileId());
|
||||
}
|
||||
});
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean queueFollowingSongs = settingsService.getUserSettings(username).isQueueFollowingSongs();
|
||||
|
||||
if (!files.isEmpty() && index != null) {
|
||||
if (queueFollowingSongs) {
|
||||
files = files.subList(index, files.size());
|
||||
} else {
|
||||
files = Arrays.asList(files.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playStarred() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
List<MediaFile> files = mediaFileDao.getStarredFiles(0, Integer.MAX_VALUE, username, musicFolders);
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playShuffle(String albumListType, int offset, int count, String genre, String decade) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(securityService.getCurrentUsername(request));
|
||||
|
||||
MusicFolder selectedMusicFolder = settingsService.getSelectedMusicFolder(username);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username,
|
||||
selectedMusicFolder == null ? null : selectedMusicFolder.getId());
|
||||
List<MediaFile> albums;
|
||||
if ("highest".equals(albumListType)) {
|
||||
albums = ratingService.getHighestRatedAlbums(offset, count, musicFolders);
|
||||
} else if ("frequent".equals(albumListType)) {
|
||||
albums = mediaFileService.getMostFrequentlyPlayedAlbums(offset, count, musicFolders);
|
||||
} else if ("recent".equals(albumListType)) {
|
||||
albums = mediaFileService.getMostRecentlyPlayedAlbums(offset, count, musicFolders);
|
||||
} else if ("newest".equals(albumListType)) {
|
||||
albums = mediaFileService.getNewestAlbums(offset, count, musicFolders);
|
||||
} else if ("starred".equals(albumListType)) {
|
||||
albums = mediaFileService.getStarredAlbums(offset, count, username, musicFolders);
|
||||
} else if ("random".equals(albumListType)) {
|
||||
albums = searchService.getRandomAlbums(count, musicFolders);
|
||||
} else if ("alphabetical".equals(albumListType)) {
|
||||
albums = mediaFileService.getAlphabeticalAlbums(offset, count, true, musicFolders);
|
||||
} else if ("decade".equals(albumListType)) {
|
||||
int fromYear = Integer.parseInt(decade);
|
||||
int toYear = fromYear + 9;
|
||||
albums = mediaFileService.getAlbumsByYear(offset, count, fromYear, toYear, musicFolders);
|
||||
} else if ("genre".equals(albumListType)) {
|
||||
albums = mediaFileService.getAlbumsByGenre(offset, count, genre, musicFolders);
|
||||
} else {
|
||||
albums = Collections.emptyList();
|
||||
}
|
||||
|
||||
List<MediaFile> songs = new ArrayList<MediaFile>();
|
||||
for (MediaFile album : albums) {
|
||||
songs.addAll(mediaFileService.getChildrenOf(album, true, false, false));
|
||||
}
|
||||
Collections.shuffle(songs);
|
||||
songs = songs.subList(0, Math.min(40, songs.size()));
|
||||
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, songs).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
private PlayQueueInfo doPlay(HttpServletRequest request, Player player, List<MediaFile> files) throws Exception {
|
||||
if (player.isWeb()) {
|
||||
mediaFileService.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<MediaFile> randomFiles = mediaFileService.getRandomSongsForParent(file, count);
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().addFiles(false, randomFiles);
|
||||
player.getPlayQueue().setRandomSearchCriteria(null);
|
||||
return convert(request, player, true).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playSimilar(int id, int count) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
MediaFile artist = mediaFileService.getMediaFile(id);
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
List<MediaFile> similarSongs = lastFmService.getSimilarSongs(artist, count, musicFolders);
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().addFiles(false, similarSongs);
|
||||
return convert(request, player, true).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo add(int id) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
return doAdd(request, response, new int[]{id}, null);
|
||||
}
|
||||
|
||||
public PlayQueueInfo addAt(int id, int index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
return doAdd(request, response, new int[]{id}, index);
|
||||
}
|
||||
|
||||
public PlayQueueInfo doAdd(HttpServletRequest request, HttpServletResponse response, int[] ids, Integer index) throws Exception {
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
List<MediaFile> files = new ArrayList<MediaFile>(ids.length);
|
||||
for (int id : ids) {
|
||||
MediaFile ancestor = mediaFileService.getMediaFile(id);
|
||||
files.addAll(mediaFileService.getDescendantsOf(ancestor, true));
|
||||
}
|
||||
if (player.isWeb()) {
|
||||
mediaFileService.removeVideoFiles(files);
|
||||
}
|
||||
if (index != null) {
|
||||
player.getPlayQueue().addFilesAt(files, index);
|
||||
} else {
|
||||
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, null);
|
||||
|
||||
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 rearrange(int[] indexes) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().rearrange(indexes);
|
||||
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);
|
||||
}
|
||||
|
||||
private PlayQueueInfo convert(HttpServletRequest request, Player player, boolean serverSidePlaylist) throws Exception {
|
||||
return convert(request, player, serverSidePlaylist, 0);
|
||||
}
|
||||
|
||||
private PlayQueueInfo convert(HttpServletRequest request, Player player, boolean serverSidePlaylist, int offset) throws Exception {
|
||||
String url = request.getRequestURL().toString();
|
||||
|
||||
if (serverSidePlaylist && player.isJukebox()) {
|
||||
jukeboxService.updateJukebox(player, offset);
|
||||
}
|
||||
boolean isCurrentPlayer = player.getIpAddress() != null && player.getIpAddress().equals(request.getRemoteAddr());
|
||||
|
||||
boolean m3uSupported = player.isExternal() || player.isExternalWithPlaylist();
|
||||
serverSidePlaylist = player.isAutoControlEnabled() && m3uSupported && isCurrentPlayer && serverSidePlaylist;
|
||||
Locale locale = RequestContextUtils.getLocale(request);
|
||||
|
||||
List<PlayQueueInfo.Entry> entries = new ArrayList<PlayQueueInfo.Entry>();
|
||||
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());
|
||||
String coverArtUrl = url.replaceFirst("/dwr/.*", "/coverArt.view?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 remoteStreamUrl = settingsService.rewriteRemoteUrl(streamUrl);
|
||||
String remoteCoverArtUrl = settingsService.rewriteRemoteUrl(coverArtUrl);
|
||||
|
||||
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, remoteStreamUrl,
|
||||
coverArtUrl, remoteCoverArtUrl));
|
||||
}
|
||||
boolean isStopEnabled = playQueue.getStatus() == PlayQueue.Status.PLAYING && !player.isExternalWithPlaylist();
|
||||
float gain = jukeboxService.getGain();
|
||||
return new PlayQueueInfo(entries, isStopEnabled, playQueue.isRepeatEnabled(), serverSidePlaylist, 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 setLastFmService(LastFmService lastFmService) {
|
||||
this.lastFmService = lastFmService;
|
||||
}
|
||||
|
||||
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 setSearchService(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
public void setRatingService(RatingService ratingService) {
|
||||
this.ratingService = ratingService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
|
||||
public void setMediaFileDao(MediaFileDao mediaFileDao) {
|
||||
this.mediaFileDao = mediaFileDao;
|
||||
}
|
||||
|
||||
public void setPlayQueueDao(PlayQueueDao playQueueDao) {
|
||||
this.playQueueDao = playQueueDao;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.Playlist;
|
||||
|
||||
/**
|
||||
* The playlist of a player.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlaylistInfo {
|
||||
|
||||
private final Playlist playlist;
|
||||
private final List<Entry> entries;
|
||||
|
||||
public PlaylistInfo(Playlist playlist, List<Entry> entries) {
|
||||
this.playlist = playlist;
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
public Playlist getPlaylist() {
|
||||
return playlist;
|
||||
}
|
||||
|
||||
public List<Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static class Entry {
|
||||
private final int id;
|
||||
private final String title;
|
||||
private final String artist;
|
||||
private final String album;
|
||||
private final String durationAsString;
|
||||
private final boolean starred;
|
||||
private final boolean present;
|
||||
|
||||
public Entry(int id, String title, String artist, String album, String durationAsString, boolean starred, boolean present) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.artist = artist;
|
||||
this.album = album;
|
||||
this.durationAsString = durationAsString;
|
||||
this.starred = starred;
|
||||
this.present = present;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public String getDurationAsString() {
|
||||
return durationAsString;
|
||||
}
|
||||
|
||||
public boolean isStarred() {
|
||||
return starred;
|
||||
}
|
||||
|
||||
public boolean isPresent() {
|
||||
return present;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import net.sourceforge.subsonic.dao.MediaFileDao;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.Playlist;
|
||||
import net.sourceforge.subsonic.i18n.SubsonicLocaleResolver;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import org.directwebremoting.WebContextFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for manipulating playlists.
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlaylistService {
|
||||
|
||||
private MediaFileService mediaFileService;
|
||||
private SecurityService securityService;
|
||||
private net.sourceforge.subsonic.service.PlaylistService playlistService;
|
||||
private MediaFileDao mediaFileDao;
|
||||
private SettingsService settingsService;
|
||||
private PlayerService playerService;
|
||||
private SubsonicLocaleResolver localeResolver;
|
||||
|
||||
public List<Playlist> getReadablePlaylists() {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
return playlistService.getReadablePlaylistsForUser(username);
|
||||
}
|
||||
|
||||
public List<Playlist> getWritablePlaylists() {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
return playlistService.getWritablePlaylistsForUser(username);
|
||||
}
|
||||
|
||||
public PlaylistInfo getPlaylist(int id) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
|
||||
Playlist playlist = playlistService.getPlaylist(id);
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
mediaFileService.populateStarredDate(files, username);
|
||||
populateAccess(files, username);
|
||||
return new PlaylistInfo(playlist, createEntries(files));
|
||||
}
|
||||
|
||||
private void populateAccess(List<MediaFile> files, String username) {
|
||||
for (MediaFile file : files) {
|
||||
if (!securityService.isFolderAccessAllowed(file, username)) {
|
||||
file.setPresent(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Playlist> createEmptyPlaylist() {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
Locale locale = localeResolver.resolveLocale(request);
|
||||
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.setShared(false);
|
||||
playlist.setName(dateFormat.format(now));
|
||||
|
||||
playlistService.createPlaylist(playlist);
|
||||
return getReadablePlaylists();
|
||||
}
|
||||
|
||||
public int createPlaylistForPlayQueue() {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
Locale locale = localeResolver.resolveLocale(request);
|
||||
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.setShared(false);
|
||||
playlist.setName(dateFormat.format(now));
|
||||
|
||||
playlistService.createPlaylist(playlist);
|
||||
playlistService.setFilesInPlaylist(playlist.getId(), player.getPlayQueue().getFiles());
|
||||
|
||||
return playlist.getId();
|
||||
}
|
||||
|
||||
public int createPlaylistForStarredSongs() {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
Locale locale = localeResolver.resolveLocale(request);
|
||||
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, locale);
|
||||
|
||||
Date now = new Date();
|
||||
Playlist playlist = new Playlist();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
playlist.setUsername(username);
|
||||
playlist.setCreated(now);
|
||||
playlist.setChanged(now);
|
||||
playlist.setShared(false);
|
||||
|
||||
ResourceBundle bundle = ResourceBundle.getBundle("net.sourceforge.subsonic.i18n.ResourceBundle", locale);
|
||||
playlist.setName(bundle.getString("top.starred") + " " + dateFormat.format(now));
|
||||
|
||||
playlistService.createPlaylist(playlist);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
List<MediaFile> songs = mediaFileDao.getStarredFiles(0, Integer.MAX_VALUE, username, musicFolders);
|
||||
playlistService.setFilesInPlaylist(playlist.getId(), songs);
|
||||
|
||||
return playlist.getId();
|
||||
}
|
||||
|
||||
public void appendToPlaylist(int playlistId, List<Integer> mediaFileIds) {
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(playlistId, true);
|
||||
for (Integer mediaFileId : mediaFileIds) {
|
||||
MediaFile file = mediaFileService.getMediaFile(mediaFileId);
|
||||
if (file != null) {
|
||||
files.add(file);
|
||||
}
|
||||
}
|
||||
playlistService.setFilesInPlaylist(playlistId, files);
|
||||
}
|
||||
|
||||
private List<PlaylistInfo.Entry> createEntries(List<MediaFile> files) {
|
||||
List<PlaylistInfo.Entry> result = new ArrayList<PlaylistInfo.Entry>();
|
||||
for (MediaFile file : files) {
|
||||
result.add(new PlaylistInfo.Entry(file.getId(), file.getTitle(), file.getArtist(), file.getAlbumName(),
|
||||
file.getDurationString(), file.getStarredDate() != null, file.isPresent()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public PlaylistInfo toggleStar(int id, int index) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
MediaFile file = files.get(index);
|
||||
|
||||
boolean starred = mediaFileDao.getMediaFileStarredDate(file.getId(), username) != null;
|
||||
if (starred) {
|
||||
mediaFileDao.unstarMediaFile(file.getId(), username);
|
||||
} else {
|
||||
mediaFileDao.starMediaFile(file.getId(), username);
|
||||
}
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public PlaylistInfo remove(int id, int index) {
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
files.remove(index);
|
||||
playlistService.setFilesInPlaylist(id, files);
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public PlaylistInfo up(int id, int index) {
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
if (index > 0) {
|
||||
MediaFile file = files.remove(index);
|
||||
files.add(index - 1, file);
|
||||
playlistService.setFilesInPlaylist(id, files);
|
||||
}
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public PlaylistInfo rearrange(int id, int[] indexes) {
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
MediaFile[] newFiles = new MediaFile[files.size()];
|
||||
for (int i = 0; i < indexes.length; i++) {
|
||||
newFiles[i] = files.get(indexes[i]);
|
||||
}
|
||||
playlistService.setFilesInPlaylist(id, Arrays.asList(newFiles));
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public PlaylistInfo down(int id, int index) {
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
if (index < files.size() - 1) {
|
||||
MediaFile file = files.remove(index);
|
||||
files.add(index + 1, file);
|
||||
playlistService.setFilesInPlaylist(id, files);
|
||||
}
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public void deletePlaylist(int id) {
|
||||
playlistService.deletePlaylist(id);
|
||||
}
|
||||
|
||||
public PlaylistInfo updatePlaylist(int id, String name, String comment, boolean shared) {
|
||||
Playlist playlist = playlistService.getPlaylist(id);
|
||||
playlist.setName(name);
|
||||
playlist.setComment(comment);
|
||||
playlist.setShared(shared);
|
||||
playlistService.updatePlaylist(playlist);
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public void setPlaylistService(net.sourceforge.subsonic.service.PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setMediaFileDao(MediaFileDao mediaFileDao) {
|
||||
this.mediaFileDao = mediaFileDao;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setLocaleResolver(SubsonicLocaleResolver localeResolver) {
|
||||
this.localeResolver = localeResolver;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
/**
|
||||
* Media folder scanning status.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ScanInfo {
|
||||
|
||||
private final boolean scanning;
|
||||
private final int count;
|
||||
|
||||
public ScanInfo(boolean scanning, int count) {
|
||||
this.scanning = scanning;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public boolean isScanning() {
|
||||
return scanning;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 2014 (C) Sindre Mehus
|
||||
*/
|
||||
package net.sourceforge.subsonic.ajax;
|
||||
|
||||
/**
|
||||
* Contains info about a similar artist.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SimilarArtist {
|
||||
|
||||
private final int mediaFileId;
|
||||
private final String artistName;
|
||||
|
||||
public SimilarArtist(int mediaFileId, String artistName) {
|
||||
this.mediaFileId = mediaFileId;
|
||||
this.artistName = artistName;
|
||||
}
|
||||
|
||||
public int getMediaFileId() {
|
||||
return mediaFileId;
|
||||
}
|
||||
|
||||
public String getArtistName() {
|
||||
return artistName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.dao.MediaFileDao;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import org.directwebremoting.WebContext;
|
||||
import org.directwebremoting.WebContextFactory;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for starring.
|
||||
* <p/>
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class StarService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(StarService.class);
|
||||
|
||||
private SecurityService securityService;
|
||||
private MediaFileDao mediaFileDao;
|
||||
|
||||
public void star(int id) {
|
||||
mediaFileDao.starMediaFile(id, getUser());
|
||||
}
|
||||
|
||||
public void unstar(int id) {
|
||||
mediaFileDao.unstarMediaFile(id, getUser());
|
||||
}
|
||||
|
||||
private String getUser() {
|
||||
WebContext webContext = WebContextFactory.get();
|
||||
User user = securityService.getCurrentUser(webContext.getHttpServletRequest());
|
||||
return user.getUsername();
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMediaFileDao(MediaFileDao mediaFileDao) {
|
||||
this.mediaFileDao = mediaFileDao;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.metadata.MetaData;
|
||||
import net.sourceforge.subsonic.service.metadata.MetaDataParser;
|
||||
import net.sourceforge.subsonic.service.metadata.MetaDataParserFactory;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for editing tags in music files.
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class TagService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(TagService.class);
|
||||
|
||||
private MetaDataParserFactory metaDataParserFactory;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
/**
|
||||
* Updated tags for a given music file.
|
||||
*
|
||||
* @param id The ID of the music file.
|
||||
* @param track The track number.
|
||||
* @param artist The artist name.
|
||||
* @param album The album name.
|
||||
* @param title The song title.
|
||||
* @param year The release year.
|
||||
* @param genre The musical genre.
|
||||
* @return "UPDATED" if the new tags were updated, "SKIPPED" if no update was necessary.
|
||||
* Otherwise the error message is returned.
|
||||
*/
|
||||
public String setTags(int id, String track, String artist, String album, String title, String year, String genre) {
|
||||
|
||||
track = StringUtils.trimToNull(track);
|
||||
artist = StringUtils.trimToNull(artist);
|
||||
album = StringUtils.trimToNull(album);
|
||||
title = StringUtils.trimToNull(title);
|
||||
year = StringUtils.trimToNull(year);
|
||||
genre = StringUtils.trimToNull(genre);
|
||||
|
||||
Integer trackNumber = null;
|
||||
if (track != null) {
|
||||
try {
|
||||
trackNumber = new Integer(track);
|
||||
} catch (NumberFormatException x) {
|
||||
LOG.warn("Illegal track number: " + track, x);
|
||||
}
|
||||
}
|
||||
|
||||
Integer yearNumber = null;
|
||||
if (year != null) {
|
||||
try {
|
||||
yearNumber = new Integer(year);
|
||||
} catch (NumberFormatException x) {
|
||||
LOG.warn("Illegal year: " + year, x);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
MediaFile file = mediaFileService.getMediaFile(id);
|
||||
MetaDataParser parser = metaDataParserFactory.getParser(file.getFile());
|
||||
|
||||
if (!parser.isEditingSupported()) {
|
||||
return "Tag editing of " + FilenameUtils.getExtension(file.getPath()) + " files is not supported.";
|
||||
}
|
||||
|
||||
if (StringUtils.equals(artist, file.getArtist()) &&
|
||||
StringUtils.equals(album, file.getAlbumName()) &&
|
||||
StringUtils.equals(title, file.getTitle()) &&
|
||||
ObjectUtils.equals(yearNumber, file.getYear()) &&
|
||||
StringUtils.equals(genre, file.getGenre()) &&
|
||||
ObjectUtils.equals(trackNumber, file.getTrackNumber())) {
|
||||
return "SKIPPED";
|
||||
}
|
||||
|
||||
MetaData newMetaData = parser.getMetaData(file.getFile());
|
||||
|
||||
// Note: album artist is intentionally set, as it is not user-changeable.
|
||||
newMetaData.setArtist(artist);
|
||||
newMetaData.setAlbumName(album);
|
||||
newMetaData.setTitle(title);
|
||||
newMetaData.setYear(yearNumber);
|
||||
newMetaData.setGenre(genre);
|
||||
newMetaData.setTrackNumber(trackNumber);
|
||||
parser.setMetaData(file, newMetaData);
|
||||
mediaFileService.refreshMediaFile(file);
|
||||
mediaFileService.refreshMediaFile(mediaFileService.getParentOf(file));
|
||||
return "UPDATED";
|
||||
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to update tags for " + id, x);
|
||||
return x.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setMetaDataParserFactory(MetaDataParserFactory metaDataParserFactory) {
|
||||
this.metaDataParserFactory = metaDataParserFactory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 2015 (C) Sindre Mehus
|
||||
*/
|
||||
package net.sourceforge.subsonic.ajax;
|
||||
|
||||
/**
|
||||
* See {@link ArtistInfo}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class TopSong {
|
||||
|
||||
private final int id;
|
||||
private final String title;
|
||||
private final String artist;
|
||||
private final String album;
|
||||
private final String durationAsString;
|
||||
private final boolean starred;
|
||||
|
||||
public TopSong(int id, String title, String artist, String album, String durationAsString, boolean starred) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.artist = artist;
|
||||
this.album = album;
|
||||
this.durationAsString = durationAsString;
|
||||
this.starred = starred;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public String getDurationAsString() {
|
||||
return durationAsString;
|
||||
}
|
||||
|
||||
public boolean isStarred() {
|
||||
return starred;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
import net.sourceforge.subsonic.domain.*;
|
||||
import net.sourceforge.subsonic.controller.*;
|
||||
import org.directwebremoting.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for retrieving the status of ongoing transfers.
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class TransferService {
|
||||
|
||||
/**
|
||||
* Returns info about any ongoing upload within the current session.
|
||||
* @return Info about ongoing upload.
|
||||
*/
|
||||
public UploadInfo getUploadInfo() {
|
||||
|
||||
HttpSession session = WebContextFactory.get().getSession();
|
||||
TransferStatus status = (TransferStatus) session.getAttribute(UploadController.UPLOAD_STATUS);
|
||||
|
||||
if (status != null) {
|
||||
return new UploadInfo(status.getBytesTransfered(), status.getBytesTotal());
|
||||
}
|
||||
return new UploadInfo(0L, 0L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
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.ajax;
|
||||
|
||||
/**
|
||||
* Contains status for a file upload.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class UploadInfo {
|
||||
|
||||
private long bytesUploaded;
|
||||
private long bytesTotal;
|
||||
|
||||
public UploadInfo(long bytesUploaded, long bytesTotal) {
|
||||
this.bytesUploaded = bytesUploaded;
|
||||
this.bytesTotal = bytesTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes uploaded.
|
||||
* @return The number of bytes uploaded.
|
||||
*/
|
||||
public long getBytesUploaded() {
|
||||
return bytesUploaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of bytes.
|
||||
* @return The total number of bytes.
|
||||
*/
|
||||
public long getBytesTotal() {
|
||||
return bytesTotal;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
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.cache;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import net.sf.ehcache.config.Configuration;
|
||||
import net.sf.ehcache.config.ConfigurationFactory;
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Initializes Ehcache and creates caches.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
* @version $Id$
|
||||
*/
|
||||
public class CacheFactory implements InitializingBean {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(CacheFactory.class);
|
||||
private CacheManager cacheManager;
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Configuration configuration = ConfigurationFactory.parseConfiguration();
|
||||
|
||||
// Override configuration to make sure cache is stored in Subsonic home dir.
|
||||
File cacheDir = new File(SettingsService.getSubsonicHome(), "cache");
|
||||
configuration.getDiskStoreConfiguration().setPath(cacheDir.getPath());
|
||||
|
||||
cacheManager = CacheManager.create(configuration);
|
||||
}
|
||||
|
||||
public Ehcache getCache(String name) {
|
||||
return cacheManager.getCache(name);
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
import net.sourceforge.subsonic.controller.AdvancedSettingsController;
|
||||
|
||||
/**
|
||||
* Command used in {@link AdvancedSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class AdvancedSettingsCommand {
|
||||
|
||||
private String downloadLimit;
|
||||
private String uploadLimit;
|
||||
private boolean ldapEnabled;
|
||||
private String ldapUrl;
|
||||
private String ldapSearchFilter;
|
||||
private String ldapManagerDn;
|
||||
private String ldapManagerPassword;
|
||||
private boolean ldapAutoShadowing;
|
||||
private String brand;
|
||||
private boolean isReloadNeeded;
|
||||
private boolean toast;
|
||||
|
||||
public String getDownloadLimit() {
|
||||
return downloadLimit;
|
||||
}
|
||||
|
||||
public void setDownloadLimit(String downloadLimit) {
|
||||
this.downloadLimit = downloadLimit;
|
||||
}
|
||||
|
||||
public String getUploadLimit() {
|
||||
return uploadLimit;
|
||||
}
|
||||
|
||||
public void setUploadLimit(String uploadLimit) {
|
||||
this.uploadLimit = uploadLimit;
|
||||
}
|
||||
|
||||
public boolean isLdapEnabled() {
|
||||
return ldapEnabled;
|
||||
}
|
||||
|
||||
public void setLdapEnabled(boolean ldapEnabled) {
|
||||
this.ldapEnabled = ldapEnabled;
|
||||
}
|
||||
|
||||
public String getLdapUrl() {
|
||||
return ldapUrl;
|
||||
}
|
||||
|
||||
public void setLdapUrl(String ldapUrl) {
|
||||
this.ldapUrl = ldapUrl;
|
||||
}
|
||||
|
||||
public String getLdapSearchFilter() {
|
||||
return ldapSearchFilter;
|
||||
}
|
||||
|
||||
public void setLdapSearchFilter(String ldapSearchFilter) {
|
||||
this.ldapSearchFilter = ldapSearchFilter;
|
||||
}
|
||||
|
||||
public String getLdapManagerDn() {
|
||||
return ldapManagerDn;
|
||||
}
|
||||
|
||||
public void setLdapManagerDn(String ldapManagerDn) {
|
||||
this.ldapManagerDn = ldapManagerDn;
|
||||
}
|
||||
|
||||
public String getLdapManagerPassword() {
|
||||
return ldapManagerPassword;
|
||||
}
|
||||
|
||||
public void setLdapManagerPassword(String ldapManagerPassword) {
|
||||
this.ldapManagerPassword = ldapManagerPassword;
|
||||
}
|
||||
|
||||
public boolean isLdapAutoShadowing() {
|
||||
return ldapAutoShadowing;
|
||||
}
|
||||
|
||||
public void setLdapAutoShadowing(boolean ldapAutoShadowing) {
|
||||
this.ldapAutoShadowing = ldapAutoShadowing;
|
||||
}
|
||||
|
||||
public void setBrand(String brand) {
|
||||
this.brand = brand;
|
||||
}
|
||||
|
||||
public String getBrand() {
|
||||
return brand;
|
||||
}
|
||||
|
||||
public void setReloadNeeded(boolean reloadNeeded) {
|
||||
isReloadNeeded = reloadNeeded;
|
||||
}
|
||||
|
||||
public boolean isReloadNeeded() {
|
||||
return isReloadNeeded;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
/**
|
||||
* Holds the name and description of an enum value.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class EnumHolder {
|
||||
private String name;
|
||||
private String description;
|
||||
|
||||
public EnumHolder(String name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
import net.sourceforge.subsonic.controller.GeneralSettingsController;
|
||||
import net.sourceforge.subsonic.domain.Theme;
|
||||
|
||||
/**
|
||||
* Command used in {@link GeneralSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class GeneralSettingsCommand {
|
||||
|
||||
private String playlistFolder;
|
||||
private String musicFileTypes;
|
||||
private String videoFileTypes;
|
||||
private String coverArtFileTypes;
|
||||
private String index;
|
||||
private String ignoredArticles;
|
||||
private String shortcuts;
|
||||
private boolean sortAlbumsByYear;
|
||||
private boolean gettingStartedEnabled;
|
||||
private String welcomeTitle;
|
||||
private String welcomeSubtitle;
|
||||
private String welcomeMessage;
|
||||
private String loginMessage;
|
||||
private String localeIndex;
|
||||
private String[] locales;
|
||||
private String themeIndex;
|
||||
private Theme[] themes;
|
||||
private boolean isReloadNeeded;
|
||||
private boolean toast;
|
||||
|
||||
public String getPlaylistFolder() {
|
||||
return playlistFolder;
|
||||
}
|
||||
|
||||
public void setPlaylistFolder(String playlistFolder) {
|
||||
this.playlistFolder = playlistFolder;
|
||||
}
|
||||
|
||||
public String getMusicFileTypes() {
|
||||
return musicFileTypes;
|
||||
}
|
||||
|
||||
public void setMusicFileTypes(String musicFileTypes) {
|
||||
this.musicFileTypes = musicFileTypes;
|
||||
}
|
||||
|
||||
public String getVideoFileTypes() {
|
||||
return videoFileTypes;
|
||||
}
|
||||
|
||||
public void setVideoFileTypes(String videoFileTypes) {
|
||||
this.videoFileTypes = videoFileTypes;
|
||||
}
|
||||
|
||||
public String getCoverArtFileTypes() {
|
||||
return coverArtFileTypes;
|
||||
}
|
||||
|
||||
public void setCoverArtFileTypes(String coverArtFileTypes) {
|
||||
this.coverArtFileTypes = coverArtFileTypes;
|
||||
}
|
||||
|
||||
public String getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public void setIndex(String index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String getIgnoredArticles() {
|
||||
return ignoredArticles;
|
||||
}
|
||||
|
||||
public void setIgnoredArticles(String ignoredArticles) {
|
||||
this.ignoredArticles = ignoredArticles;
|
||||
}
|
||||
|
||||
public String getShortcuts() {
|
||||
return shortcuts;
|
||||
}
|
||||
|
||||
public void setShortcuts(String shortcuts) {
|
||||
this.shortcuts = shortcuts;
|
||||
}
|
||||
|
||||
public String getWelcomeTitle() {
|
||||
return welcomeTitle;
|
||||
}
|
||||
|
||||
public void setWelcomeTitle(String welcomeTitle) {
|
||||
this.welcomeTitle = welcomeTitle;
|
||||
}
|
||||
|
||||
public String getWelcomeSubtitle() {
|
||||
return welcomeSubtitle;
|
||||
}
|
||||
|
||||
public void setWelcomeSubtitle(String welcomeSubtitle) {
|
||||
this.welcomeSubtitle = welcomeSubtitle;
|
||||
}
|
||||
|
||||
public String getWelcomeMessage() {
|
||||
return welcomeMessage;
|
||||
}
|
||||
|
||||
public void setWelcomeMessage(String welcomeMessage) {
|
||||
this.welcomeMessage = welcomeMessage;
|
||||
}
|
||||
|
||||
public String getLoginMessage() {
|
||||
return loginMessage;
|
||||
}
|
||||
|
||||
public void setLoginMessage(String loginMessage) {
|
||||
this.loginMessage = loginMessage;
|
||||
}
|
||||
|
||||
public String getLocaleIndex() {
|
||||
return localeIndex;
|
||||
}
|
||||
|
||||
public void setLocaleIndex(String localeIndex) {
|
||||
this.localeIndex = localeIndex;
|
||||
}
|
||||
|
||||
public String[] getLocales() {
|
||||
return locales;
|
||||
}
|
||||
|
||||
public void setLocales(String[] locales) {
|
||||
this.locales = locales;
|
||||
}
|
||||
|
||||
public String getThemeIndex() {
|
||||
return themeIndex;
|
||||
}
|
||||
|
||||
public void setThemeIndex(String themeIndex) {
|
||||
this.themeIndex = themeIndex;
|
||||
}
|
||||
|
||||
public Theme[] getThemes() {
|
||||
return themes;
|
||||
}
|
||||
|
||||
public void setThemes(Theme[] themes) {
|
||||
this.themes = themes;
|
||||
}
|
||||
|
||||
public boolean isReloadNeeded() {
|
||||
return isReloadNeeded;
|
||||
}
|
||||
|
||||
public void setReloadNeeded(boolean reloadNeeded) {
|
||||
isReloadNeeded = reloadNeeded;
|
||||
}
|
||||
|
||||
public boolean isSortAlbumsByYear() {
|
||||
return sortAlbumsByYear;
|
||||
}
|
||||
|
||||
public void setSortAlbumsByYear(boolean sortAlbumsByYear) {
|
||||
this.sortAlbumsByYear = sortAlbumsByYear;
|
||||
}
|
||||
|
||||
public boolean isGettingStartedEnabled() {
|
||||
return gettingStartedEnabled;
|
||||
}
|
||||
|
||||
public void setGettingStartedEnabled(boolean gettingStartedEnabled) {
|
||||
this.gettingStartedEnabled = gettingStartedEnabled;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.subsonic.controller.MusicFolderSettingsController;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* Command used in {@link MusicFolderSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MusicFolderSettingsCommand {
|
||||
|
||||
private String interval;
|
||||
private String hour;
|
||||
private boolean scanning;
|
||||
private boolean fastCache;
|
||||
private boolean organizeByFolderStructure;
|
||||
private List<MusicFolderInfo> musicFolders;
|
||||
private MusicFolderInfo newMusicFolder;
|
||||
private boolean reload;
|
||||
|
||||
public String getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
public void setInterval(String interval) {
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
public String getHour() {
|
||||
return hour;
|
||||
}
|
||||
|
||||
public void setHour(String hour) {
|
||||
this.hour = hour;
|
||||
}
|
||||
|
||||
public boolean isScanning() {
|
||||
return scanning;
|
||||
}
|
||||
|
||||
public void setScanning(boolean scanning) {
|
||||
this.scanning = scanning;
|
||||
}
|
||||
|
||||
public boolean isFastCache() {
|
||||
return fastCache;
|
||||
}
|
||||
|
||||
public List<MusicFolderInfo> getMusicFolders() {
|
||||
return musicFolders;
|
||||
}
|
||||
|
||||
public void setMusicFolders(List<MusicFolderInfo> musicFolders) {
|
||||
this.musicFolders = musicFolders;
|
||||
}
|
||||
|
||||
public void setFastCache(boolean fastCache) {
|
||||
this.fastCache = fastCache;
|
||||
}
|
||||
|
||||
public MusicFolderInfo getNewMusicFolder() {
|
||||
return newMusicFolder;
|
||||
}
|
||||
|
||||
public void setNewMusicFolder(MusicFolderInfo newMusicFolder) {
|
||||
this.newMusicFolder = newMusicFolder;
|
||||
}
|
||||
|
||||
public void setReload(boolean reload) {
|
||||
this.reload = reload;
|
||||
}
|
||||
|
||||
public boolean isReload() {
|
||||
return reload;
|
||||
}
|
||||
|
||||
public boolean isOrganizeByFolderStructure() {
|
||||
return organizeByFolderStructure;
|
||||
}
|
||||
|
||||
public void setOrganizeByFolderStructure(boolean organizeByFolderStructure) {
|
||||
this.organizeByFolderStructure = organizeByFolderStructure;
|
||||
}
|
||||
|
||||
public static class MusicFolderInfo {
|
||||
|
||||
private Integer id;
|
||||
private String path;
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
private boolean delete;
|
||||
private boolean existing;
|
||||
|
||||
public MusicFolderInfo(MusicFolder musicFolder) {
|
||||
id = musicFolder.getId();
|
||||
path = musicFolder.getPath().getPath();
|
||||
name = musicFolder.getName();
|
||||
enabled = musicFolder.isEnabled();
|
||||
existing = musicFolder.getPath().exists() && musicFolder.getPath().isDirectory();
|
||||
}
|
||||
|
||||
public MusicFolderInfo() {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isDelete() {
|
||||
return delete;
|
||||
}
|
||||
|
||||
public void setDelete(boolean delete) {
|
||||
this.delete = delete;
|
||||
}
|
||||
|
||||
public MusicFolder toMusicFolder() {
|
||||
String path = StringUtils.trimToNull(this.path);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
File file = new File(path);
|
||||
String name = StringUtils.trimToNull(this.name);
|
||||
if (name == null) {
|
||||
name = file.getName();
|
||||
}
|
||||
return new MusicFolder(id, new File(path), name, enabled, new Date());
|
||||
}
|
||||
|
||||
public boolean isExisting() {
|
||||
return existing;
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
import net.sourceforge.subsonic.domain.LicenseInfo;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
* @version $Id$
|
||||
*/
|
||||
public class NetworkSettingsCommand {
|
||||
|
||||
private boolean portForwardingEnabled;
|
||||
private boolean urlRedirectionEnabled;
|
||||
private String urlRedirectFrom;
|
||||
private String urlRedirectCustomUrl;
|
||||
private String urlRedirectType;
|
||||
private int port;
|
||||
private boolean toast;
|
||||
private LicenseInfo licenseInfo;
|
||||
|
||||
public void setPortForwardingEnabled(boolean portForwardingEnabled) {
|
||||
this.portForwardingEnabled = portForwardingEnabled;
|
||||
}
|
||||
|
||||
public boolean isPortForwardingEnabled() {
|
||||
return portForwardingEnabled;
|
||||
}
|
||||
|
||||
public boolean isUrlRedirectionEnabled() {
|
||||
return urlRedirectionEnabled;
|
||||
}
|
||||
|
||||
public void setUrlRedirectionEnabled(boolean urlRedirectionEnabled) {
|
||||
this.urlRedirectionEnabled = urlRedirectionEnabled;
|
||||
}
|
||||
|
||||
public String getUrlRedirectFrom() {
|
||||
return urlRedirectFrom;
|
||||
}
|
||||
|
||||
public void setUrlRedirectFrom(String urlRedirectFrom) {
|
||||
this.urlRedirectFrom = urlRedirectFrom;
|
||||
}
|
||||
|
||||
public String getUrlRedirectCustomUrl() {
|
||||
return urlRedirectCustomUrl;
|
||||
}
|
||||
|
||||
public void setUrlRedirectCustomUrl(String urlRedirectCustomUrl) {
|
||||
this.urlRedirectCustomUrl = urlRedirectCustomUrl;
|
||||
}
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
|
||||
public void setLicenseInfo(LicenseInfo licenseInfo) {
|
||||
this.licenseInfo = licenseInfo;
|
||||
}
|
||||
|
||||
public LicenseInfo getLicenseInfo() {
|
||||
return licenseInfo;
|
||||
}
|
||||
|
||||
public String getUrlRedirectType() {
|
||||
return urlRedirectType;
|
||||
}
|
||||
|
||||
public void setUrlRedirectType(String urlRedirectType) {
|
||||
this.urlRedirectType = urlRedirectType;
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
import net.sourceforge.subsonic.controller.*;
|
||||
|
||||
/**
|
||||
* Command used in {@link PasswordSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PasswordSettingsCommand {
|
||||
private String username;
|
||||
private String password;
|
||||
private String confirmPassword;
|
||||
private boolean ldapAuthenticated;
|
||||
private boolean toast;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getConfirmPassword() {
|
||||
return confirmPassword;
|
||||
}
|
||||
|
||||
public void setConfirmPassword(String confirmPassword) {
|
||||
this.confirmPassword = confirmPassword;
|
||||
}
|
||||
|
||||
public boolean isLdapAuthenticated() {
|
||||
return ldapAuthenticated;
|
||||
}
|
||||
|
||||
public void setLdapAuthenticated(boolean ldapAuthenticated) {
|
||||
this.ldapAuthenticated = ldapAuthenticated;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
}
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.subsonic.controller.PersonalSettingsController;
|
||||
import net.sourceforge.subsonic.domain.AlbumListType;
|
||||
import net.sourceforge.subsonic.domain.Avatar;
|
||||
import net.sourceforge.subsonic.domain.Theme;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
|
||||
/**
|
||||
* Command used in {@link PersonalSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PersonalSettingsCommand {
|
||||
private User user;
|
||||
private String localeIndex;
|
||||
private String[] locales;
|
||||
private String themeIndex;
|
||||
private Theme[] themes;
|
||||
private String albumListId;
|
||||
private AlbumListType[] albumLists;
|
||||
private int avatarId;
|
||||
private List<Avatar> avatars;
|
||||
private Avatar customAvatar;
|
||||
private UserSettings.Visibility mainVisibility;
|
||||
private UserSettings.Visibility playlistVisibility;
|
||||
private boolean partyModeEnabled;
|
||||
private boolean showNowPlayingEnabled;
|
||||
private boolean showChatEnabled;
|
||||
private boolean showArtistInfoEnabled;
|
||||
private boolean nowPlayingAllowed;
|
||||
private boolean autoHidePlayQueue;
|
||||
private boolean finalVersionNotificationEnabled;
|
||||
private boolean betaVersionNotificationEnabled;
|
||||
private boolean songNotificationEnabled;
|
||||
private boolean queueFollowingSongs;
|
||||
private boolean lastFmEnabled;
|
||||
private String lastFmUsername;
|
||||
private String lastFmPassword;
|
||||
private boolean isReloadNeeded;
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getLocaleIndex() {
|
||||
return localeIndex;
|
||||
}
|
||||
|
||||
public void setLocaleIndex(String localeIndex) {
|
||||
this.localeIndex = localeIndex;
|
||||
}
|
||||
|
||||
public String[] getLocales() {
|
||||
return locales;
|
||||
}
|
||||
|
||||
public void setLocales(String[] locales) {
|
||||
this.locales = locales;
|
||||
}
|
||||
|
||||
public String getThemeIndex() {
|
||||
return themeIndex;
|
||||
}
|
||||
|
||||
public void setThemeIndex(String themeIndex) {
|
||||
this.themeIndex = themeIndex;
|
||||
}
|
||||
|
||||
public Theme[] getThemes() {
|
||||
return themes;
|
||||
}
|
||||
|
||||
public void setThemes(Theme[] themes) {
|
||||
this.themes = themes;
|
||||
}
|
||||
|
||||
public String getAlbumListId() {
|
||||
return albumListId;
|
||||
}
|
||||
|
||||
public void setAlbumListId(String albumListId) {
|
||||
this.albumListId = albumListId;
|
||||
}
|
||||
|
||||
public AlbumListType[] getAlbumLists() {
|
||||
return albumLists;
|
||||
}
|
||||
|
||||
public void setAlbumLists(AlbumListType[] albumLists) {
|
||||
this.albumLists = albumLists;
|
||||
}
|
||||
|
||||
public int getAvatarId() {
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
public void setAvatarId(int avatarId) {
|
||||
this.avatarId = avatarId;
|
||||
}
|
||||
|
||||
public List<Avatar> getAvatars() {
|
||||
return avatars;
|
||||
}
|
||||
|
||||
public void setAvatars(List<Avatar> avatars) {
|
||||
this.avatars = avatars;
|
||||
}
|
||||
|
||||
public Avatar getCustomAvatar() {
|
||||
return customAvatar;
|
||||
}
|
||||
|
||||
public void setCustomAvatar(Avatar customAvatar) {
|
||||
this.customAvatar = customAvatar;
|
||||
}
|
||||
|
||||
public UserSettings.Visibility getMainVisibility() {
|
||||
return mainVisibility;
|
||||
}
|
||||
|
||||
public void setMainVisibility(UserSettings.Visibility mainVisibility) {
|
||||
this.mainVisibility = mainVisibility;
|
||||
}
|
||||
|
||||
public UserSettings.Visibility getPlaylistVisibility() {
|
||||
return playlistVisibility;
|
||||
}
|
||||
|
||||
public void setPlaylistVisibility(UserSettings.Visibility playlistVisibility) {
|
||||
this.playlistVisibility = playlistVisibility;
|
||||
}
|
||||
|
||||
public boolean isPartyModeEnabled() {
|
||||
return partyModeEnabled;
|
||||
}
|
||||
|
||||
public void setPartyModeEnabled(boolean partyModeEnabled) {
|
||||
this.partyModeEnabled = partyModeEnabled;
|
||||
}
|
||||
|
||||
public boolean isShowNowPlayingEnabled() {
|
||||
return showNowPlayingEnabled;
|
||||
}
|
||||
|
||||
public void setShowNowPlayingEnabled(boolean showNowPlayingEnabled) {
|
||||
this.showNowPlayingEnabled = showNowPlayingEnabled;
|
||||
}
|
||||
|
||||
public boolean isShowChatEnabled() {
|
||||
return showChatEnabled;
|
||||
}
|
||||
|
||||
public void setShowChatEnabled(boolean showChatEnabled) {
|
||||
this.showChatEnabled = showChatEnabled;
|
||||
}
|
||||
|
||||
public boolean isShowArtistInfoEnabled() {
|
||||
return showArtistInfoEnabled;
|
||||
}
|
||||
|
||||
public void setShowArtistInfoEnabled(boolean showArtistInfoEnabled) {
|
||||
this.showArtistInfoEnabled = showArtistInfoEnabled;
|
||||
}
|
||||
|
||||
public boolean isNowPlayingAllowed() {
|
||||
return nowPlayingAllowed;
|
||||
}
|
||||
|
||||
public void setNowPlayingAllowed(boolean nowPlayingAllowed) {
|
||||
this.nowPlayingAllowed = nowPlayingAllowed;
|
||||
}
|
||||
|
||||
public boolean isFinalVersionNotificationEnabled() {
|
||||
return finalVersionNotificationEnabled;
|
||||
}
|
||||
|
||||
public void setFinalVersionNotificationEnabled(boolean finalVersionNotificationEnabled) {
|
||||
this.finalVersionNotificationEnabled = finalVersionNotificationEnabled;
|
||||
}
|
||||
|
||||
public boolean isBetaVersionNotificationEnabled() {
|
||||
return betaVersionNotificationEnabled;
|
||||
}
|
||||
|
||||
public void setBetaVersionNotificationEnabled(boolean betaVersionNotificationEnabled) {
|
||||
this.betaVersionNotificationEnabled = betaVersionNotificationEnabled;
|
||||
}
|
||||
|
||||
public void setSongNotificationEnabled(boolean songNotificationEnabled) {
|
||||
this.songNotificationEnabled = songNotificationEnabled;
|
||||
}
|
||||
|
||||
public boolean isSongNotificationEnabled() {
|
||||
return songNotificationEnabled;
|
||||
}
|
||||
|
||||
public boolean isAutoHidePlayQueue() {
|
||||
return autoHidePlayQueue;
|
||||
}
|
||||
|
||||
public void setAutoHidePlayQueue(boolean autoHidePlayQueue) {
|
||||
this.autoHidePlayQueue = autoHidePlayQueue;
|
||||
}
|
||||
|
||||
public boolean isLastFmEnabled() {
|
||||
return lastFmEnabled;
|
||||
}
|
||||
|
||||
public void setLastFmEnabled(boolean lastFmEnabled) {
|
||||
this.lastFmEnabled = lastFmEnabled;
|
||||
}
|
||||
|
||||
public String getLastFmUsername() {
|
||||
return lastFmUsername;
|
||||
}
|
||||
|
||||
public void setLastFmUsername(String lastFmUsername) {
|
||||
this.lastFmUsername = lastFmUsername;
|
||||
}
|
||||
|
||||
public String getLastFmPassword() {
|
||||
return lastFmPassword;
|
||||
}
|
||||
|
||||
public void setLastFmPassword(String lastFmPassword) {
|
||||
this.lastFmPassword = lastFmPassword;
|
||||
}
|
||||
|
||||
public boolean isReloadNeeded() {
|
||||
return isReloadNeeded;
|
||||
}
|
||||
|
||||
public void setReloadNeeded(boolean reloadNeeded) {
|
||||
isReloadNeeded = reloadNeeded;
|
||||
}
|
||||
|
||||
public boolean isQueueFollowingSongs() {
|
||||
return queueFollowingSongs;
|
||||
}
|
||||
|
||||
public void setQueueFollowingSongs(boolean queueFollowingSongs) {
|
||||
this.queueFollowingSongs = queueFollowingSongs;
|
||||
}
|
||||
}
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.subsonic.controller.PlayerSettingsController;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.PlayerTechnology;
|
||||
import net.sourceforge.subsonic.domain.TranscodeScheme;
|
||||
import net.sourceforge.subsonic.domain.Transcoding;
|
||||
|
||||
/**
|
||||
* Command used in {@link PlayerSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlayerSettingsCommand {
|
||||
private String playerId;
|
||||
private String name;
|
||||
private String description;
|
||||
private String type;
|
||||
private Date lastSeen;
|
||||
private boolean isDynamicIp;
|
||||
private boolean isAutoControlEnabled;
|
||||
private String technologyName;
|
||||
private String transcodeSchemeName;
|
||||
private boolean transcodingSupported;
|
||||
private String transcodeDirectory;
|
||||
private List<Transcoding> allTranscodings;
|
||||
private int[] activeTranscodingIds;
|
||||
private EnumHolder[] technologyHolders;
|
||||
private EnumHolder[] transcodeSchemeHolders;
|
||||
private Player[] players;
|
||||
private boolean isAdmin;
|
||||
private boolean isReloadNeeded;
|
||||
|
||||
public String getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
|
||||
public void setPlayerId(String playerId) {
|
||||
this.playerId = playerId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Date getLastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
public void setLastSeen(Date lastSeen) {
|
||||
this.lastSeen = lastSeen;
|
||||
}
|
||||
|
||||
public boolean isDynamicIp() {
|
||||
return isDynamicIp;
|
||||
}
|
||||
|
||||
public void setDynamicIp(boolean dynamicIp) {
|
||||
isDynamicIp = dynamicIp;
|
||||
}
|
||||
|
||||
public boolean isAutoControlEnabled() {
|
||||
return isAutoControlEnabled;
|
||||
}
|
||||
|
||||
public void setAutoControlEnabled(boolean autoControlEnabled) {
|
||||
isAutoControlEnabled = autoControlEnabled;
|
||||
}
|
||||
|
||||
public String getTranscodeSchemeName() {
|
||||
return transcodeSchemeName;
|
||||
}
|
||||
|
||||
public void setTranscodeSchemeName(String transcodeSchemeName) {
|
||||
this.transcodeSchemeName = transcodeSchemeName;
|
||||
}
|
||||
|
||||
public boolean isTranscodingSupported() {
|
||||
return transcodingSupported;
|
||||
}
|
||||
|
||||
public void setTranscodingSupported(boolean transcodingSupported) {
|
||||
this.transcodingSupported = transcodingSupported;
|
||||
}
|
||||
|
||||
public String getTranscodeDirectory() {
|
||||
return transcodeDirectory;
|
||||
}
|
||||
|
||||
public void setTranscodeDirectory(String transcodeDirectory) {
|
||||
this.transcodeDirectory = transcodeDirectory;
|
||||
}
|
||||
|
||||
public List<Transcoding> getAllTranscodings() {
|
||||
return allTranscodings;
|
||||
}
|
||||
|
||||
public void setAllTranscodings(List<Transcoding> allTranscodings) {
|
||||
this.allTranscodings = allTranscodings;
|
||||
}
|
||||
|
||||
public int[] getActiveTranscodingIds() {
|
||||
return activeTranscodingIds;
|
||||
}
|
||||
|
||||
public void setActiveTranscodingIds(int[] activeTranscodingIds) {
|
||||
this.activeTranscodingIds = activeTranscodingIds;
|
||||
}
|
||||
|
||||
public EnumHolder[] getTechnologyHolders() {
|
||||
return technologyHolders;
|
||||
}
|
||||
|
||||
public void setTechnologies(PlayerTechnology[] technologies) {
|
||||
technologyHolders = new EnumHolder[technologies.length];
|
||||
for (int i = 0; i < technologies.length; i++) {
|
||||
PlayerTechnology technology = technologies[i];
|
||||
technologyHolders[i] = new EnumHolder(technology.name(), technology.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public EnumHolder[] getTranscodeSchemeHolders() {
|
||||
return transcodeSchemeHolders;
|
||||
}
|
||||
|
||||
public void setTranscodeSchemes(TranscodeScheme[] transcodeSchemes) {
|
||||
transcodeSchemeHolders = new EnumHolder[transcodeSchemes.length];
|
||||
for (int i = 0; i < transcodeSchemes.length; i++) {
|
||||
TranscodeScheme scheme = transcodeSchemes[i];
|
||||
transcodeSchemeHolders[i] = new EnumHolder(scheme.name(), scheme.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public String getTechnologyName() {
|
||||
return technologyName;
|
||||
}
|
||||
|
||||
public void setTechnologyName(String technologyName) {
|
||||
this.technologyName = technologyName;
|
||||
}
|
||||
|
||||
public Player[] getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
public void setPlayers(Player[] players) {
|
||||
this.players = players;
|
||||
}
|
||||
|
||||
public boolean isAdmin() {
|
||||
return isAdmin;
|
||||
}
|
||||
|
||||
public void setAdmin(boolean admin) {
|
||||
isAdmin = admin;
|
||||
}
|
||||
|
||||
public boolean isReloadNeeded() {
|
||||
return isReloadNeeded;
|
||||
}
|
||||
|
||||
public void setReloadNeeded(boolean reloadNeeded) {
|
||||
isReloadNeeded = reloadNeeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the transcoding and whether it is active for the given player.
|
||||
*/
|
||||
public static class TranscodingHolder {
|
||||
private Transcoding transcoding;
|
||||
private boolean isActive;
|
||||
|
||||
public TranscodingHolder(Transcoding transcoding, boolean isActive) {
|
||||
this.transcoding = transcoding;
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
public Transcoding getTranscoding() {
|
||||
return transcoding;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return isActive;
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
import net.sourceforge.subsonic.controller.PodcastSettingsController;
|
||||
|
||||
/**
|
||||
* Command used in {@link PodcastSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastSettingsCommand {
|
||||
|
||||
private String interval;
|
||||
private String folder;
|
||||
private String episodeRetentionCount;
|
||||
private String episodeDownloadCount;
|
||||
private boolean toast;
|
||||
|
||||
public String getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
public void setInterval(String interval) {
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
public String getFolder() {
|
||||
return folder;
|
||||
}
|
||||
|
||||
public void setFolder(String folder) {
|
||||
this.folder = folder;
|
||||
}
|
||||
|
||||
public String getEpisodeRetentionCount() {
|
||||
return episodeRetentionCount;
|
||||
}
|
||||
|
||||
public void setEpisodeRetentionCount(String episodeRetentionCount) {
|
||||
this.episodeRetentionCount = episodeRetentionCount;
|
||||
}
|
||||
|
||||
public String getEpisodeDownloadCount() {
|
||||
return episodeDownloadCount;
|
||||
}
|
||||
|
||||
public void setEpisodeDownloadCount(String episodeDownloadCount) {
|
||||
this.episodeDownloadCount = episodeDownloadCount;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import net.sourceforge.subsonic.controller.PremiumSettingsController;
|
||||
import net.sourceforge.subsonic.domain.LicenseInfo;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
|
||||
/**
|
||||
* Command used in {@link PremiumSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PremiumSettingsCommand {
|
||||
|
||||
private String path;
|
||||
private String brand;
|
||||
private LicenseInfo licenseInfo;
|
||||
private String licenseCode;
|
||||
private boolean forceChange;
|
||||
private boolean submissionError;
|
||||
private User user;
|
||||
private boolean toast;
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getBrand() {
|
||||
return brand;
|
||||
}
|
||||
|
||||
public void setBrand(String brand) {
|
||||
this.brand = brand;
|
||||
}
|
||||
|
||||
public LicenseInfo getLicenseInfo() {
|
||||
return licenseInfo;
|
||||
}
|
||||
|
||||
public String getLicenseCode() {
|
||||
return licenseCode;
|
||||
}
|
||||
|
||||
public void setLicenseCode(String licenseCode) {
|
||||
this.licenseCode = StringUtils.trimToNull(licenseCode);
|
||||
}
|
||||
|
||||
public void setLicenseInfo(LicenseInfo licenseInfo) {
|
||||
this.licenseInfo = licenseInfo;
|
||||
}
|
||||
|
||||
public boolean isForceChange() {
|
||||
return forceChange;
|
||||
}
|
||||
|
||||
public void setForceChange(boolean forceChange) {
|
||||
this.forceChange = forceChange;
|
||||
}
|
||||
|
||||
public boolean isSubmissionError() {
|
||||
return submissionError;
|
||||
}
|
||||
|
||||
public void setSubmissionError(boolean submissionError) {
|
||||
this.submissionError = submissionError;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
import net.sourceforge.subsonic.domain.*;
|
||||
import net.sourceforge.subsonic.controller.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Command used in {@link SearchController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SearchCommand {
|
||||
|
||||
private String query;
|
||||
private List<MediaFile> artists;
|
||||
private List<MediaFile> albums;
|
||||
private List<MediaFile> songs;
|
||||
private boolean isIndexBeingCreated;
|
||||
private User user;
|
||||
private boolean partyModeEnabled;
|
||||
private Player player;
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void setQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public boolean isIndexBeingCreated() {
|
||||
return isIndexBeingCreated;
|
||||
}
|
||||
|
||||
public void setIndexBeingCreated(boolean indexBeingCreated) {
|
||||
isIndexBeingCreated = indexBeingCreated;
|
||||
}
|
||||
|
||||
public List<MediaFile> getArtists() {
|
||||
return artists;
|
||||
}
|
||||
|
||||
public void setArtists(List<MediaFile> artists) {
|
||||
this.artists = artists;
|
||||
}
|
||||
|
||||
public List<MediaFile> getAlbums() {
|
||||
return albums;
|
||||
}
|
||||
|
||||
public void setAlbums(List<MediaFile> albums) {
|
||||
this.albums = albums;
|
||||
}
|
||||
|
||||
public List<MediaFile> getSongs() {
|
||||
return songs;
|
||||
}
|
||||
|
||||
public void setSongs(List<MediaFile> songs) {
|
||||
this.songs = songs;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public boolean isPartyModeEnabled() {
|
||||
return partyModeEnabled;
|
||||
}
|
||||
|
||||
public void setPartyModeEnabled(boolean partyModeEnabled) {
|
||||
this.partyModeEnabled = partyModeEnabled;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public static class Match {
|
||||
private MediaFile mediaFile;
|
||||
private String title;
|
||||
private String album;
|
||||
private String artist;
|
||||
|
||||
public Match(MediaFile mediaFile, String title, String album, String artist) {
|
||||
this.mediaFile = mediaFile;
|
||||
this.title = title;
|
||||
this.album = album;
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public MediaFile getMediaFile() {
|
||||
return mediaFile;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
}
|
||||
}
|
||||
+316
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
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.command;
|
||||
|
||||
import net.sourceforge.subsonic.controller.UserSettingsController;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.TranscodeScheme;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command used in {@link UserSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class UserSettingsCommand {
|
||||
private String username;
|
||||
private boolean isAdminRole;
|
||||
private boolean isDownloadRole;
|
||||
private boolean isUploadRole;
|
||||
private boolean isCoverArtRole;
|
||||
private boolean isCommentRole;
|
||||
private boolean isPodcastRole;
|
||||
private boolean isStreamRole;
|
||||
private boolean isJukeboxRole;
|
||||
private boolean isSettingsRole;
|
||||
private boolean isShareRole;
|
||||
|
||||
private List<User> users;
|
||||
private boolean isAdmin;
|
||||
private boolean isPasswordChange;
|
||||
private boolean isNewUser;
|
||||
private boolean isDeleteUser;
|
||||
private String password;
|
||||
private String confirmPassword;
|
||||
private String email;
|
||||
private boolean isLdapAuthenticated;
|
||||
private boolean isLdapEnabled;
|
||||
private List<MusicFolder> allMusicFolders;
|
||||
private int[] allowedMusicFolderIds;
|
||||
|
||||
private String transcodeSchemeName;
|
||||
private EnumHolder[] transcodeSchemeHolders;
|
||||
private boolean transcodingSupported;
|
||||
private String transcodeDirectory;
|
||||
private boolean toast;
|
||||
private boolean reload;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public boolean isAdminRole() {
|
||||
return isAdminRole;
|
||||
}
|
||||
|
||||
public void setAdminRole(boolean adminRole) {
|
||||
isAdminRole = adminRole;
|
||||
}
|
||||
|
||||
public boolean isDownloadRole() {
|
||||
return isDownloadRole;
|
||||
}
|
||||
|
||||
public void setDownloadRole(boolean downloadRole) {
|
||||
isDownloadRole = downloadRole;
|
||||
}
|
||||
|
||||
public boolean isUploadRole() {
|
||||
return isUploadRole;
|
||||
}
|
||||
|
||||
public void setUploadRole(boolean uploadRole) {
|
||||
isUploadRole = uploadRole;
|
||||
}
|
||||
|
||||
public boolean isCoverArtRole() {
|
||||
return isCoverArtRole;
|
||||
}
|
||||
|
||||
public void setCoverArtRole(boolean coverArtRole) {
|
||||
isCoverArtRole = coverArtRole;
|
||||
}
|
||||
|
||||
public boolean isCommentRole() {
|
||||
return isCommentRole;
|
||||
}
|
||||
|
||||
public void setCommentRole(boolean commentRole) {
|
||||
isCommentRole = commentRole;
|
||||
}
|
||||
|
||||
public boolean isPodcastRole() {
|
||||
return isPodcastRole;
|
||||
}
|
||||
|
||||
public void setPodcastRole(boolean podcastRole) {
|
||||
isPodcastRole = podcastRole;
|
||||
}
|
||||
|
||||
public boolean isStreamRole() {
|
||||
return isStreamRole;
|
||||
}
|
||||
|
||||
public void setStreamRole(boolean streamRole) {
|
||||
isStreamRole = streamRole;
|
||||
}
|
||||
|
||||
public boolean isJukeboxRole() {
|
||||
return isJukeboxRole;
|
||||
}
|
||||
|
||||
public void setJukeboxRole(boolean jukeboxRole) {
|
||||
isJukeboxRole = jukeboxRole;
|
||||
}
|
||||
|
||||
public boolean isSettingsRole() {
|
||||
return isSettingsRole;
|
||||
}
|
||||
|
||||
public void setSettingsRole(boolean settingsRole) {
|
||||
isSettingsRole = settingsRole;
|
||||
}
|
||||
|
||||
public boolean isShareRole() {
|
||||
return isShareRole;
|
||||
}
|
||||
|
||||
public void setShareRole(boolean shareRole) {
|
||||
isShareRole = shareRole;
|
||||
}
|
||||
|
||||
public List<User> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public void setUsers(List<User> users) {
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
public boolean isAdmin() {
|
||||
return isAdmin;
|
||||
}
|
||||
|
||||
public void setAdmin(boolean admin) {
|
||||
isAdmin = admin;
|
||||
}
|
||||
|
||||
public boolean isPasswordChange() {
|
||||
return isPasswordChange;
|
||||
}
|
||||
|
||||
public void setPasswordChange(boolean passwordChange) {
|
||||
isPasswordChange = passwordChange;
|
||||
}
|
||||
|
||||
public boolean isNewUser() {
|
||||
return isNewUser;
|
||||
}
|
||||
|
||||
public void setNewUser(boolean isNewUser) {
|
||||
this.isNewUser = isNewUser;
|
||||
}
|
||||
|
||||
public boolean isDeleteUser() {
|
||||
return isDeleteUser;
|
||||
}
|
||||
|
||||
public void setDeleteUser(boolean deleteUser) {
|
||||
this.isDeleteUser = deleteUser;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getConfirmPassword() {
|
||||
return confirmPassword;
|
||||
}
|
||||
|
||||
public void setConfirmPassword(String confirmPassword) {
|
||||
this.confirmPassword = confirmPassword;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public boolean isLdapAuthenticated() {
|
||||
return isLdapAuthenticated;
|
||||
}
|
||||
|
||||
public void setLdapAuthenticated(boolean ldapAuthenticated) {
|
||||
isLdapAuthenticated = ldapAuthenticated;
|
||||
}
|
||||
|
||||
public boolean isLdapEnabled() {
|
||||
return isLdapEnabled;
|
||||
}
|
||||
|
||||
public void setLdapEnabled(boolean ldapEnabled) {
|
||||
isLdapEnabled = ldapEnabled;
|
||||
}
|
||||
|
||||
public List<MusicFolder> getAllMusicFolders() {
|
||||
return allMusicFolders;
|
||||
}
|
||||
|
||||
public void setAllMusicFolders(List<MusicFolder> allMusicFolders) {
|
||||
this.allMusicFolders = allMusicFolders;
|
||||
}
|
||||
|
||||
public int[] getAllowedMusicFolderIds() {
|
||||
return allowedMusicFolderIds;
|
||||
}
|
||||
|
||||
public void setAllowedMusicFolderIds(int[] allowedMusicFolderIds) {
|
||||
this.allowedMusicFolderIds = allowedMusicFolderIds;
|
||||
}
|
||||
|
||||
public String getTranscodeSchemeName() {
|
||||
return transcodeSchemeName;
|
||||
}
|
||||
|
||||
public void setTranscodeSchemeName(String transcodeSchemeName) {
|
||||
this.transcodeSchemeName = transcodeSchemeName;
|
||||
}
|
||||
|
||||
public EnumHolder[] getTranscodeSchemeHolders() {
|
||||
return transcodeSchemeHolders;
|
||||
}
|
||||
|
||||
public void setTranscodeSchemes(TranscodeScheme[] transcodeSchemes) {
|
||||
transcodeSchemeHolders = new EnumHolder[transcodeSchemes.length];
|
||||
for (int i = 0; i < transcodeSchemes.length; i++) {
|
||||
TranscodeScheme scheme = transcodeSchemes[i];
|
||||
transcodeSchemeHolders[i] = new EnumHolder(scheme.name(), scheme.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTranscodingSupported() {
|
||||
return transcodingSupported;
|
||||
}
|
||||
|
||||
public void setTranscodingSupported(boolean transcodingSupported) {
|
||||
this.transcodingSupported = transcodingSupported;
|
||||
}
|
||||
|
||||
public String getTranscodeDirectory() {
|
||||
return transcodeDirectory;
|
||||
}
|
||||
|
||||
public void setTranscodeDirectory(String transcodeDirectory) {
|
||||
this.transcodeDirectory = transcodeDirectory;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
username = user == null ? null : user.getUsername();
|
||||
isAdminRole = user != null && user.isAdminRole();
|
||||
isDownloadRole = user != null && user.isDownloadRole();
|
||||
isUploadRole = user != null && user.isUploadRole();
|
||||
isCoverArtRole = user != null && user.isCoverArtRole();
|
||||
isCommentRole = user != null && user.isCommentRole();
|
||||
isPodcastRole = user != null && user.isPodcastRole();
|
||||
isStreamRole = user != null && user.isStreamRole();
|
||||
isJukeboxRole = user != null && user.isJukeboxRole();
|
||||
isSettingsRole = user != null && user.isSettingsRole();
|
||||
isShareRole = user != null && user.isShareRole();
|
||||
isLdapAuthenticated = user != null && user.isLdapAuthenticated();
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public boolean isReload() {
|
||||
return reload;
|
||||
}
|
||||
|
||||
public void setReload(boolean reload) {
|
||||
this.reload = reload;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 org.springframework.web.servlet.support.*;
|
||||
import org.springframework.web.servlet.mvc.*;
|
||||
import org.springframework.ui.context.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Abstract super class for controllers which generate charts.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public abstract class AbstractChartController implements Controller {
|
||||
|
||||
/**
|
||||
* Returns the chart background color for the current theme.
|
||||
* @param request The servlet request.
|
||||
* @return The chart background color.
|
||||
*/
|
||||
protected Color getBackground(HttpServletRequest request) {
|
||||
return getColor("backgroundColor", request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chart foreground color for the current theme.
|
||||
* @param request The servlet request.
|
||||
* @return The chart foreground color.
|
||||
*/
|
||||
protected Color getForeground(HttpServletRequest request) {
|
||||
return getColor("textColor", request);
|
||||
}
|
||||
|
||||
private Color getColor(String code, HttpServletRequest request) {
|
||||
Theme theme = RequestContextUtils.getTheme(request);
|
||||
Locale locale = RequestContextUtils.getLocale(request);
|
||||
String colorHex = theme.getMessageSource().getMessage(code, new Object[0], locale);
|
||||
return new Color(Integer.parseInt(colorHex, 16));
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.command.AdvancedSettingsCommand;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate advanced settings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class AdvancedSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
AdvancedSettingsCommand command = new AdvancedSettingsCommand();
|
||||
command.setDownloadLimit(String.valueOf(settingsService.getDownloadBitrateLimit()));
|
||||
command.setUploadLimit(String.valueOf(settingsService.getUploadBitrateLimit()));
|
||||
command.setLdapEnabled(settingsService.isLdapEnabled());
|
||||
command.setLdapUrl(settingsService.getLdapUrl());
|
||||
command.setLdapSearchFilter(settingsService.getLdapSearchFilter());
|
||||
command.setLdapManagerDn(settingsService.getLdapManagerDn());
|
||||
command.setLdapAutoShadowing(settingsService.isLdapAutoShadowing());
|
||||
command.setBrand(settingsService.getBrand());
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
AdvancedSettingsCommand command = (AdvancedSettingsCommand) comm;
|
||||
|
||||
command.setToast(true);
|
||||
command.setReloadNeeded(false);
|
||||
|
||||
try {
|
||||
settingsService.setDownloadBitrateLimit(Long.parseLong(command.getDownloadLimit()));
|
||||
} catch (NumberFormatException x) { /* Intentionally ignored. */ }
|
||||
try {
|
||||
settingsService.setUploadBitrateLimit(Long.parseLong(command.getUploadLimit()));
|
||||
} catch (NumberFormatException x) { /* Intentionally ignored. */ }
|
||||
|
||||
settingsService.setLdapEnabled(command.isLdapEnabled());
|
||||
settingsService.setLdapUrl(command.getLdapUrl());
|
||||
settingsService.setLdapSearchFilter(command.getLdapSearchFilter());
|
||||
settingsService.setLdapManagerDn(command.getLdapManagerDn());
|
||||
settingsService.setLdapAutoShadowing(command.isLdapAutoShadowing());
|
||||
|
||||
if (StringUtils.isNotEmpty(command.getLdapManagerPassword())) {
|
||||
settingsService.setLdapManagerPassword(command.getLdapManagerPassword());
|
||||
}
|
||||
|
||||
settingsService.save();
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
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 org.springframework.web.servlet.*;
|
||||
import org.springframework.web.servlet.mvc.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/**
|
||||
* Controller for the page which forwards to allmusic.com.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class AllmusicController extends ParameterizableViewController {
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("album", request.getParameter("album"));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
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 2013 (C) Sindre Mehus
|
||||
*/
|
||||
package net.sourceforge.subsonic.controller;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AutoCoverDemo {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
JFrame frame = new JFrame();
|
||||
JPanel panel = new JPanel();
|
||||
panel.add(new AlbumComponent(110, 110));
|
||||
panel.add(new AlbumComponent(150, 150));
|
||||
panel.add(new AlbumComponent(200, 200));
|
||||
panel.add(new AlbumComponent(300, 300));
|
||||
panel.add(new AlbumComponent(400, 240));
|
||||
panel.add(new AlbumComponent(240, 400));
|
||||
|
||||
panel.setBackground(Color.LIGHT_GRAY);
|
||||
frame.add(panel);
|
||||
frame.setSize(1000, 800);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private static class AlbumComponent extends JComponent {
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public AlbumComponent(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
setPreferredSize(new Dimension(width, height));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
String key = RandomStringUtils.random(5);
|
||||
new CoverArtController.AutoCover((Graphics2D) g, key, "Artist with a very long name", "Album", width, height).paintCover();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
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 javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
import org.springframework.web.servlet.mvc.LastModified;
|
||||
|
||||
import net.sourceforge.subsonic.domain.Avatar;
|
||||
import net.sourceforge.subsonic.domain.AvatarScheme;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller which produces avatar images.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class AvatarController implements Controller, LastModified {
|
||||
|
||||
private SettingsService settingsService;
|
||||
|
||||
public long getLastModified(HttpServletRequest request) {
|
||||
Avatar avatar = getAvatar(request);
|
||||
long result = avatar == null ? -1L : avatar.getCreatedDate().getTime();
|
||||
|
||||
String username = request.getParameter("username");
|
||||
if (username != null) {
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
result = Math.max(result, userSettings.getChanged().getTime());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Avatar avatar = getAvatar(request);
|
||||
|
||||
if (avatar == null) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return null;
|
||||
}
|
||||
|
||||
response.setContentType(avatar.getMimeType());
|
||||
response.getOutputStream().write(avatar.getData());
|
||||
return null;
|
||||
}
|
||||
|
||||
private Avatar getAvatar(HttpServletRequest request) {
|
||||
String id = request.getParameter("id");
|
||||
boolean forceCustom = ServletRequestUtils.getBooleanParameter(request, "forceCustom", false);
|
||||
|
||||
if (id != null) {
|
||||
return settingsService.getSystemAvatar(Integer.parseInt(id));
|
||||
}
|
||||
|
||||
String username = request.getParameter("username");
|
||||
if (username == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
if (userSettings.getAvatarScheme() == AvatarScheme.CUSTOM || forceCustom) {
|
||||
return settingsService.getCustomAvatar(username);
|
||||
}
|
||||
return settingsService.getSystemAvatar(userSettings.getSystemAvatarId());
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.domain.Avatar;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.FileItemFactory;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller which receives uploaded avatar images.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class AvatarUploadController extends ParameterizableViewController {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(AvatarUploadController.class);
|
||||
private static final int MAX_AVATAR_SIZE = 64;
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
|
||||
// Check that we have a file upload request.
|
||||
if (!ServletFileUpload.isMultipartContent(request)) {
|
||||
throw new Exception("Illegal request.");
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
FileItemFactory factory = new DiskFileItemFactory();
|
||||
ServletFileUpload upload = new ServletFileUpload(factory);
|
||||
List<?> items = upload.parseRequest(request);
|
||||
|
||||
// Look for file items.
|
||||
for (Object o : items) {
|
||||
FileItem item = (FileItem) o;
|
||||
|
||||
if (!item.isFormField()) {
|
||||
String fileName = item.getName();
|
||||
byte[] data = item.get();
|
||||
|
||||
if (StringUtils.isNotBlank(fileName) && data.length > 0) {
|
||||
createAvatar(fileName, data, username, map);
|
||||
} else {
|
||||
map.put("error", new Exception("Missing file."));
|
||||
LOG.warn("Failed to upload personal image. No file specified.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
map.put("username", username);
|
||||
map.put("avatar", settingsService.getCustomAvatar(username));
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void createAvatar(String fileName, byte[] data, String username, Map<String, Object> map) throws IOException {
|
||||
|
||||
BufferedImage image;
|
||||
try {
|
||||
image = ImageIO.read(new ByteArrayInputStream(data));
|
||||
if (image == null) {
|
||||
throw new Exception("Failed to decode incoming image: " + fileName + " (" + data.length + " bytes).");
|
||||
}
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
String mimeType = StringUtil.getMimeType(FilenameUtils.getExtension(fileName));
|
||||
|
||||
// Scale down image if necessary.
|
||||
if (width > MAX_AVATAR_SIZE || height > MAX_AVATAR_SIZE) {
|
||||
double scaleFactor = (double) MAX_AVATAR_SIZE / (double) Math.max(width, height);
|
||||
height = (int) (height * scaleFactor);
|
||||
width = (int) (width * scaleFactor);
|
||||
image = CoverArtController.scale(image, width, height);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "jpeg", out);
|
||||
data = out.toByteArray();
|
||||
mimeType = StringUtil.getMimeType("jpeg");
|
||||
map.put("resized", true);
|
||||
}
|
||||
Avatar avatar = new Avatar(0, fileName, new Date(), mimeType, width, height, data);
|
||||
settingsService.setCustomAvatar(avatar, username);
|
||||
LOG.info("Created avatar '" + fileName + "' (" + data.length + " bytes) for user " + username);
|
||||
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to upload personal image: " + x, x);
|
||||
map.put("error", x);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
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.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
|
||||
/**
|
||||
* Controller for changing cover art.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ChangeCoverArtController extends ParameterizableViewController {
|
||||
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
String artist = request.getParameter("artist");
|
||||
String album = request.getParameter("album");
|
||||
MediaFile dir = mediaFileService.getMediaFile(id);
|
||||
|
||||
if (artist == null) {
|
||||
artist = dir.getArtist();
|
||||
}
|
||||
if (album == null) {
|
||||
album = dir.getAlbumName();
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("id", id);
|
||||
map.put("artist", artist);
|
||||
map.put("album", album);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+731
@@ -0,0 +1,731 @@
|
||||
/*
|
||||
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.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.GradientPaint;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
import org.springframework.web.servlet.mvc.LastModified;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.dao.AlbumDao;
|
||||
import net.sourceforge.subsonic.dao.ArtistDao;
|
||||
import net.sourceforge.subsonic.domain.Album;
|
||||
import net.sourceforge.subsonic.domain.Artist;
|
||||
import net.sourceforge.subsonic.domain.CoverArtScheme;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.Playlist;
|
||||
import net.sourceforge.subsonic.domain.PodcastChannel;
|
||||
import net.sourceforge.subsonic.domain.Transcoding;
|
||||
import net.sourceforge.subsonic.domain.VideoTranscodingSettings;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlaylistService;
|
||||
import net.sourceforge.subsonic.service.PodcastService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.TranscodingService;
|
||||
import net.sourceforge.subsonic.service.metadata.JaudiotaggerParser;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller which produces cover art images.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class CoverArtController implements Controller, LastModified {
|
||||
|
||||
public static final String ALBUM_COVERART_PREFIX = "al-";
|
||||
public static final String ARTIST_COVERART_PREFIX = "ar-";
|
||||
public static final String PLAYLIST_COVERART_PREFIX = "pl-";
|
||||
public static final String PODCAST_COVERART_PREFIX = "pod-";
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(CoverArtController.class);
|
||||
|
||||
private MediaFileService mediaFileService;
|
||||
private TranscodingService transcodingService;
|
||||
private SettingsService settingsService;
|
||||
private PlaylistService playlistService;
|
||||
private PodcastService podcastService;
|
||||
private ArtistDao artistDao;
|
||||
private AlbumDao albumDao;
|
||||
private Semaphore semaphore;
|
||||
|
||||
public void init() {
|
||||
semaphore = new Semaphore(settingsService.getCoverArtConcurrency());
|
||||
}
|
||||
|
||||
public long getLastModified(HttpServletRequest request) {
|
||||
CoverArtRequest coverArtRequest = createCoverArtRequest(request);
|
||||
long result = coverArtRequest.lastModified();
|
||||
// LOG.info("getLastModified - " + coverArtRequest + ": " + new Date(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
CoverArtRequest coverArtRequest = createCoverArtRequest(request);
|
||||
// LOG.info("handleRequest - " + coverArtRequest);
|
||||
Integer size = ServletRequestUtils.getIntParameter(request, "size");
|
||||
|
||||
// Send fallback image if no ID is given. (No need to cache it, since it will be cached in browser.)
|
||||
if (coverArtRequest == null) {
|
||||
sendFallback(size, response);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Optimize if no scaling is required.
|
||||
if (size == null && coverArtRequest.getCoverArt() != null) {
|
||||
// LOG.info("sendUnscaled - " + coverArtRequest);
|
||||
sendUnscaled(coverArtRequest, response);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Send cached image, creating it if necessary.
|
||||
if (size == null) {
|
||||
size = CoverArtScheme.LARGE.getSize() * 2;
|
||||
}
|
||||
try {
|
||||
File cachedImage = getCachedImage(coverArtRequest, size);
|
||||
sendImage(cachedImage, response);
|
||||
} catch (IOException e) {
|
||||
sendFallback(size, response);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private CoverArtRequest createCoverArtRequest(HttpServletRequest request) {
|
||||
String id = request.getParameter("id");
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id.startsWith(ALBUM_COVERART_PREFIX)) {
|
||||
return createAlbumCoverArtRequest(Integer.valueOf(id.replace(ALBUM_COVERART_PREFIX, "")));
|
||||
}
|
||||
if (id.startsWith(ARTIST_COVERART_PREFIX)) {
|
||||
return createArtistCoverArtRequest(Integer.valueOf(id.replace(ARTIST_COVERART_PREFIX, "")));
|
||||
}
|
||||
if (id.startsWith(PLAYLIST_COVERART_PREFIX)) {
|
||||
return createPlaylistCoverArtRequest(Integer.valueOf(id.replace(PLAYLIST_COVERART_PREFIX, "")));
|
||||
}
|
||||
if (id.startsWith(PODCAST_COVERART_PREFIX)) {
|
||||
return createPodcastCoverArtRequest(Integer.valueOf(id.replace(PODCAST_COVERART_PREFIX, "")), request);
|
||||
}
|
||||
return createMediaFileCoverArtRequest(Integer.valueOf(id), request);
|
||||
}
|
||||
|
||||
private CoverArtRequest createAlbumCoverArtRequest(int id) {
|
||||
Album album = albumDao.getAlbum(id);
|
||||
return album == null ? null : new AlbumCoverArtRequest(album);
|
||||
}
|
||||
|
||||
private CoverArtRequest createArtistCoverArtRequest(int id) {
|
||||
Artist artist = artistDao.getArtist(id);
|
||||
return artist == null ? null : new ArtistCoverArtRequest(artist);
|
||||
}
|
||||
|
||||
private PlaylistCoverArtRequest createPlaylistCoverArtRequest(int id) {
|
||||
Playlist playlist = playlistService.getPlaylist(id);
|
||||
return playlist == null ? null : new PlaylistCoverArtRequest(playlist);
|
||||
}
|
||||
|
||||
private CoverArtRequest createPodcastCoverArtRequest(int id, HttpServletRequest request) {
|
||||
PodcastChannel channel = podcastService.getChannel(id);
|
||||
if (channel == null) {
|
||||
return null;
|
||||
}
|
||||
if (channel.getMediaFileId() == null) {
|
||||
return new PodcastCoverArtRequest(channel);
|
||||
}
|
||||
return createMediaFileCoverArtRequest(channel.getMediaFileId(), request);
|
||||
}
|
||||
|
||||
private CoverArtRequest createMediaFileCoverArtRequest(int id, HttpServletRequest request) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(id);
|
||||
if (mediaFile == null) {
|
||||
return null;
|
||||
}
|
||||
if (mediaFile.isVideo()) {
|
||||
int offset = ServletRequestUtils.getIntParameter(request, "offset", 60);
|
||||
return new VideoCoverArtRequest(mediaFile, offset);
|
||||
}
|
||||
return new MediaFileCoverArtRequest(mediaFile);
|
||||
}
|
||||
|
||||
private void sendImage(File file, HttpServletResponse response) throws IOException {
|
||||
response.setContentType(StringUtil.getMimeType(FilenameUtils.getExtension(file.getName())));
|
||||
InputStream in = new FileInputStream(file);
|
||||
try {
|
||||
IOUtils.copy(in, response.getOutputStream());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendFallback(Integer size, HttpServletResponse response) throws IOException {
|
||||
if (response.getContentType() == null) {
|
||||
response.setContentType(StringUtil.getMimeType("jpeg"));
|
||||
}
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getClass().getResourceAsStream("default_cover.jpg");
|
||||
BufferedImage image = ImageIO.read(in);
|
||||
if (size != null) {
|
||||
image = scale(image, size, size);
|
||||
}
|
||||
ImageIO.write(image, "jpeg", response.getOutputStream());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendUnscaled(CoverArtRequest coverArtRequest, HttpServletResponse response) throws IOException {
|
||||
File file = coverArtRequest.getCoverArt();
|
||||
JaudiotaggerParser parser = new JaudiotaggerParser();
|
||||
if (!parser.isApplicable(file)) {
|
||||
response.setContentType(StringUtil.getMimeType(FilenameUtils.getExtension(file.getName())));
|
||||
}
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getImageInputStream(file);
|
||||
IOUtils.copy(in, response.getOutputStream());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
private File getCachedImage(CoverArtRequest request, int size) throws IOException {
|
||||
String hash = DigestUtils.md5Hex(request.getKey());
|
||||
String encoding = request.getCoverArt() != null ? "jpeg" : "png";
|
||||
File cachedImage = new File(getImageCacheDirectory(size), hash + "." + encoding);
|
||||
|
||||
// Synchronize to avoid concurrent writing to the same file.
|
||||
synchronized (hash.intern()) {
|
||||
|
||||
// Is cache missing or obsolete?
|
||||
if (!cachedImage.exists() || request.lastModified() > cachedImage.lastModified()) {
|
||||
// LOG.info("Cache MISS - " + request + " (" + size + ")");
|
||||
OutputStream out = null;
|
||||
try {
|
||||
semaphore.acquire();
|
||||
BufferedImage image = request.createImage(size);
|
||||
if (image == null) {
|
||||
throw new Exception("Unable to decode image.");
|
||||
}
|
||||
out = new FileOutputStream(cachedImage);
|
||||
ImageIO.write(image, encoding, out);
|
||||
|
||||
} catch (Throwable x) {
|
||||
// Delete corrupt (probably empty) thumbnail cache.
|
||||
LOG.warn("Failed to create thumbnail for " + request, x);
|
||||
IOUtils.closeQuietly(out);
|
||||
cachedImage.delete();
|
||||
throw new IOException("Failed to create thumbnail for " + request + ". " + x.getMessage());
|
||||
|
||||
} finally {
|
||||
semaphore.release();
|
||||
IOUtils.closeQuietly(out);
|
||||
}
|
||||
} else {
|
||||
// LOG.info("Cache HIT - " + request + " (" + size + ")");
|
||||
}
|
||||
return cachedImage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input stream to the image in the given file. If the file is an audio file,
|
||||
* the embedded album art is returned.
|
||||
*/
|
||||
private InputStream getImageInputStream(File file) throws IOException {
|
||||
JaudiotaggerParser parser = new JaudiotaggerParser();
|
||||
if (parser.isApplicable(file)) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(file);
|
||||
return new ByteArrayInputStream(parser.getImageData(mediaFile));
|
||||
} else {
|
||||
return new FileInputStream(file);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream getImageInputStreamForVideo(MediaFile mediaFile, int width, int height, int offset) throws Exception {
|
||||
VideoTranscodingSettings videoSettings = new VideoTranscodingSettings(width, height, offset, 0, false);
|
||||
TranscodingService.Parameters parameters = new TranscodingService.Parameters(mediaFile, videoSettings);
|
||||
String command = settingsService.getVideoImageCommand();
|
||||
parameters.setTranscoding(new Transcoding(null, null, null, null, command, null, null, false));
|
||||
return transcodingService.getTranscodedInputStream(parameters);
|
||||
}
|
||||
|
||||
private synchronized File getImageCacheDirectory(int size) {
|
||||
File dir = new File(SettingsService.getSubsonicHome(), "thumbs");
|
||||
dir = new File(dir, String.valueOf(size));
|
||||
if (!dir.exists()) {
|
||||
if (dir.mkdirs()) {
|
||||
LOG.info("Created thumbnail cache " + dir);
|
||||
} else {
|
||||
LOG.error("Failed to create thumbnail cache " + dir);
|
||||
}
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
public static BufferedImage scale(BufferedImage image, int width, int height) {
|
||||
int w = image.getWidth();
|
||||
int h = image.getHeight();
|
||||
BufferedImage thumb = image;
|
||||
|
||||
// For optimal results, use step by step bilinear resampling - halfing the size at each step.
|
||||
do {
|
||||
w /= 2;
|
||||
h /= 2;
|
||||
if (w < width) {
|
||||
w = width;
|
||||
}
|
||||
if (h < height) {
|
||||
h = height;
|
||||
}
|
||||
|
||||
BufferedImage temp = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2 = temp.createGraphics();
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
|
||||
g2.dispose();
|
||||
|
||||
thumb = temp;
|
||||
} while (w != width);
|
||||
|
||||
return thumb;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setArtistDao(ArtistDao artistDao) {
|
||||
this.artistDao = artistDao;
|
||||
}
|
||||
|
||||
public void setAlbumDao(AlbumDao albumDao) {
|
||||
this.albumDao = albumDao;
|
||||
}
|
||||
|
||||
public void setTranscodingService(TranscodingService transcodingService) {
|
||||
this.transcodingService = transcodingService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
|
||||
private abstract class CoverArtRequest {
|
||||
|
||||
protected File coverArt;
|
||||
|
||||
private CoverArtRequest() {
|
||||
}
|
||||
|
||||
private CoverArtRequest(String coverArtPath) {
|
||||
this.coverArt = coverArtPath == null ? null : new File(coverArtPath);
|
||||
}
|
||||
|
||||
private File getCoverArt() {
|
||||
return coverArt;
|
||||
}
|
||||
|
||||
public abstract String getKey();
|
||||
|
||||
public abstract long lastModified();
|
||||
|
||||
public BufferedImage createImage(int size) {
|
||||
if (coverArt != null) {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getImageInputStream(coverArt);
|
||||
return scale(ImageIO.read(in), size, size);
|
||||
} catch (Throwable x) {
|
||||
LOG.warn("Failed to process cover art " + coverArt + ": " + x, x);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
return createAutoCover(size, size);
|
||||
}
|
||||
|
||||
protected BufferedImage createAutoCover(int width, int height) {
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
AutoCover autoCover = new AutoCover(graphics, getKey(), getArtist(), getAlbum(), width, height);
|
||||
autoCover.paintCover();
|
||||
graphics.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
public abstract String getAlbum();
|
||||
|
||||
public abstract String getArtist();
|
||||
}
|
||||
|
||||
private class ArtistCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final Artist artist;
|
||||
|
||||
private ArtistCoverArtRequest(Artist artist) {
|
||||
super(artist.getCoverArtPath());
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return artist.getCoverArtPath() != null ? artist.getCoverArtPath() : (ARTIST_COVERART_PREFIX + artist.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return coverArt != null ? coverArt.lastModified() : artist.getLastScanned().getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return artist.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Artist " + artist.getId() + " - " + artist.getName();
|
||||
}
|
||||
}
|
||||
|
||||
private class AlbumCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final Album album;
|
||||
|
||||
private AlbumCoverArtRequest(Album album) {
|
||||
super(album.getCoverArtPath());
|
||||
this.album = album;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return album.getCoverArtPath() != null ? album.getCoverArtPath() : (ALBUM_COVERART_PREFIX + album.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return coverArt != null ? coverArt.lastModified() : album.getLastScanned().getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return album.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return album.getArtist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Album " + album.getId() + " - " + album.getName();
|
||||
}
|
||||
}
|
||||
|
||||
private class PlaylistCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final Playlist playlist;
|
||||
|
||||
private PlaylistCoverArtRequest(Playlist playlist) {
|
||||
super(null);
|
||||
this.playlist = playlist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return PLAYLIST_COVERART_PREFIX + playlist.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return playlist.getChanged().getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return playlist.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Playlist " + playlist.getId() + " - " + playlist.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage createImage(int size) {
|
||||
List<MediaFile> albums = getRepresentativeAlbums();
|
||||
if (albums.isEmpty()) {
|
||||
return createAutoCover(size, size);
|
||||
}
|
||||
if (albums.size() < 4) {
|
||||
return new MediaFileCoverArtRequest(albums.get(0)).createImage(size);
|
||||
}
|
||||
|
||||
BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
|
||||
int half = size / 2;
|
||||
graphics.drawImage(new MediaFileCoverArtRequest(albums.get(0)).createImage(half), null, 0, 0);
|
||||
graphics.drawImage(new MediaFileCoverArtRequest(albums.get(1)).createImage(half), null, half, 0);
|
||||
graphics.drawImage(new MediaFileCoverArtRequest(albums.get(2)).createImage(half), null, 0, half);
|
||||
graphics.drawImage(new MediaFileCoverArtRequest(albums.get(3)).createImage(half), null, half, half);
|
||||
graphics.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
private List<MediaFile> getRepresentativeAlbums() {
|
||||
Set<MediaFile> albums = new LinkedHashSet<MediaFile>();
|
||||
for (MediaFile song : playlistService.getFilesInPlaylist(playlist.getId())) {
|
||||
MediaFile album = mediaFileService.getParentOf(song);
|
||||
if (album != null && !mediaFileService.isRoot(album)) {
|
||||
albums.add(album);
|
||||
}
|
||||
}
|
||||
return new ArrayList<MediaFile>(albums);
|
||||
}
|
||||
}
|
||||
|
||||
private class PodcastCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final PodcastChannel channel;
|
||||
|
||||
public PodcastCoverArtRequest(PodcastChannel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return PODCAST_COVERART_PREFIX + channel.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return channel.getTitle() != null ? channel.getTitle() : channel.getUrl();
|
||||
}
|
||||
}
|
||||
|
||||
private class MediaFileCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final MediaFile mediaFile;
|
||||
private final MediaFile dir;
|
||||
|
||||
private MediaFileCoverArtRequest(MediaFile mediaFile) {
|
||||
this.mediaFile = mediaFile;
|
||||
dir = mediaFile.isDirectory() ? mediaFile : mediaFileService.getParentOf(mediaFile);
|
||||
coverArt = mediaFileService.getCoverArt(mediaFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return coverArt != null ? coverArt.getPath() : dir.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return coverArt != null ? coverArt.lastModified() : dir.getChanged().getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return dir.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return dir.getAlbumArtist() != null ? dir.getAlbumArtist() : dir.getArtist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Media file " + mediaFile.getId() + " - " + mediaFile;
|
||||
}
|
||||
}
|
||||
|
||||
private class VideoCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final MediaFile mediaFile;
|
||||
private final int offset;
|
||||
|
||||
private VideoCoverArtRequest(MediaFile mediaFile, int offset) {
|
||||
this.mediaFile = mediaFile;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage createImage(int size) {
|
||||
int height = size;
|
||||
int width = height * 16 / 9;
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getImageInputStreamForVideo(mediaFile, width, height, offset);
|
||||
BufferedImage result = ImageIO.read(in);
|
||||
if (result == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return result;
|
||||
} catch (Throwable x) {
|
||||
LOG.warn("Failed to process cover art for " + mediaFile + ": " + x, x);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
return createAutoCover(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return mediaFile.getPath() + "/" + offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return mediaFile.getChanged().getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return mediaFile.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Video file " + mediaFile.getId() + " - " + mediaFile;
|
||||
}
|
||||
}
|
||||
|
||||
static class AutoCover {
|
||||
|
||||
private final static int[] COLORS = {0x33B5E5, 0xAA66CC, 0x99CC00, 0xFFBB33, 0xFF4444};
|
||||
private final Graphics2D graphics;
|
||||
private final String artist;
|
||||
private final String album;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final Color color;
|
||||
|
||||
public AutoCover(Graphics2D graphics, String key, String artist, String album, int width, int height) {
|
||||
this.graphics = graphics;
|
||||
this.artist = artist;
|
||||
this.album = album;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
int hash = key.hashCode();
|
||||
int rgb = COLORS[Math.abs(hash) % COLORS.length];
|
||||
this.color = new Color(rgb);
|
||||
}
|
||||
|
||||
public void paintCover() {
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
|
||||
graphics.setPaint(color);
|
||||
graphics.fillRect(0, 0, width, height);
|
||||
|
||||
int y = height * 2 / 3;
|
||||
graphics.setPaint(new GradientPaint(0, y, new Color(82, 82, 82), 0, height, Color.BLACK));
|
||||
graphics.fillRect(0, y, width, height / 3);
|
||||
|
||||
graphics.setPaint(Color.WHITE);
|
||||
float fontSize = 3.0f + height * 0.07f;
|
||||
Font font = new Font(Font.SANS_SERIF, Font.BOLD, (int) fontSize);
|
||||
graphics.setFont(font);
|
||||
|
||||
if (album != null) {
|
||||
graphics.drawString(album, width * 0.05f, height * 0.6f);
|
||||
}
|
||||
if (artist != null) {
|
||||
graphics.drawString(artist, width * 0.05f, height * 0.8f);
|
||||
}
|
||||
|
||||
int borderWidth = height / 50;
|
||||
graphics.fillRect(0, 0, borderWidth, height);
|
||||
graphics.fillRect(width - borderWidth, 0, height - borderWidth, height);
|
||||
graphics.fillRect(0, 0, width, borderWidth);
|
||||
graphics.fillRect(0, height - borderWidth, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.dao.DaoHelper;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.ColumnMapRowMapper;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for the DB admin page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class DBController extends ParameterizableViewController {
|
||||
|
||||
private DaoHelper daoHelper;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
String query = request.getParameter("query");
|
||||
if (query != null) {
|
||||
map.put("query", query);
|
||||
|
||||
try {
|
||||
List<?> result = daoHelper.getJdbcTemplate().query(query, new ColumnMapRowMapper());
|
||||
map.put("result", result);
|
||||
} catch (DataAccessException x) {
|
||||
map.put("error", ExceptionUtils.getRootCause(x).getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setDaoHelper(DaoHelper daoHelper) {
|
||||
this.daoHelper = daoHelper;
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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 2013 (C) Sindre Mehus
|
||||
*/
|
||||
package net.sourceforge.subsonic.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.UPnPService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate the UPnP/DLNA server settings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class DLNASettingsController extends ParameterizableViewController {
|
||||
|
||||
private UPnPService upnpService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
if (isFormSubmission(request)) {
|
||||
handleParameters(request);
|
||||
map.put("toast", true);
|
||||
}
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
map.put("dlnaEnabled", settingsService.isDlnaEnabled());
|
||||
map.put("dlnaServerName", settingsService.getDlnaServerName());
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given request represents a form submission.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return if the request represents a form submission
|
||||
*/
|
||||
private boolean isFormSubmission(HttpServletRequest request) {
|
||||
return "POST".equals(request.getMethod());
|
||||
}
|
||||
|
||||
private void handleParameters(HttpServletRequest request) {
|
||||
boolean dlnaEnabled = ServletRequestUtils.getBooleanParameter(request, "dlnaEnabled", false);
|
||||
String dlnaServerName = StringUtils.trimToNull(request.getParameter("dlnaServerName"));
|
||||
if (dlnaServerName == null) {
|
||||
dlnaServerName = "Subsonic";
|
||||
}
|
||||
|
||||
upnpService.setMediaServerEnabled(false);
|
||||
settingsService.setDlnaEnabled(dlnaEnabled);
|
||||
settingsService.setDlnaServerName(dlnaServerName);
|
||||
settingsService.save();
|
||||
upnpService.setMediaServerEnabled(dlnaEnabled);
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setUpnpService(UPnPService upnpService) {
|
||||
this.upnpService = upnpService;
|
||||
}
|
||||
}
|
||||
+414
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
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.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
import org.springframework.web.servlet.mvc.LastModified;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.PlayQueue;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.Playlist;
|
||||
import net.sourceforge.subsonic.domain.TransferStatus;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.io.RangeOutputStream;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.PlaylistService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.StatusService;
|
||||
import net.sourceforge.subsonic.util.FileUtil;
|
||||
import net.sourceforge.subsonic.util.HttpRange;
|
||||
import net.sourceforge.subsonic.util.Util;
|
||||
|
||||
/**
|
||||
* A controller used for downloading files to a remote client. If the requested path refers to a file, the
|
||||
* given file is downloaded. If the requested path refers to a directory, the entire directory (including
|
||||
* sub-directories) are downloaded as an uncompressed zip-file.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class DownloadController implements Controller, LastModified {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(DownloadController.class);
|
||||
|
||||
private PlayerService playerService;
|
||||
private StatusService statusService;
|
||||
private SecurityService securityService;
|
||||
private PlaylistService playlistService;
|
||||
private SettingsService settingsService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
public long getLastModified(HttpServletRequest request) {
|
||||
try {
|
||||
MediaFile mediaFile = getMediaFile(request);
|
||||
if (mediaFile == null || mediaFile.isDirectory() || mediaFile.getChanged() == null) {
|
||||
return -1;
|
||||
}
|
||||
return mediaFile.getChanged().getTime();
|
||||
} catch (ServletRequestBindingException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
TransferStatus status = null;
|
||||
try {
|
||||
|
||||
status = statusService.createDownloadStatus(playerService.getPlayer(request, response, false, false));
|
||||
|
||||
MediaFile mediaFile = getMediaFile(request);
|
||||
|
||||
Integer playlistId = ServletRequestUtils.getIntParameter(request, "playlist");
|
||||
String playerId = request.getParameter("player");
|
||||
int[] indexes = request.getParameter("i") == null ? null : ServletRequestUtils.getIntParameters(request, "i");
|
||||
|
||||
if (mediaFile != null) {
|
||||
response.setIntHeader("ETag", mediaFile.getId());
|
||||
response.setHeader("Accept-Ranges", "bytes");
|
||||
}
|
||||
|
||||
HttpRange range = HttpRange.valueOf(request.getHeader("Range"));
|
||||
if (range != null) {
|
||||
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
||||
LOG.info("Got HTTP range: " + range);
|
||||
}
|
||||
|
||||
if (mediaFile != null) {
|
||||
if (!securityService.isFolderAccessAllowed(mediaFile, user.getUsername())) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||
"Access to file " + mediaFile.getId() + " is forbidden for user " + user.getUsername());
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mediaFile.isFile()) {
|
||||
downloadFile(response, status, mediaFile.getFile(), range);
|
||||
} else {
|
||||
List<MediaFile> children = mediaFileService.getChildrenOf(mediaFile, true, false, true);
|
||||
String zipFileName = FilenameUtils.getBaseName(mediaFile.getPath()) + ".zip";
|
||||
File coverArtFile = indexes == null ? mediaFile.getCoverArtFile() : null;
|
||||
downloadFiles(response, status, children, indexes, coverArtFile, range, zipFileName);
|
||||
}
|
||||
|
||||
} else if (playlistId != null) {
|
||||
List<MediaFile> songs = playlistService.getFilesInPlaylist(playlistId);
|
||||
Playlist playlist = playlistService.getPlaylist(playlistId);
|
||||
downloadFiles(response, status, songs, null, null, range, playlist.getName() + ".zip");
|
||||
|
||||
} else if (playerId != null) {
|
||||
Player player = playerService.getPlayerById(playerId);
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
playQueue.setName("Playlist");
|
||||
downloadFiles(response, status, playQueue.getFiles(), indexes, null, range, "download.zip");
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (status != null) {
|
||||
statusService.removeDownloadStatus(status);
|
||||
securityService.updateUserByteCounts(user, 0L, status.getBytesTransfered(), 0L);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private MediaFile getMediaFile(HttpServletRequest request) throws ServletRequestBindingException {
|
||||
Integer id = ServletRequestUtils.getIntParameter(request, "id");
|
||||
return id == null ? null : mediaFileService.getMediaFile(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a single file.
|
||||
*
|
||||
*
|
||||
* @param response The HTTP response.
|
||||
* @param status The download status.
|
||||
* @param file The file to download.
|
||||
* @param range The byte range, may be <code>null</code>.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private void downloadFile(HttpServletResponse response, TransferStatus status, File file, HttpRange range) throws IOException {
|
||||
LOG.info("Starting to download '" + FileUtil.getShortPath(file) + "' to " + status.getPlayer());
|
||||
status.setFile(file);
|
||||
|
||||
response.setContentType("application/x-download");
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodeAsRFC5987(file.getName()));
|
||||
if (range == null) {
|
||||
Util.setContentLength(response, file.length());
|
||||
}
|
||||
|
||||
copyFileToStream(file, RangeOutputStream.wrap(response.getOutputStream(), range), status, range);
|
||||
LOG.info("Downloaded '" + FileUtil.getShortPath(file) + "' to " + status.getPlayer());
|
||||
}
|
||||
|
||||
private String encodeAsRFC5987(String string) throws UnsupportedEncodingException {
|
||||
byte[] stringAsByteArray = string.getBytes("UTF-8");
|
||||
char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
byte[] attrChar = {'!', '#', '$', '&', '+', '-', '.', '^', '_', '`', '|', '~', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : stringAsByteArray) {
|
||||
if (Arrays.binarySearch(attrChar, b) >= 0) {
|
||||
sb.append((char) b);
|
||||
} else {
|
||||
sb.append('%');
|
||||
sb.append(digits[0x0f & (b >>> 4)]);
|
||||
sb.append(digits[b & 0x0f]);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the given files. The files are packed together in an
|
||||
* uncompressed zip-file.
|
||||
*
|
||||
*
|
||||
* @param response The HTTP response.
|
||||
* @param status The download status.
|
||||
* @param files The files to download.
|
||||
* @param indexes Only download songs at these indexes. May be <code>null</code>.
|
||||
* @param coverArtFile The cover art file to include, may be {@code null}.
|
||||
*@param range The byte range, may be <code>null</code>.
|
||||
* @param zipFileName The name of the resulting zip file. @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private void downloadFiles(HttpServletResponse response, TransferStatus status, List<MediaFile> files, int[] indexes, File coverArtFile, HttpRange range, String zipFileName) throws IOException {
|
||||
if (indexes != null && indexes.length == 1) {
|
||||
downloadFile(response, status, files.get(indexes[0]).getFile(), range);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Starting to download '" + zipFileName + "' to " + status.getPlayer());
|
||||
response.setContentType("application/x-download");
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodeAsRFC5987(zipFileName));
|
||||
|
||||
ZipOutputStream out = new ZipOutputStream(RangeOutputStream.wrap(response.getOutputStream(), range));
|
||||
out.setMethod(ZipOutputStream.STORED); // No compression.
|
||||
|
||||
List<MediaFile> filesToDownload = new ArrayList<MediaFile>();
|
||||
if (indexes == null) {
|
||||
filesToDownload.addAll(files);
|
||||
} else {
|
||||
for (int index : indexes) {
|
||||
try {
|
||||
filesToDownload.add(files.get(index));
|
||||
} catch (IndexOutOfBoundsException x) { /* Ignored */}
|
||||
}
|
||||
}
|
||||
|
||||
for (MediaFile mediaFile : filesToDownload) {
|
||||
zip(out, mediaFile.getParentFile(), mediaFile.getFile(), status, range);
|
||||
}
|
||||
if (coverArtFile != null && coverArtFile.exists()) {
|
||||
zip(out, coverArtFile.getParentFile(), coverArtFile, status, range);
|
||||
}
|
||||
|
||||
|
||||
out.close();
|
||||
LOG.info("Downloaded '" + zipFileName + "' to " + status.getPlayer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for writing the content of a given file to a given output stream.
|
||||
*
|
||||
*
|
||||
* @param file The file to copy.
|
||||
* @param out The output stream to write to.
|
||||
* @param status The download status.
|
||||
* @param range The byte range, may be <code>null</code>.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private void copyFileToStream(File file, OutputStream out, TransferStatus status, HttpRange range) throws IOException {
|
||||
LOG.info("Downloading '" + FileUtil.getShortPath(file) + "' to " + status.getPlayer());
|
||||
|
||||
final int bufferSize = 16 * 1024; // 16 Kbit
|
||||
InputStream in = new BufferedInputStream(new FileInputStream(file), bufferSize);
|
||||
|
||||
try {
|
||||
byte[] buf = new byte[bufferSize];
|
||||
long bitrateLimit = 0;
|
||||
long lastLimitCheck = 0;
|
||||
|
||||
while (true) {
|
||||
long before = System.currentTimeMillis();
|
||||
int n = in.read(buf);
|
||||
if (n == -1) {
|
||||
break;
|
||||
}
|
||||
out.write(buf, 0, n);
|
||||
|
||||
// Don't sleep if outside range.
|
||||
if (range != null && !range.contains(status.getBytesSkipped() + status.getBytesTransfered())) {
|
||||
status.addBytesSkipped(n);
|
||||
continue;
|
||||
}
|
||||
|
||||
status.addBytesTransfered(n);
|
||||
long after = System.currentTimeMillis();
|
||||
|
||||
// Calculate bitrate limit every 5 seconds.
|
||||
if (after - lastLimitCheck > 5000) {
|
||||
bitrateLimit = 1024L * settingsService.getDownloadBitrateLimit() /
|
||||
Math.max(1, statusService.getAllDownloadStatuses().size());
|
||||
lastLimitCheck = after;
|
||||
}
|
||||
|
||||
// Sleep for a while to throttle bitrate.
|
||||
if (bitrateLimit != 0) {
|
||||
long sleepTime = 8L * 1000 * bufferSize / bitrateLimit - (after - before);
|
||||
if (sleepTime > 0L) {
|
||||
try {
|
||||
Thread.sleep(sleepTime);
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to sleep.", x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
out.flush();
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a file or a directory structure to a zip output stream. File entries in the zip file are relative
|
||||
* to the given root.
|
||||
*
|
||||
*
|
||||
* @param out The zip output stream.
|
||||
* @param root The root of the directory structure. Used to create path information in the zip file.
|
||||
* @param file The file or directory to zip.
|
||||
* @param status The download status.
|
||||
* @param range The byte range, may be <code>null</code>.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private void zip(ZipOutputStream out, File root, File file, TransferStatus status, HttpRange range) throws IOException {
|
||||
|
||||
// Exclude all hidden files starting with a "."
|
||||
if (file.getName().startsWith(".")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String zipName = file.getCanonicalPath().substring(root.getCanonicalPath().length() + 1);
|
||||
|
||||
if (file.isFile()) {
|
||||
status.setFile(file);
|
||||
|
||||
ZipEntry zipEntry = new ZipEntry(zipName);
|
||||
zipEntry.setSize(file.length());
|
||||
zipEntry.setCompressedSize(file.length());
|
||||
zipEntry.setCrc(computeCrc(file));
|
||||
|
||||
out.putNextEntry(zipEntry);
|
||||
copyFileToStream(file, out, status, range);
|
||||
out.closeEntry();
|
||||
|
||||
} else {
|
||||
ZipEntry zipEntry = new ZipEntry(zipName + '/');
|
||||
zipEntry.setSize(0);
|
||||
zipEntry.setCompressedSize(0);
|
||||
zipEntry.setCrc(0);
|
||||
|
||||
out.putNextEntry(zipEntry);
|
||||
out.closeEntry();
|
||||
|
||||
File[] children = FileUtil.listFiles(file);
|
||||
for (File child : children) {
|
||||
zip(out, root, child, status, range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the CRC checksum for the given file.
|
||||
*
|
||||
* @param file The file to compute checksum for.
|
||||
* @return A CRC32 checksum.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private long computeCrc(File file) throws IOException {
|
||||
CRC32 crc = new CRC32();
|
||||
InputStream in = new FileInputStream(file);
|
||||
|
||||
try {
|
||||
|
||||
byte[] buf = new byte[8192];
|
||||
int n = in.read(buf);
|
||||
while (n != -1) {
|
||||
crc.update(buf, 0, n);
|
||||
n = in.read(buf);
|
||||
}
|
||||
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
|
||||
return crc.getValue();
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setStatusService(StatusService statusService) {
|
||||
this.statusService = statusService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
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.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.metadata.JaudiotaggerParser;
|
||||
import net.sourceforge.subsonic.service.metadata.MetaDataParser;
|
||||
import net.sourceforge.subsonic.service.metadata.MetaDataParserFactory;
|
||||
|
||||
/**
|
||||
* Controller for the page used to edit MP3 tags.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class EditTagsController extends ParameterizableViewController {
|
||||
|
||||
private MetaDataParserFactory metaDataParserFactory;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
MediaFile dir = mediaFileService.getMediaFile(id);
|
||||
List<MediaFile> files = mediaFileService.getChildrenOf(dir, true, false, true, false);
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
if (!files.isEmpty()) {
|
||||
map.put("defaultArtist", files.get(0).getArtist());
|
||||
map.put("defaultAlbum", files.get(0).getAlbumName());
|
||||
map.put("defaultYear", files.get(0).getYear());
|
||||
map.put("defaultGenre", files.get(0).getGenre());
|
||||
}
|
||||
map.put("allGenres", JaudiotaggerParser.getID3V1Genres());
|
||||
|
||||
List<Song> songs = new ArrayList<Song>();
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
songs.add(createSong(files.get(i), i));
|
||||
}
|
||||
map.put("id", id);
|
||||
map.put("songs", songs);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Song createSong(MediaFile file, int index) {
|
||||
MetaDataParser parser = metaDataParserFactory.getParser(file.getFile());
|
||||
|
||||
Song song = new Song();
|
||||
song.setId(file.getId());
|
||||
song.setFileName(FilenameUtils.getBaseName(file.getPath()));
|
||||
song.setTrack(file.getTrackNumber());
|
||||
song.setSuggestedTrack(index + 1);
|
||||
song.setTitle(file.getTitle());
|
||||
song.setSuggestedTitle(parser.guessTitle(file.getFile()));
|
||||
song.setArtist(file.getArtist());
|
||||
song.setAlbum(file.getAlbumName());
|
||||
song.setYear(file.getYear());
|
||||
song.setGenre(file.getGenre());
|
||||
return song;
|
||||
}
|
||||
|
||||
public void setMetaDataParserFactory(MetaDataParserFactory metaDataParserFactory) {
|
||||
this.metaDataParserFactory = metaDataParserFactory;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains information about a single song.
|
||||
*/
|
||||
public static class Song {
|
||||
private int id;
|
||||
private String fileName;
|
||||
private Integer suggestedTrack;
|
||||
private Integer track;
|
||||
private String suggestedTitle;
|
||||
private String title;
|
||||
private String artist;
|
||||
private String album;
|
||||
private Integer year;
|
||||
private String genre;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public Integer getSuggestedTrack() {
|
||||
return suggestedTrack;
|
||||
}
|
||||
|
||||
public void setSuggestedTrack(Integer suggestedTrack) {
|
||||
this.suggestedTrack = suggestedTrack;
|
||||
}
|
||||
|
||||
public Integer getTrack() {
|
||||
return track;
|
||||
}
|
||||
|
||||
public void setTrack(Integer track) {
|
||||
this.track = track;
|
||||
}
|
||||
|
||||
public String getSuggestedTitle() {
|
||||
return suggestedTitle;
|
||||
}
|
||||
|
||||
public void setSuggestedTitle(String suggestedTitle) {
|
||||
this.suggestedTitle = suggestedTitle;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public void setArtist(String artist) {
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public void setAlbum(String album) {
|
||||
this.album = album;
|
||||
}
|
||||
|
||||
public Integer getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
public void setYear(Integer year) {
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
public String getGenre() {
|
||||
return genre;
|
||||
}
|
||||
|
||||
public void setGenre(String genre) {
|
||||
this.genre = genre;
|
||||
}
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
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.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.Share;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.ShareService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to play shared music (Twitter, Facebook etc).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ExternalPlayerController extends ParameterizableViewController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private PlayerService playerService;
|
||||
private ShareService shareService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
String pathInfo = request.getPathInfo();
|
||||
|
||||
if (pathInfo == null || !pathInfo.startsWith("/")) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return null;
|
||||
}
|
||||
|
||||
Share share = shareService.getShareByName(pathInfo.substring(1));
|
||||
|
||||
if (share != null && share.getExpires() != null && share.getExpires().before(new Date())) {
|
||||
share = null;
|
||||
}
|
||||
|
||||
if (share != null) {
|
||||
share.setLastVisited(new Date());
|
||||
share.setVisitCount(share.getVisitCount() + 1);
|
||||
shareService.updateShare(share);
|
||||
}
|
||||
|
||||
Player player = playerService.getGuestPlayer(request);
|
||||
|
||||
map.put("share", share);
|
||||
map.put("songs", getSongs(share, player.getUsername()));
|
||||
map.put("redirectUrl", settingsService.getUrlRedirectUrl());
|
||||
map.put("player", player.getId());
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<MediaFile> getSongs(Share share, String username) throws IOException {
|
||||
List<MediaFile> result = new ArrayList<MediaFile>();
|
||||
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
|
||||
if (share != null) {
|
||||
for (MediaFile file : shareService.getSharedFiles(share.getId(), musicFolders)) {
|
||||
if (file.getFile().exists()) {
|
||||
if (file.isDirectory()) {
|
||||
result.addAll(mediaFileService.getChildrenOf(file, true, false, true));
|
||||
} else {
|
||||
result.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setShareService(ShareService shareService) {
|
||||
this.shareService = shareService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
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.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import net.sourceforge.subsonic.command.GeneralSettingsCommand;
|
||||
import net.sourceforge.subsonic.domain.Theme;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate general settings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class GeneralSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
GeneralSettingsCommand command = new GeneralSettingsCommand();
|
||||
command.setCoverArtFileTypes(settingsService.getCoverArtFileTypes());
|
||||
command.setIgnoredArticles(settingsService.getIgnoredArticles());
|
||||
command.setShortcuts(settingsService.getShortcuts());
|
||||
command.setIndex(settingsService.getIndexString());
|
||||
command.setPlaylistFolder(settingsService.getPlaylistFolder());
|
||||
command.setMusicFileTypes(settingsService.getMusicFileTypes());
|
||||
command.setVideoFileTypes(settingsService.getVideoFileTypes());
|
||||
command.setSortAlbumsByYear(settingsService.isSortAlbumsByYear());
|
||||
command.setGettingStartedEnabled(settingsService.isGettingStartedEnabled());
|
||||
command.setWelcomeTitle(settingsService.getWelcomeTitle());
|
||||
command.setWelcomeSubtitle(settingsService.getWelcomeSubtitle());
|
||||
command.setWelcomeMessage(settingsService.getWelcomeMessage());
|
||||
command.setLoginMessage(settingsService.getLoginMessage());
|
||||
|
||||
Theme[] themes = settingsService.getAvailableThemes();
|
||||
command.setThemes(themes);
|
||||
String currentThemeId = settingsService.getThemeId();
|
||||
for (int i = 0; i < themes.length; i++) {
|
||||
if (currentThemeId.equals(themes[i].getId())) {
|
||||
command.setThemeIndex(String.valueOf(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Locale currentLocale = settingsService.getLocale();
|
||||
Locale[] locales = settingsService.getAvailableLocales();
|
||||
String[] localeStrings = new String[locales.length];
|
||||
for (int i = 0; i < locales.length; i++) {
|
||||
localeStrings[i] = locales[i].getDisplayName(locales[i]);
|
||||
|
||||
if (currentLocale.equals(locales[i])) {
|
||||
command.setLocaleIndex(String.valueOf(i));
|
||||
}
|
||||
}
|
||||
command.setLocales(localeStrings);
|
||||
|
||||
return command;
|
||||
|
||||
}
|
||||
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
GeneralSettingsCommand command = (GeneralSettingsCommand) comm;
|
||||
|
||||
int themeIndex = Integer.parseInt(command.getThemeIndex());
|
||||
Theme theme = settingsService.getAvailableThemes()[themeIndex];
|
||||
|
||||
int localeIndex = Integer.parseInt(command.getLocaleIndex());
|
||||
Locale locale = settingsService.getAvailableLocales()[localeIndex];
|
||||
|
||||
command.setToast(true);
|
||||
command.setReloadNeeded(!settingsService.getIndexString().equals(command.getIndex()) ||
|
||||
!settingsService.getIgnoredArticles().equals(command.getIgnoredArticles()) ||
|
||||
!settingsService.getShortcuts().equals(command.getShortcuts()) ||
|
||||
!settingsService.getThemeId().equals(theme.getId()) ||
|
||||
!settingsService.getLocale().equals(locale));
|
||||
|
||||
settingsService.setIndexString(command.getIndex());
|
||||
settingsService.setIgnoredArticles(command.getIgnoredArticles());
|
||||
settingsService.setShortcuts(command.getShortcuts());
|
||||
settingsService.setPlaylistFolder(command.getPlaylistFolder());
|
||||
settingsService.setMusicFileTypes(command.getMusicFileTypes());
|
||||
settingsService.setVideoFileTypes(command.getVideoFileTypes());
|
||||
settingsService.setCoverArtFileTypes(command.getCoverArtFileTypes());
|
||||
settingsService.setSortAlbumsByYear(command.isSortAlbumsByYear());
|
||||
settingsService.setGettingStartedEnabled(command.isGettingStartedEnabled());
|
||||
settingsService.setWelcomeTitle(command.getWelcomeTitle());
|
||||
settingsService.setWelcomeSubtitle(command.getWelcomeSubtitle());
|
||||
settingsService.setWelcomeMessage(command.getWelcomeMessage());
|
||||
settingsService.setLoginMessage(command.getLoginMessage());
|
||||
settingsService.setThemeId(theme.getId());
|
||||
settingsService.setLocale(locale);
|
||||
settingsService.save();
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
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.awt.Dimension;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.util.Pair;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller which produces the HLS (Http Live Streaming) playlist.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class HLSController implements Controller {
|
||||
|
||||
private static final int SEGMENT_DURATION = 10;
|
||||
private static final Pattern BITRATE_PATTERN = Pattern.compile("(\\d+)(@(\\d+)x(\\d+))?");
|
||||
|
||||
private PlayerService playerService;
|
||||
private MediaFileService mediaFileService;
|
||||
private SecurityService securityService;
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
|
||||
int id = ServletRequestUtils.getIntParameter(request, "id");
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(id);
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
String username = player.getUsername();
|
||||
|
||||
if (mediaFile == null) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Media file not found: " + id);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (username != null && !securityService.isFolderAccessAllowed(mediaFile, username)) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||
"Access to file " + mediaFile.getId() + " is forbidden for user " + username);
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer duration = mediaFile.getDurationSeconds();
|
||||
if (duration == null || duration == 0) {
|
||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unknown duration for media file: " + id);
|
||||
return null;
|
||||
}
|
||||
|
||||
response.setContentType("application/vnd.apple.mpegurl");
|
||||
response.setCharacterEncoding(StringUtil.ENCODING_UTF8);
|
||||
List<Pair<Integer, Dimension>> bitRates = parseBitRates(request);
|
||||
PrintWriter writer = response.getWriter();
|
||||
if (bitRates.size() > 1) {
|
||||
generateVariantPlaylist(request, id, player, bitRates, writer);
|
||||
} else {
|
||||
generateNormalPlaylist(request, id, player, bitRates.size() == 1 ? bitRates.get(0) : null, duration, writer);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Pair<Integer, Dimension>> parseBitRates(HttpServletRequest request) throws IllegalArgumentException {
|
||||
List<Pair<Integer, Dimension>> result = new ArrayList<Pair<Integer, Dimension>>();
|
||||
String[] bitRates = request.getParameterValues("bitRate");
|
||||
if (bitRates != null) {
|
||||
for (String bitRate : bitRates) {
|
||||
result.add(parseBitRate(bitRate));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string containing the bitrate and an optional width/height, e.g., 1200@640x480
|
||||
*/
|
||||
protected Pair<Integer, Dimension> parseBitRate(String bitRate) throws IllegalArgumentException {
|
||||
|
||||
Matcher matcher = BITRATE_PATTERN.matcher(bitRate);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException("Invalid bitrate specification: " + bitRate);
|
||||
}
|
||||
int kbps = Integer.parseInt(matcher.group(1));
|
||||
if (matcher.group(3) == null) {
|
||||
return new Pair<Integer, Dimension>(kbps, null);
|
||||
} else {
|
||||
int width = Integer.parseInt(matcher.group(3));
|
||||
int height = Integer.parseInt(matcher.group(4));
|
||||
return new Pair<Integer, Dimension>(kbps, new Dimension(width, height));
|
||||
}
|
||||
}
|
||||
|
||||
private void generateVariantPlaylist(HttpServletRequest request, int id, Player player, List<Pair<Integer, Dimension>> bitRates, PrintWriter writer) {
|
||||
writer.println("#EXTM3U");
|
||||
writer.println("#EXT-X-VERSION:1");
|
||||
// writer.println("#EXT-X-TARGETDURATION:" + SEGMENT_DURATION);
|
||||
|
||||
String contextPath = getContextPath(request);
|
||||
for (Pair<Integer, Dimension> bitRate : bitRates) {
|
||||
Integer kbps = bitRate.getFirst();
|
||||
writer.println("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + kbps * 1000L);
|
||||
writer.print(contextPath + "hls/hls.m3u8?id=" + id + "&player=" + player.getId() + "&bitRate=" + kbps);
|
||||
Dimension dimension = bitRate.getSecond();
|
||||
if (dimension != null) {
|
||||
writer.print("@" + dimension.width + "x" + dimension.height);
|
||||
}
|
||||
writer.println();
|
||||
}
|
||||
// writer.println("#EXT-X-ENDLIST");
|
||||
}
|
||||
|
||||
private void generateNormalPlaylist(HttpServletRequest request, int id, Player player, Pair<Integer, Dimension> bitRate, int totalDuration, PrintWriter writer) {
|
||||
writer.println("#EXTM3U");
|
||||
writer.println("#EXT-X-VERSION:1");
|
||||
writer.println("#EXT-X-TARGETDURATION:" + SEGMENT_DURATION);
|
||||
|
||||
for (int i = 0; i < totalDuration / SEGMENT_DURATION; i++) {
|
||||
int offset = i * SEGMENT_DURATION;
|
||||
writer.println("#EXTINF:" + SEGMENT_DURATION + ",");
|
||||
writer.println(createStreamUrl(request, player, id, offset, SEGMENT_DURATION, bitRate));
|
||||
}
|
||||
|
||||
int remainder = totalDuration % SEGMENT_DURATION;
|
||||
if (remainder > 0) {
|
||||
writer.println("#EXTINF:" + remainder + ",");
|
||||
int offset = totalDuration - remainder;
|
||||
writer.println(createStreamUrl(request, player, id, offset, remainder, bitRate));
|
||||
}
|
||||
writer.println("#EXT-X-ENDLIST");
|
||||
}
|
||||
|
||||
private String createStreamUrl(HttpServletRequest request, Player player, int id, int offset, int duration, Pair<Integer, Dimension> bitRate) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(getContextPath(request)).append("stream/stream.ts?id=").append(id).append("&hls=true&timeOffset=").append(offset)
|
||||
.append("&player=").append(player.getId()).append("&duration=").append(duration);
|
||||
if (bitRate != null) {
|
||||
builder.append("&maxBitRate=").append(bitRate.getFirst());
|
||||
Dimension dimension = bitRate.getSecond();
|
||||
if (dimension != null) {
|
||||
builder.append("&size=").append(dimension.width).append("x").append(dimension.height);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private String getContextPath(HttpServletRequest request) {
|
||||
String contextPath = request.getContextPath();
|
||||
if (StringUtils.isEmpty(contextPath)) {
|
||||
contextPath = "/";
|
||||
} else {
|
||||
contextPath += "/";
|
||||
}
|
||||
return contextPath;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
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.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.VersionService;
|
||||
|
||||
/**
|
||||
* Controller for the help page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class HelpController extends ParameterizableViewController {
|
||||
|
||||
private VersionService versionService;
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
if (versionService.isNewFinalVersionAvailable()) {
|
||||
map.put("newVersionAvailable", true);
|
||||
map.put("latestVersion", versionService.getLatestFinalVersion());
|
||||
} else if (versionService.isNewBetaVersionAvailable()) {
|
||||
map.put("newVersionAvailable", true);
|
||||
map.put("latestVersion", versionService.getLatestBetaVersion());
|
||||
}
|
||||
|
||||
long totalMemory = Runtime.getRuntime().totalMemory();
|
||||
long freeMemory = Runtime.getRuntime().freeMemory();
|
||||
|
||||
String serverInfo = request.getSession().getServletContext().getServerInfo() +
|
||||
", java " + System.getProperty("java.version") +
|
||||
", " + System.getProperty("os.name");
|
||||
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("brand", settingsService.getBrand());
|
||||
map.put("localVersion", versionService.getLocalVersion());
|
||||
map.put("buildDate", versionService.getLocalBuildDate());
|
||||
map.put("buildNumber", versionService.getLocalBuildNumber());
|
||||
map.put("serverInfo", serverInfo);
|
||||
map.put("usedMemory", totalMemory - freeMemory);
|
||||
map.put("totalMemory", totalMemory);
|
||||
map.put("logEntries", Logger.getLatestLogEntries());
|
||||
map.put("logFile", Logger.getLogFile());
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setVersionService(VersionService versionService) {
|
||||
this.versionService = versionService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
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.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import net.sourceforge.subsonic.domain.AlbumListType;
|
||||
import net.sourceforge.subsonic.domain.CoverArtScheme;
|
||||
import net.sourceforge.subsonic.domain.Genre;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.MediaScannerService;
|
||||
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 static org.springframework.web.bind.ServletRequestUtils.getIntParameter;
|
||||
import static org.springframework.web.bind.ServletRequestUtils.getStringParameter;
|
||||
|
||||
/**
|
||||
* Controller for the home page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class HomeController extends ParameterizableViewController {
|
||||
|
||||
private static final int LIST_SIZE = 40;
|
||||
|
||||
private SettingsService settingsService;
|
||||
private MediaScannerService mediaScannerService;
|
||||
private RatingService ratingService;
|
||||
private SecurityService securityService;
|
||||
private MediaFileService mediaFileService;
|
||||
private SearchService searchService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
if (user.isAdminRole() && settingsService.isGettingStartedEnabled()) {
|
||||
return new ModelAndView(new RedirectView("gettingStarted.view"));
|
||||
}
|
||||
int listOffset = getIntParameter(request, "listOffset", 0);
|
||||
AlbumListType listType = AlbumListType.fromId(getStringParameter(request, "listType"));
|
||||
if (listType == null) {
|
||||
UserSettings userSettings = settingsService.getUserSettings(user.getUsername());
|
||||
listType = userSettings.getDefaultAlbumList();
|
||||
}
|
||||
|
||||
MusicFolder selectedMusicFolder = settingsService.getSelectedMusicFolder(user.getUsername());
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(user.getUsername(),
|
||||
selectedMusicFolder == null ? null : selectedMusicFolder.getId());
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
List<Album> albums = Collections.emptyList();
|
||||
switch (listType) {
|
||||
case HIGHEST:
|
||||
albums = getHighestRated(listOffset, LIST_SIZE, musicFolders);
|
||||
break;
|
||||
case FREQUENT:
|
||||
albums = getMostFrequent(listOffset, LIST_SIZE, musicFolders);
|
||||
break;
|
||||
case RECENT:
|
||||
albums = getMostRecent(listOffset, LIST_SIZE, musicFolders);
|
||||
break;
|
||||
case NEWEST:
|
||||
albums = getNewest(listOffset, LIST_SIZE, musicFolders);
|
||||
break;
|
||||
case STARRED:
|
||||
albums = getStarred(listOffset, LIST_SIZE, user.getUsername(), musicFolders);
|
||||
break;
|
||||
case RANDOM:
|
||||
albums = getRandom(LIST_SIZE, musicFolders);
|
||||
break;
|
||||
case ALPHABETICAL:
|
||||
albums = getAlphabetical(listOffset, LIST_SIZE, true, musicFolders);
|
||||
break;
|
||||
case DECADE:
|
||||
List<Integer> decades = createDecades();
|
||||
map.put("decades", decades);
|
||||
int decade = getIntParameter(request, "decade", decades.get(0));
|
||||
map.put("decade", decade);
|
||||
albums = getByYear(listOffset, LIST_SIZE, decade, decade + 9, musicFolders);
|
||||
break;
|
||||
case GENRE:
|
||||
List<Genre> genres = mediaFileService.getGenres(true);
|
||||
map.put("genres", genres);
|
||||
if (!genres.isEmpty()) {
|
||||
String genre = getStringParameter(request, "genre", genres.get(0).getName());
|
||||
map.put("genre", genre);
|
||||
albums = getByGenre(listOffset, LIST_SIZE, genre, musicFolders);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
map.put("albums", albums);
|
||||
map.put("welcomeTitle", settingsService.getWelcomeTitle());
|
||||
map.put("welcomeSubtitle", settingsService.getWelcomeSubtitle());
|
||||
map.put("welcomeMessage", settingsService.getWelcomeMessage());
|
||||
map.put("isIndexBeingCreated", mediaScannerService.isScanning());
|
||||
map.put("musicFoldersExist", !settingsService.getAllMusicFolders().isEmpty());
|
||||
map.put("listType", listType.getId());
|
||||
map.put("listSize", LIST_SIZE);
|
||||
map.put("coverArtSize", CoverArtScheme.MEDIUM.getSize());
|
||||
map.put("listOffset", listOffset);
|
||||
map.put("musicFolder", selectedMusicFolder);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getHighestRated(int offset, int count, List<MusicFolder> musicFolders) {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile mediaFile : ratingService.getHighestRatedAlbums(offset, count, musicFolders)) {
|
||||
Album album = createAlbum(mediaFile);
|
||||
album.setRating((int) Math.round(ratingService.getAverageRating(mediaFile) * 10.0D));
|
||||
result.add(album);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getMostFrequent(int offset, int count, List<MusicFolder> musicFolders) {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile mediaFile : mediaFileService.getMostFrequentlyPlayedAlbums(offset, count, musicFolders)) {
|
||||
Album album = createAlbum(mediaFile);
|
||||
album.setPlayCount(mediaFile.getPlayCount());
|
||||
result.add(album);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getMostRecent(int offset, int count, List<MusicFolder> musicFolders) {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile mediaFile : mediaFileService.getMostRecentlyPlayedAlbums(offset, count, musicFolders)) {
|
||||
Album album = createAlbum(mediaFile);
|
||||
album.setLastPlayed(mediaFile.getLastPlayed());
|
||||
result.add(album);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getNewest(int offset, int count, List<MusicFolder> musicFolders) throws IOException {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : mediaFileService.getNewestAlbums(offset, count, musicFolders)) {
|
||||
Album album = createAlbum(file);
|
||||
Date created = file.getCreated();
|
||||
if (created == null) {
|
||||
created = file.getChanged();
|
||||
}
|
||||
album.setCreated(created);
|
||||
result.add(album);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getStarred(int offset, int count, String username, List<MusicFolder> musicFolders) throws IOException {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : mediaFileService.getStarredAlbums(offset, count, username, musicFolders)) {
|
||||
result.add(createAlbum(file));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getRandom(int count, List<MusicFolder> musicFolders) throws IOException {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : searchService.getRandomAlbums(count, musicFolders)) {
|
||||
result.add(createAlbum(file));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getAlphabetical(int offset, int count, boolean byArtist, List<MusicFolder> musicFolders) throws IOException {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : mediaFileService.getAlphabeticalAlbums(offset, count, byArtist, musicFolders)) {
|
||||
result.add(createAlbum(file));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getByYear(int offset, int count, int fromYear, int toYear, List<MusicFolder> musicFolders) {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : mediaFileService.getAlbumsByYear(offset, count, fromYear, toYear, musicFolders)) {
|
||||
Album album = createAlbum(file);
|
||||
album.setYear(file.getYear());
|
||||
result.add(album);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Integer> createDecades() {
|
||||
List<Integer> result = new ArrayList<Integer>();
|
||||
int decade = Calendar.getInstance().get(Calendar.YEAR) / 10;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
result.add((decade - i) * 10);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getByGenre(int offset, int count, String genre, List<MusicFolder> musicFolders) {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : mediaFileService.getAlbumsByGenre(offset, count, genre, musicFolders)) {
|
||||
result.add(createAlbum(file));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Album createAlbum(MediaFile file) {
|
||||
Album album = new Album();
|
||||
album.setId(file.getId());
|
||||
album.setPath(file.getPath());
|
||||
album.setArtist(file.getArtist());
|
||||
album.setAlbumTitle(file.getAlbumName());
|
||||
album.setCoverArtPath(file.getCoverArtPath());
|
||||
return album;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setMediaScannerService(MediaScannerService mediaScannerService) {
|
||||
this.mediaScannerService = mediaScannerService;
|
||||
}
|
||||
|
||||
public void setRatingService(RatingService ratingService) {
|
||||
this.ratingService = ratingService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains info for a single album.
|
||||
*/
|
||||
public static class Album {
|
||||
private String path;
|
||||
private String coverArtPath;
|
||||
private String artist;
|
||||
private String albumTitle;
|
||||
private Date created;
|
||||
private Date lastPlayed;
|
||||
private Integer playCount;
|
||||
private Integer rating;
|
||||
private int id;
|
||||
private Integer year;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getCoverArtPath() {
|
||||
return coverArtPath;
|
||||
}
|
||||
|
||||
public void setCoverArtPath(String coverArtPath) {
|
||||
this.coverArtPath = coverArtPath;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public void setArtist(String artist) {
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public String getAlbumTitle() {
|
||||
return albumTitle;
|
||||
}
|
||||
|
||||
public void setAlbumTitle(String albumTitle) {
|
||||
this.albumTitle = albumTitle;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Date getLastPlayed() {
|
||||
return lastPlayed;
|
||||
}
|
||||
|
||||
public void setLastPlayed(Date lastPlayed) {
|
||||
this.lastPlayed = lastPlayed;
|
||||
}
|
||||
|
||||
public Integer getPlayCount() {
|
||||
return playCount;
|
||||
}
|
||||
|
||||
public void setPlayCount(Integer playCount) {
|
||||
this.playCount = playCount;
|
||||
}
|
||||
|
||||
public Integer getRating() {
|
||||
return rating;
|
||||
}
|
||||
|
||||
public void setRating(Integer rating) {
|
||||
this.rating = rating;
|
||||
}
|
||||
|
||||
public void setYear(Integer year) {
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
public Integer getYear() {
|
||||
return year;
|
||||
}
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
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.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.FileItemFactory;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.Playlist;
|
||||
import net.sourceforge.subsonic.service.PlaylistService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ImportPlaylistController extends ParameterizableViewController {
|
||||
|
||||
private static final long MAX_PLAYLIST_SIZE_MB = 5L;
|
||||
|
||||
private SecurityService securityService;
|
||||
private PlaylistService playlistService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
try {
|
||||
if (ServletFileUpload.isMultipartContent(request)) {
|
||||
|
||||
FileItemFactory factory = new DiskFileItemFactory();
|
||||
ServletFileUpload upload = new ServletFileUpload(factory);
|
||||
List<?> items = upload.parseRequest(request);
|
||||
for (Object o : items) {
|
||||
FileItem item = (FileItem) o;
|
||||
|
||||
if ("file".equals(item.getFieldName()) && !StringUtils.isBlank(item.getName())) {
|
||||
if (item.getSize() > MAX_PLAYLIST_SIZE_MB * 1024L * 1024L) {
|
||||
throw new Exception("The playlist file is too large. Max file size is " + MAX_PLAYLIST_SIZE_MB + " MB.");
|
||||
}
|
||||
String playlistName = FilenameUtils.getBaseName(item.getName());
|
||||
String fileName = FilenameUtils.getName(item.getName());
|
||||
String format = StringUtils.lowerCase(FilenameUtils.getExtension(item.getName()));
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
Playlist playlist = playlistService.importPlaylist(username, playlistName, fileName, format, item.getInputStream(), null);
|
||||
map.put("playlist", playlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
map.put("error", e.getMessage());
|
||||
}
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.domain.InternetRadio;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate the set of internet radio/tv stations.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class InternetRadioSettingsController extends ParameterizableViewController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
if (isFormSubmission(request)) {
|
||||
String error = handleParameters(request);
|
||||
map.put("error", error);
|
||||
if (error == null) {
|
||||
map.put("reload", true);
|
||||
}
|
||||
}
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
map.put("internetRadios", settingsService.getAllInternetRadios(true));
|
||||
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given request represents a form submission.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return if the request represents a form submission
|
||||
*/
|
||||
private boolean isFormSubmission(HttpServletRequest request) {
|
||||
return "POST".equals(request.getMethod());
|
||||
}
|
||||
|
||||
private String handleParameters(HttpServletRequest request) {
|
||||
List<InternetRadio> radios = settingsService.getAllInternetRadios(true);
|
||||
for (InternetRadio radio : radios) {
|
||||
Integer id = radio.getId();
|
||||
String streamUrl = getParameter(request, "streamUrl", id);
|
||||
String homepageUrl = getParameter(request, "homepageUrl", id);
|
||||
String name = getParameter(request, "name", id);
|
||||
boolean enabled = getParameter(request, "enabled", id) != null;
|
||||
boolean delete = getParameter(request, "delete", id) != null;
|
||||
|
||||
if (delete) {
|
||||
settingsService.deleteInternetRadio(id);
|
||||
} else {
|
||||
if (name == null) {
|
||||
return "internetradiosettings.noname";
|
||||
}
|
||||
if (streamUrl == null) {
|
||||
return "internetradiosettings.nourl";
|
||||
}
|
||||
settingsService.updateInternetRadio(new InternetRadio(id, name, streamUrl, homepageUrl, enabled, new Date()));
|
||||
}
|
||||
}
|
||||
|
||||
String name = StringUtils.trimToNull(request.getParameter("name"));
|
||||
String streamUrl = StringUtils.trimToNull(request.getParameter("streamUrl"));
|
||||
String homepageUrl = StringUtils.trimToNull(request.getParameter("homepageUrl"));
|
||||
boolean enabled = StringUtils.trimToNull(request.getParameter("enabled")) != null;
|
||||
|
||||
if (name != null || streamUrl != null || homepageUrl != null) {
|
||||
if (name == null) {
|
||||
return "internetradiosettings.noname";
|
||||
}
|
||||
if (streamUrl == null) {
|
||||
return "internetradiosettings.nourl";
|
||||
}
|
||||
settingsService.createInternetRadio(new InternetRadio(name, streamUrl, homepageUrl, enabled, new Date()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getParameter(HttpServletRequest request, String name, Integer id) {
|
||||
return StringUtils.trimToNull(request.getParameter(name + "[" + id + "]"));
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
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.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.datatype.DatatypeFactory;
|
||||
import javax.xml.datatype.XMLGregorianCalendar;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.eclipse.persistence.jaxb.JAXBContext;
|
||||
import org.eclipse.persistence.jaxb.MarshallerProperties;
|
||||
import org.jdom.Attribute;
|
||||
import org.jdom.Document;
|
||||
import org.jdom.input.SAXBuilder;
|
||||
import org.subsonic.restapi.Error;
|
||||
import org.subsonic.restapi.ObjectFactory;
|
||||
import org.subsonic.restapi.Response;
|
||||
import org.subsonic.restapi.ResponseStatus;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
import static org.springframework.web.bind.ServletRequestUtils.getStringParameter;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
* @version $Id$
|
||||
*/
|
||||
public class JAXBWriter {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(JAXBWriter.class);
|
||||
|
||||
private final javax.xml.bind.JAXBContext jaxbContext;
|
||||
private final DatatypeFactory datatypeFactory;
|
||||
private final String restProtocolVersion;
|
||||
|
||||
public JAXBWriter() {
|
||||
try {
|
||||
jaxbContext = JAXBContext.newInstance(Response.class);
|
||||
datatypeFactory = DatatypeFactory.newInstance();
|
||||
restProtocolVersion = getRESTProtocolVersion();
|
||||
} catch (Exception x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private Marshaller createXmlMarshaller() throws JAXBException {
|
||||
Marshaller marshaller = jaxbContext.createMarshaller();
|
||||
marshaller.setProperty(Marshaller.JAXB_ENCODING, StringUtil.ENCODING_UTF8);
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
private Marshaller createJsonMarshaller() throws JAXBException {
|
||||
Marshaller marshaller = jaxbContext.createMarshaller();
|
||||
marshaller.setProperty(Marshaller.JAXB_ENCODING, StringUtil.ENCODING_UTF8);
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
|
||||
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
|
||||
marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, true);
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
private String getRESTProtocolVersion() throws Exception {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = StringUtil.class.getResourceAsStream("/subsonic-rest-api.xsd");
|
||||
Document document = new SAXBuilder().build(in);
|
||||
Attribute version = document.getRootElement().getAttribute("version");
|
||||
return version.getValue();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRestProtocolVersion() {
|
||||
return restProtocolVersion;
|
||||
}
|
||||
|
||||
public Response createResponse(boolean ok) {
|
||||
Response response = new ObjectFactory().createResponse();
|
||||
response.setStatus(ok ? ResponseStatus.OK : ResponseStatus.FAILED);
|
||||
response.setVersion(restProtocolVersion);
|
||||
return response;
|
||||
}
|
||||
|
||||
public void writeResponse(HttpServletRequest request, HttpServletResponse httpResponse, Response jaxbResponse) throws Exception {
|
||||
|
||||
String format = getStringParameter(request, "f", "xml");
|
||||
String jsonpCallback = request.getParameter("callback");
|
||||
boolean json = "json".equals(format);
|
||||
boolean jsonp = "jsonp".equals(format) && jsonpCallback != null;
|
||||
Marshaller marshaller;
|
||||
|
||||
if (json) {
|
||||
marshaller = createJsonMarshaller();
|
||||
httpResponse.setContentType("application/json");
|
||||
} else if (jsonp) {
|
||||
marshaller = createJsonMarshaller();
|
||||
httpResponse.setContentType("text/javascript");
|
||||
} else {
|
||||
marshaller = createXmlMarshaller();
|
||||
httpResponse.setContentType("text/xml");
|
||||
}
|
||||
|
||||
httpResponse.setCharacterEncoding(StringUtil.ENCODING_UTF8);
|
||||
|
||||
try {
|
||||
StringWriter writer = new StringWriter();
|
||||
if (jsonp) {
|
||||
writer.append(jsonpCallback).append('(');
|
||||
}
|
||||
marshaller.marshal(new ObjectFactory().createSubsonicResponse(jaxbResponse), writer);
|
||||
if (jsonp) {
|
||||
writer.append(");");
|
||||
}
|
||||
httpResponse.getWriter().append(writer.getBuffer());
|
||||
} catch (Exception x) {
|
||||
LOG.error("Failed to marshal JAXB", x);
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
|
||||
public void writeErrorResponse(HttpServletRequest request, HttpServletResponse response,
|
||||
RESTController.ErrorCode code, String message) throws Exception {
|
||||
Response res = createResponse(false);
|
||||
Error error = new Error();
|
||||
res.setError(error);
|
||||
error.setCode(code.getCode());
|
||||
error.setMessage(message);
|
||||
writeResponse(request, response, res);
|
||||
}
|
||||
|
||||
public XMLGregorianCalendar convertDate(Date date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GregorianCalendar c = new GregorianCalendar();
|
||||
c.setTime(date);
|
||||
return datatypeFactory.newXMLGregorianCalendar(c).normalize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
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.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
import net.sourceforge.subsonic.domain.InternetRadio;
|
||||
import net.sourceforge.subsonic.domain.MediaLibraryStatistics;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.MusicFolderContent;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.MediaScannerService;
|
||||
import net.sourceforge.subsonic.service.MusicIndexService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.util.FileUtil;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller for the left index frame.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class LeftController extends ParameterizableViewController {
|
||||
|
||||
// Update this time if you want to force a refresh in clients.
|
||||
private static final Calendar LAST_COMPATIBILITY_TIME = Calendar.getInstance();
|
||||
static {
|
||||
LAST_COMPATIBILITY_TIME.set(2012, Calendar.MARCH, 6, 0, 0, 0);
|
||||
LAST_COMPATIBILITY_TIME.set(Calendar.MILLISECOND, 0);
|
||||
}
|
||||
|
||||
private MediaScannerService mediaScannerService;
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
private MusicIndexService musicIndexService;
|
||||
private PlayerService playerService;
|
||||
|
||||
/**
|
||||
* Note: This class intentionally does not implement org.springframework.web.servlet.mvc.LastModified
|
||||
* as we don't need browser-side caching of left.jsp. This method is only used by RESTController.
|
||||
*/
|
||||
public long getLastModified(HttpServletRequest request) {
|
||||
saveSelectedMusicFolder(request);
|
||||
|
||||
if (mediaScannerService.isScanning()) {
|
||||
return -1L;
|
||||
}
|
||||
|
||||
long lastModified = LAST_COMPATIBILITY_TIME.getTimeInMillis();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
|
||||
// When was settings last changed?
|
||||
lastModified = Math.max(lastModified, settingsService.getSettingsChanged());
|
||||
|
||||
// When was music folder(s) on disk last changed?
|
||||
List<MusicFolder> allMusicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
MusicFolder selectedMusicFolder = settingsService.getSelectedMusicFolder(username);
|
||||
if (selectedMusicFolder != null) {
|
||||
File file = selectedMusicFolder.getPath();
|
||||
lastModified = Math.max(lastModified, FileUtil.lastModified(file));
|
||||
} else {
|
||||
for (MusicFolder musicFolder : allMusicFolders) {
|
||||
File file = musicFolder.getPath();
|
||||
lastModified = Math.max(lastModified, FileUtil.lastModified(file));
|
||||
}
|
||||
}
|
||||
|
||||
// When was music folder table last changed?
|
||||
for (MusicFolder musicFolder : allMusicFolders) {
|
||||
lastModified = Math.max(lastModified, musicFolder.getChanged().getTime());
|
||||
}
|
||||
|
||||
// When was internet radio table last changed?
|
||||
for (InternetRadio internetRadio : settingsService.getAllInternetRadios()) {
|
||||
lastModified = Math.max(lastModified, internetRadio.getChanged().getTime());
|
||||
}
|
||||
|
||||
// When was user settings last changed?
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
lastModified = Math.max(lastModified, userSettings.getChanged().getTime());
|
||||
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
boolean musicFolderChanged = saveSelectedMusicFolder(request);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
MediaLibraryStatistics statistics = mediaScannerService.getStatistics();
|
||||
Locale locale = RequestContextUtils.getLocale(request);
|
||||
|
||||
boolean refresh = ServletRequestUtils.getBooleanParameter(request, "refresh", false);
|
||||
if (refresh) {
|
||||
settingsService.clearMusicFolderCache();
|
||||
}
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MusicFolder> allMusicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
MusicFolder selectedMusicFolder = settingsService.getSelectedMusicFolder(username);
|
||||
List<MusicFolder> musicFoldersToUse = selectedMusicFolder == null ? allMusicFolders : Arrays.asList(selectedMusicFolder);
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
MusicFolderContent musicFolderContent = musicIndexService.getMusicFolderContent(musicFoldersToUse, refresh);
|
||||
|
||||
map.put("player", playerService.getPlayer(request, response));
|
||||
map.put("scanning", mediaScannerService.isScanning());
|
||||
map.put("musicFolders", allMusicFolders);
|
||||
map.put("selectedMusicFolder", selectedMusicFolder);
|
||||
map.put("radios", settingsService.getAllInternetRadios());
|
||||
map.put("shortcuts", musicIndexService.getShortcuts(musicFoldersToUse));
|
||||
map.put("partyMode", userSettings.isPartyModeEnabled());
|
||||
map.put("organizeByFolderStructure", settingsService.isOrganizeByFolderStructure());
|
||||
map.put("musicFolderChanged", musicFolderChanged);
|
||||
|
||||
if (statistics != null) {
|
||||
map.put("statistics", statistics);
|
||||
long bytes = statistics.getTotalLengthInBytes();
|
||||
long hours = statistics.getTotalDurationInSeconds() / 3600L;
|
||||
map.put("hours", hours);
|
||||
map.put("bytes", StringUtil.formatBytes(bytes, locale));
|
||||
}
|
||||
|
||||
map.put("indexedArtists", musicFolderContent.getIndexedArtists());
|
||||
map.put("singleSongs", musicFolderContent.getSingleSongs());
|
||||
map.put("indexes", musicFolderContent.getIndexedArtists().keySet());
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean saveSelectedMusicFolder(HttpServletRequest request) {
|
||||
if (request.getParameter("musicFolderId") == null) {
|
||||
return false;
|
||||
}
|
||||
int musicFolderId = Integer.parseInt(request.getParameter("musicFolderId"));
|
||||
|
||||
// Note: UserSettings.setChanged() is intentionally not called. This would break browser caching
|
||||
// of the left frame.
|
||||
UserSettings settings = settingsService.getUserSettings(securityService.getCurrentUsername(request));
|
||||
settings.setSelectedMusicFolderId(musicFolderId);
|
||||
settingsService.updateUserSettings(settings);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setMediaScannerService(MediaScannerService mediaScannerService) {
|
||||
this.mediaScannerService = mediaScannerService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMusicIndexService(MusicIndexService musicIndexService) {
|
||||
this.musicIndexService = musicIndexService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
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 org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Controller for the lyrics popup.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class LyricsController extends ParameterizableViewController {
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
map.put("artist", request.getParameter("artist"));
|
||||
map.put("song", request.getParameter("song"));
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
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.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.PlayQueue;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.TranscodingService;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller which produces the M3U playlist.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class M3UController implements Controller {
|
||||
|
||||
private PlayerService playerService;
|
||||
private SettingsService settingsService;
|
||||
private TranscodingService transcodingService;
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
response.setContentType("audio/x-mpegurl");
|
||||
response.setCharacterEncoding(StringUtil.ENCODING_UTF8);
|
||||
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
|
||||
String url = request.getRequestURL().toString();
|
||||
url = url.replaceFirst("play.m3u.*", "stream?");
|
||||
|
||||
// Rewrite URLs in case we're behind a proxy.
|
||||
if (settingsService.isRewriteUrlEnabled()) {
|
||||
String referer = request.getHeader("referer");
|
||||
url = StringUtil.rewriteUrl(url, referer);
|
||||
}
|
||||
|
||||
url = settingsService.rewriteRemoteUrl(url);
|
||||
|
||||
if (player.isExternalWithPlaylist()) {
|
||||
createClientSidePlaylist(response.getWriter(), player, url);
|
||||
} else {
|
||||
createServerSidePlaylist(response.getWriter(), player, url);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void createClientSidePlaylist(PrintWriter out, Player player, String url) throws Exception {
|
||||
out.println("#EXTM3U");
|
||||
List<MediaFile> result;
|
||||
synchronized (player.getPlayQueue()) {
|
||||
result = player.getPlayQueue().getFiles();
|
||||
}
|
||||
for (MediaFile mediaFile : result) {
|
||||
Integer duration = mediaFile.getDurationSeconds();
|
||||
if (duration == null) {
|
||||
duration = -1;
|
||||
}
|
||||
out.println("#EXTINF:" + duration + "," + mediaFile.getArtist() + " - " + mediaFile.getTitle());
|
||||
out.println(url + "player=" + player.getId() + "&id=" + mediaFile.getId() + "&suffix=." + transcodingService.getSuffix(player, mediaFile, null));
|
||||
}
|
||||
}
|
||||
|
||||
private void createServerSidePlaylist(PrintWriter out, Player player, String url) throws IOException {
|
||||
|
||||
url += "player=" + player.getId();
|
||||
|
||||
// Get suffix of current file, e.g., ".mp3".
|
||||
String suffix = getSuffix(player);
|
||||
if (suffix != null) {
|
||||
url += "&suffix=." + suffix;
|
||||
}
|
||||
|
||||
out.println("#EXTM3U");
|
||||
out.println("#EXTINF:-1,Subsonic");
|
||||
out.println(url);
|
||||
}
|
||||
|
||||
private String getSuffix(Player player) {
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
return playQueue.isEmpty() ? null : transcodingService.getSuffix(player, playQueue.getFile(0), null);
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setTranscodingService(TranscodingService transcodingService) {
|
||||
this.transcodingService = transcodingService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
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.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import net.sourceforge.subsonic.domain.CoverArtScheme;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.MediaFileComparator;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.AdService;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.RatingService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the main page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MainController extends AbstractController {
|
||||
|
||||
private SecurityService securityService;
|
||||
private PlayerService playerService;
|
||||
private SettingsService settingsService;
|
||||
private RatingService ratingService;
|
||||
private MediaFileService mediaFileService;
|
||||
private AdService adService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
List<MediaFile> mediaFiles = getMediaFiles(request);
|
||||
|
||||
if (mediaFiles.isEmpty()) {
|
||||
return new ModelAndView(new RedirectView("notFound.view"));
|
||||
}
|
||||
|
||||
MediaFile dir = mediaFiles.get(0);
|
||||
if (dir.isFile()) {
|
||||
dir = mediaFileService.getParentOf(dir);
|
||||
}
|
||||
|
||||
// Redirect if root directory.
|
||||
if (mediaFileService.isRoot(dir)) {
|
||||
return new ModelAndView(new RedirectView("home.view?"));
|
||||
}
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
if (!securityService.isFolderAccessAllowed(dir, username)) {
|
||||
return new ModelAndView(new RedirectView("accessDenied.view"));
|
||||
}
|
||||
|
||||
List<MediaFile> children = mediaFiles.size() == 1 ? mediaFileService.getChildrenOf(dir, true, true, true) : getMultiFolderChildren(mediaFiles);
|
||||
List<MediaFile> files = new ArrayList<MediaFile>();
|
||||
List<MediaFile> subDirs = new ArrayList<MediaFile>();
|
||||
for (MediaFile child : children) {
|
||||
if (child.isFile()) {
|
||||
files.add(child);
|
||||
} else {
|
||||
subDirs.add(child);
|
||||
}
|
||||
}
|
||||
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
|
||||
mediaFileService.populateStarredDate(dir, username);
|
||||
mediaFileService.populateStarredDate(children, username);
|
||||
|
||||
map.put("dir", dir);
|
||||
map.put("files", files);
|
||||
map.put("subDirs", subDirs);
|
||||
map.put("ancestors", getAncestors(dir));
|
||||
map.put("coverArtSizeMedium", CoverArtScheme.MEDIUM.getSize());
|
||||
map.put("coverArtSizeLarge", CoverArtScheme.LARGE.getSize());
|
||||
map.put("player", player);
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("visibility", userSettings.getMainVisibility());
|
||||
map.put("showAlbumYear", settingsService.isSortAlbumsByYear());
|
||||
map.put("showArtistInfo", userSettings.isShowArtistInfoEnabled());
|
||||
map.put("partyMode", userSettings.isPartyModeEnabled());
|
||||
map.put("brand", settingsService.getBrand());
|
||||
map.put("showAd", !settingsService.isLicenseValid() && adService.showAd());
|
||||
map.put("viewAsList", isViewAsList(request, userSettings));
|
||||
if (dir.isAlbum()) {
|
||||
map.put("sieblingAlbums", getSieblingAlbums(dir));
|
||||
map.put("artist", guessArtist(children));
|
||||
map.put("album", guessAlbum(children));
|
||||
}
|
||||
|
||||
try {
|
||||
MediaFile parent = mediaFileService.getParentOf(dir);
|
||||
map.put("parent", parent);
|
||||
map.put("navigateUpAllowed", !mediaFileService.isRoot(parent));
|
||||
} catch (SecurityException x) {
|
||||
// Happens if Podcast directory is outside music folder.
|
||||
}
|
||||
|
||||
Integer userRating = ratingService.getRatingForUser(username, dir);
|
||||
Double averageRating = ratingService.getAverageRating(dir);
|
||||
|
||||
if (userRating == null) {
|
||||
userRating = 0;
|
||||
}
|
||||
|
||||
if (averageRating == null) {
|
||||
averageRating = 0.0D;
|
||||
}
|
||||
|
||||
map.put("userRating", 10 * userRating);
|
||||
map.put("averageRating", Math.round(10.0D * averageRating));
|
||||
map.put("starred", mediaFileService.getMediaFileStarredDate(dir.getId(), username) != null);
|
||||
|
||||
String view;
|
||||
if (isVideoOnly(children)) {
|
||||
view = "videoMain";
|
||||
} else if (dir.isAlbum()) {
|
||||
view = "albumMain";
|
||||
} else {
|
||||
view = "artistMain";
|
||||
}
|
||||
|
||||
ModelAndView result = new ModelAndView(view);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isViewAsList(HttpServletRequest request, UserSettings userSettings) {
|
||||
boolean viewAsList = ServletRequestUtils.getBooleanParameter(request, "viewAsList", userSettings.isViewAsList());
|
||||
if (viewAsList != userSettings.isViewAsList()) {
|
||||
userSettings.setViewAsList(viewAsList);
|
||||
userSettings.setChanged(new Date());
|
||||
settingsService.updateUserSettings(userSettings);
|
||||
}
|
||||
return viewAsList;
|
||||
}
|
||||
|
||||
private boolean isVideoOnly(List<MediaFile> children) {
|
||||
boolean videoFound = false;
|
||||
for (MediaFile child : children) {
|
||||
if (child.isAudio()) {
|
||||
return false;
|
||||
}
|
||||
if (child.isVideo()) {
|
||||
videoFound = true;
|
||||
}
|
||||
}
|
||||
return videoFound;
|
||||
}
|
||||
|
||||
private List<MediaFile> getMediaFiles(HttpServletRequest request) {
|
||||
List<MediaFile> mediaFiles = new ArrayList<MediaFile>();
|
||||
for (String path : ServletRequestUtils.getStringParameters(request, "path")) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(path);
|
||||
if (mediaFile != null) {
|
||||
mediaFiles.add(mediaFile);
|
||||
}
|
||||
}
|
||||
for (int id : ServletRequestUtils.getIntParameters(request, "id")) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(id);
|
||||
if (mediaFile != null) {
|
||||
mediaFiles.add(mediaFile);
|
||||
}
|
||||
}
|
||||
return mediaFiles;
|
||||
}
|
||||
|
||||
private String guessArtist(List<MediaFile> children) {
|
||||
for (MediaFile child : children) {
|
||||
if (child.isFile() && child.getArtist() != null) {
|
||||
return child.getArtist();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String guessAlbum(List<MediaFile> children) {
|
||||
for (MediaFile child : children) {
|
||||
if (child.isFile() && child.getArtist() != null) {
|
||||
return child.getAlbumName();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<MediaFile> getMultiFolderChildren(List<MediaFile> mediaFiles) throws IOException {
|
||||
SortedSet<MediaFile> result = new TreeSet<MediaFile>(new MediaFileComparator(settingsService.isSortAlbumsByYear()));
|
||||
for (MediaFile mediaFile : mediaFiles) {
|
||||
if (mediaFile.isFile()) {
|
||||
mediaFile = mediaFileService.getParentOf(mediaFile);
|
||||
}
|
||||
result.addAll(mediaFileService.getChildrenOf(mediaFile, true, true, true));
|
||||
}
|
||||
return new ArrayList<MediaFile>(result);
|
||||
}
|
||||
|
||||
private List<MediaFile> getAncestors(MediaFile dir) throws IOException {
|
||||
LinkedList<MediaFile> result = new LinkedList<MediaFile>();
|
||||
|
||||
try {
|
||||
MediaFile parent = mediaFileService.getParentOf(dir);
|
||||
while (parent != null && !mediaFileService.isRoot(parent)) {
|
||||
result.addFirst(parent);
|
||||
parent = mediaFileService.getParentOf(parent);
|
||||
}
|
||||
} catch (SecurityException x) {
|
||||
// Happens if Podcast directory is outside music folder.
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<MediaFile> getSieblingAlbums(MediaFile dir) {
|
||||
List<MediaFile> result = new ArrayList<MediaFile>();
|
||||
|
||||
MediaFile parent = mediaFileService.getParentOf(dir);
|
||||
if (!mediaFileService.isRoot(parent)) {
|
||||
List<MediaFile> sieblings = mediaFileService.getChildrenOf(parent, false, true, true);
|
||||
for (MediaFile siebling : sieblings) {
|
||||
if (siebling.isAlbum() && !siebling.equals(dir)) {
|
||||
result.add(siebling);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setRatingService(RatingService ratingService) {
|
||||
this.ratingService = ratingService;
|
||||
}
|
||||
|
||||
public void setAdService(AdService adService) {
|
||||
this.adService = adService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
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.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller for the "more" page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MoreController extends ParameterizableViewController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
private PlayerService playerService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
|
||||
String uploadDirectory = null;
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(user.getUsername());
|
||||
if (musicFolders.size() > 0) {
|
||||
uploadDirectory = new File(musicFolders.get(0).getPath(), "Incoming").getPath();
|
||||
}
|
||||
|
||||
|
||||
StringBuilder jamstashUrl = new StringBuilder("http://jamstash.com/#/settings?u=" + StringUtil.urlEncode(user.getUsername()) + "&url=");
|
||||
if (settingsService.isUrlRedirectionEnabled()) {
|
||||
jamstashUrl.append(StringUtil.urlEncode(settingsService.getUrlRedirectUrl()));
|
||||
} else {
|
||||
jamstashUrl.append(StringUtil.urlEncode(request.getRequestURL().toString().replaceAll("/more.view.*", "")));
|
||||
}
|
||||
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
map.put("user", user);
|
||||
map.put("uploadDirectory", uploadDirectory);
|
||||
map.put("genres", mediaFileService.getGenres(false));
|
||||
map.put("currentYear", Calendar.getInstance().get(Calendar.YEAR));
|
||||
map.put("musicFolders", musicFolders);
|
||||
map.put("clientSidePlaylist", player.isExternalWithPlaylist() || player.isWeb());
|
||||
map.put("brand", settingsService.getBrand());
|
||||
map.put("jamstashUrl", jamstashUrl);
|
||||
return result;
|
||||
}
|
||||
|
||||
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 setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
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.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.domain.Playlist;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.PlaylistService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
import net.tanesha.recaptcha.ReCaptcha;
|
||||
import net.tanesha.recaptcha.ReCaptchaFactory;
|
||||
import net.tanesha.recaptcha.ReCaptchaResponse;
|
||||
|
||||
/**
|
||||
* Multi-controller used for simple pages.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MultiController extends MultiActionController {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MultiController.class);
|
||||
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
private PlaylistService playlistService;
|
||||
|
||||
public ModelAndView login(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
// Auto-login if "user" and "password" parameters are given.
|
||||
String username = request.getParameter("user");
|
||||
String password = request.getParameter("password");
|
||||
if (username != null && password != null) {
|
||||
username = StringUtil.urlEncode(username);
|
||||
password = StringUtil.urlEncode(password);
|
||||
return new ModelAndView(new RedirectView("j_acegi_security_check?j_username=" + username +
|
||||
"&j_password=" + password + "&_acegi_security_remember_me=checked"));
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("logout", request.getParameter("logout") != null);
|
||||
map.put("error", request.getParameter("error") != null);
|
||||
map.put("brand", settingsService.getBrand());
|
||||
map.put("loginMessage", settingsService.getLoginMessage());
|
||||
|
||||
User admin = securityService.getUserByName(User.USERNAME_ADMIN);
|
||||
if (User.USERNAME_ADMIN.equals(admin.getPassword())) {
|
||||
map.put("insecure", true);
|
||||
}
|
||||
|
||||
return new ModelAndView("login", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView recover(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
String usernameOrEmail = StringUtils.trimToNull(request.getParameter("usernameOrEmail"));
|
||||
ReCaptcha captcha = ReCaptchaFactory.newSecureReCaptcha("6LcZ3OMSAAAAANkKMdFdaNopWu9iS03V-nLOuoiH",
|
||||
"6LcZ3OMSAAAAAPaFg89mEzs-Ft0fIu7wxfKtkwmQ", false);
|
||||
boolean showCaptcha = true;
|
||||
|
||||
if (usernameOrEmail != null) {
|
||||
|
||||
map.put("usernameOrEmail", usernameOrEmail);
|
||||
User user = getUserByUsernameOrEmail(usernameOrEmail);
|
||||
String challenge = request.getParameter("recaptcha_challenge_field");
|
||||
String uresponse = request.getParameter("recaptcha_response_field");
|
||||
ReCaptchaResponse captchaResponse = captcha.checkAnswer(request.getRemoteAddr(), challenge, uresponse);
|
||||
|
||||
if (!captchaResponse.isValid()) {
|
||||
map.put("error", "recover.error.invalidcaptcha");
|
||||
} else if (user == null) {
|
||||
map.put("error", "recover.error.usernotfound");
|
||||
} else if (user.getEmail() == null) {
|
||||
map.put("error", "recover.error.noemail");
|
||||
} else {
|
||||
String password = RandomStringUtils.randomAlphanumeric(8);
|
||||
if (emailPassword(password, user.getUsername(), user.getEmail())) {
|
||||
map.put("sentTo", user.getEmail());
|
||||
user.setLdapAuthenticated(false);
|
||||
user.setPassword(password);
|
||||
securityService.updateUser(user);
|
||||
showCaptcha = false;
|
||||
} else {
|
||||
map.put("error", "recover.error.sendfailed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showCaptcha) {
|
||||
map.put("captcha", captcha.createRecaptchaHtml(null, null));
|
||||
}
|
||||
|
||||
return new ModelAndView("recover", "model", map);
|
||||
}
|
||||
|
||||
private boolean emailPassword(String password, String username, String email) {
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
try {
|
||||
HttpConnectionParams.setConnectionTimeout(client.getParams(), 10000);
|
||||
HttpConnectionParams.setSoTimeout(client.getParams(), 10000);
|
||||
HttpPost method = new HttpPost("http://subsonic.org/backend/sendMail.view");
|
||||
|
||||
List<NameValuePair> params = new ArrayList<NameValuePair>();
|
||||
params.add(new BasicNameValuePair("from", "noreply@subsonic.org"));
|
||||
params.add(new BasicNameValuePair("to", email));
|
||||
params.add(new BasicNameValuePair("subject", "Subsonic Password"));
|
||||
params.add(new BasicNameValuePair("text",
|
||||
"Hi there!\n\n" +
|
||||
"You have requested to reset your Subsonic password. Please find your new login details below.\n\n" +
|
||||
"Username: " + username + "\n" +
|
||||
"Password: " + password + "\n\n" +
|
||||
"--\n" +
|
||||
"The Subsonic Team\n" +
|
||||
"subsonic.org"));
|
||||
method.setEntity(new UrlEncodedFormEntity(params, StringUtil.ENCODING_UTF8));
|
||||
client.execute(method);
|
||||
return true;
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to send email.", x);
|
||||
return false;
|
||||
} finally {
|
||||
client.getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private User getUserByUsernameOrEmail(String usernameOrEmail) {
|
||||
if (usernameOrEmail != null) {
|
||||
User user = securityService.getUserByName(usernameOrEmail);
|
||||
if (user != null) {
|
||||
return user;
|
||||
}
|
||||
return securityService.getUserByEmail(usernameOrEmail);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ModelAndView accessDenied(HttpServletRequest request, HttpServletResponse response) {
|
||||
return new ModelAndView("accessDenied");
|
||||
}
|
||||
|
||||
public ModelAndView notFound(HttpServletRequest request, HttpServletResponse response) {
|
||||
return new ModelAndView("notFound");
|
||||
}
|
||||
|
||||
public ModelAndView gettingStarted(HttpServletRequest request, HttpServletResponse response) {
|
||||
updatePortAndContextPath(request);
|
||||
|
||||
if (request.getParameter("hide") != null) {
|
||||
settingsService.setGettingStartedEnabled(false);
|
||||
settingsService.save();
|
||||
return new ModelAndView(new RedirectView("home.view"));
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("runningAsRoot", "root".equals(System.getProperty("user.name")));
|
||||
return new ModelAndView("gettingStarted", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView index(HttpServletRequest request, HttpServletResponse response) {
|
||||
updatePortAndContextPath(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(securityService.getCurrentUsername(request));
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("showRight", userSettings.isShowNowPlayingEnabled() || userSettings.isShowChatEnabled());
|
||||
map.put("autoHidePlayQueue", userSettings.isAutoHidePlayQueue());
|
||||
map.put("showSideBar", userSettings.isShowSideBar());
|
||||
map.put("brand", settingsService.getBrand());
|
||||
return new ModelAndView("index", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView exportPlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
Playlist playlist = playlistService.getPlaylist(id);
|
||||
if (!playlistService.isReadAllowed(playlist, securityService.getCurrentUsername(request))) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return null;
|
||||
|
||||
}
|
||||
response.setContentType("application/x-download");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + StringUtil.fileSystemSafe(playlist.getName()) + ".m3u8\"");
|
||||
|
||||
playlistService.exportPlaylist(id, response.getOutputStream());
|
||||
return null;
|
||||
}
|
||||
|
||||
private void updatePortAndContextPath(HttpServletRequest request) {
|
||||
|
||||
int port = Integer.parseInt(System.getProperty("subsonic.port", String.valueOf(request.getLocalPort())));
|
||||
int httpsPort = Integer.parseInt(System.getProperty("subsonic.httpsPort", "0"));
|
||||
|
||||
String contextPath = request.getContextPath().replace("/", "");
|
||||
|
||||
if (settingsService.getPort() != port) {
|
||||
settingsService.setPort(port);
|
||||
settingsService.save();
|
||||
}
|
||||
if (settingsService.getHttpsPort() != httpsPort) {
|
||||
settingsService.setHttpsPort(httpsPort);
|
||||
settingsService.save();
|
||||
}
|
||||
if (!ObjectUtils.equals(settingsService.getUrlRedirectContextPath(), contextPath)) {
|
||||
settingsService.setUrlRedirectContextPath(contextPath);
|
||||
settingsService.save();
|
||||
}
|
||||
}
|
||||
|
||||
public ModelAndView test(HttpServletRequest request, HttpServletResponse response) {
|
||||
return new ModelAndView("test");
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.command.MusicFolderSettingsCommand;
|
||||
import net.sourceforge.subsonic.dao.AlbumDao;
|
||||
import net.sourceforge.subsonic.dao.ArtistDao;
|
||||
import net.sourceforge.subsonic.dao.MediaFileDao;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.service.MediaScannerService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate the set of music folders.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MusicFolderSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private MediaScannerService mediaScannerService;
|
||||
private ArtistDao artistDao;
|
||||
private AlbumDao albumDao;
|
||||
private MediaFileDao mediaFileDao;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
MusicFolderSettingsCommand command = new MusicFolderSettingsCommand();
|
||||
|
||||
if (request.getParameter("scanNow") != null) {
|
||||
settingsService.clearMusicFolderCache();
|
||||
mediaScannerService.scanLibrary();
|
||||
}
|
||||
if (request.getParameter("expunge") != null) {
|
||||
expunge();
|
||||
}
|
||||
|
||||
command.setInterval(String.valueOf(settingsService.getIndexCreationInterval()));
|
||||
command.setHour(String.valueOf(settingsService.getIndexCreationHour()));
|
||||
command.setFastCache(settingsService.isFastCacheEnabled());
|
||||
command.setOrganizeByFolderStructure(settingsService.isOrganizeByFolderStructure());
|
||||
command.setScanning(mediaScannerService.isScanning());
|
||||
command.setMusicFolders(wrap(settingsService.getAllMusicFolders(true, true)));
|
||||
command.setNewMusicFolder(new MusicFolderSettingsCommand.MusicFolderInfo());
|
||||
command.setReload(request.getParameter("reload") != null || request.getParameter("scanNow") != null);
|
||||
return command;
|
||||
}
|
||||
|
||||
private void expunge() {
|
||||
artistDao.expunge();
|
||||
albumDao.expunge();
|
||||
mediaFileDao.expunge();
|
||||
}
|
||||
|
||||
private List<MusicFolderSettingsCommand.MusicFolderInfo> wrap(List<MusicFolder> musicFolders) {
|
||||
ArrayList<MusicFolderSettingsCommand.MusicFolderInfo> result = new ArrayList<MusicFolderSettingsCommand.MusicFolderInfo>();
|
||||
for (MusicFolder musicFolder : musicFolders) {
|
||||
result.add(new MusicFolderSettingsCommand.MusicFolderInfo(musicFolder));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView onSubmit(Object comm) throws Exception {
|
||||
MusicFolderSettingsCommand command = (MusicFolderSettingsCommand) comm;
|
||||
|
||||
for (MusicFolderSettingsCommand.MusicFolderInfo musicFolderInfo : command.getMusicFolders()) {
|
||||
if (musicFolderInfo.isDelete()) {
|
||||
settingsService.deleteMusicFolder(musicFolderInfo.getId());
|
||||
} else {
|
||||
MusicFolder musicFolder = musicFolderInfo.toMusicFolder();
|
||||
if (musicFolder != null) {
|
||||
settingsService.updateMusicFolder(musicFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MusicFolder newMusicFolder = command.getNewMusicFolder().toMusicFolder();
|
||||
if (newMusicFolder != null) {
|
||||
settingsService.createMusicFolder(newMusicFolder);
|
||||
}
|
||||
|
||||
settingsService.setIndexCreationInterval(Integer.parseInt(command.getInterval()));
|
||||
settingsService.setIndexCreationHour(Integer.parseInt(command.getHour()));
|
||||
settingsService.setFastCacheEnabled(command.isFastCache());
|
||||
settingsService.setOrganizeByFolderStructure(command.isOrganizeByFolderStructure());
|
||||
settingsService.save();
|
||||
|
||||
mediaScannerService.schedule();
|
||||
return new ModelAndView(new RedirectView(getSuccessView() + ".view?reload"));
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setMediaScannerService(MediaScannerService mediaScannerService) {
|
||||
this.mediaScannerService = mediaScannerService;
|
||||
}
|
||||
|
||||
public void setArtistDao(ArtistDao artistDao) {
|
||||
this.artistDao = artistDao;
|
||||
}
|
||||
|
||||
public void setAlbumDao(AlbumDao albumDao) {
|
||||
this.albumDao = albumDao;
|
||||
}
|
||||
|
||||
public void setMediaFileDao(MediaFileDao mediaFileDao) {
|
||||
this.mediaFileDao = mediaFileDao;
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
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.util.Random;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import net.sourceforge.subsonic.command.NetworkSettingsCommand;
|
||||
import net.sourceforge.subsonic.domain.UrlRedirectType;
|
||||
import net.sourceforge.subsonic.service.NetworkService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to change the network settings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class NetworkSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private NetworkService networkService;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
NetworkSettingsCommand command = new NetworkSettingsCommand();
|
||||
command.setPortForwardingEnabled(settingsService.isPortForwardingEnabled());
|
||||
command.setUrlRedirectionEnabled(settingsService.isUrlRedirectionEnabled());
|
||||
command.setUrlRedirectType(settingsService.getUrlRedirectType().name());
|
||||
command.setUrlRedirectFrom(settingsService.getUrlRedirectFrom());
|
||||
command.setUrlRedirectCustomUrl(settingsService.getUrlRedirectCustomUrl());
|
||||
command.setPort(settingsService.getPort());
|
||||
command.setLicenseInfo(settingsService.getLicenseInfo());
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
protected void doSubmitAction(Object cmd) throws Exception {
|
||||
NetworkSettingsCommand command = (NetworkSettingsCommand) cmd;
|
||||
command.setToast(true);
|
||||
|
||||
settingsService.setPortForwardingEnabled(command.isPortForwardingEnabled());
|
||||
settingsService.setUrlRedirectionEnabled(command.isUrlRedirectionEnabled());
|
||||
settingsService.setUrlRedirectType(UrlRedirectType.valueOf(command.getUrlRedirectType()));
|
||||
settingsService.setUrlRedirectFrom(StringUtils.lowerCase(command.getUrlRedirectFrom()));
|
||||
settingsService.setUrlRedirectCustomUrl(StringUtils.trimToEmpty(command.getUrlRedirectCustomUrl()));
|
||||
|
||||
if (settingsService.getServerId() == null) {
|
||||
Random rand = new Random(System.currentTimeMillis());
|
||||
settingsService.setServerId(String.valueOf(Math.abs(rand.nextLong())));
|
||||
}
|
||||
|
||||
settingsService.save();
|
||||
networkService.initPortForwarding(0);
|
||||
networkService.initUrlRedirection(true);
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setNetworkService(NetworkService networkService) {
|
||||
this.networkService = networkService;
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.TransferStatus;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.StatusService;
|
||||
|
||||
/**
|
||||
* Controller for showing what's currently playing.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class NowPlayingController extends AbstractController {
|
||||
|
||||
private PlayerService playerService;
|
||||
private StatusService statusService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
List<TransferStatus> statuses = statusService.getStreamStatusesForPlayer(player);
|
||||
|
||||
MediaFile current = statuses.isEmpty() ? null : mediaFileService.getMediaFile(statuses.get(0).getFile());
|
||||
MediaFile dir = current == null ? null : mediaFileService.getParentOf(current);
|
||||
|
||||
String url;
|
||||
if (dir != null && !mediaFileService.isRoot(dir)) {
|
||||
url = "main.view?id=" + dir.getId();
|
||||
} else {
|
||||
url = "home.view";
|
||||
}
|
||||
|
||||
return new ModelAndView(new RedirectView(url));
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setStatusService(StatusService statusService) {
|
||||
this.statusService = statusService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
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 org.springframework.web.servlet.mvc.*;
|
||||
import net.sourceforge.subsonic.service.*;
|
||||
import net.sourceforge.subsonic.command.*;
|
||||
import net.sourceforge.subsonic.domain.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/**
|
||||
* Controller for the page used to change password.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PasswordSettingsController extends SimpleFormController {
|
||||
|
||||
private SecurityService securityService;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
PasswordSettingsCommand command = new PasswordSettingsCommand();
|
||||
User user = securityService.getCurrentUser(request);
|
||||
command.setUsername(user.getUsername());
|
||||
command.setLdapAuthenticated(user.isLdapAuthenticated());
|
||||
return command;
|
||||
}
|
||||
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
PasswordSettingsCommand command = (PasswordSettingsCommand) comm;
|
||||
User user = securityService.getUserByName(command.getUsername());
|
||||
user.setPassword(command.getPassword());
|
||||
securityService.updateUser(user);
|
||||
|
||||
command.setPassword(null);
|
||||
command.setConfirmPassword(null);
|
||||
command.setToast(true);
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
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.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import net.sourceforge.subsonic.command.PersonalSettingsCommand;
|
||||
import net.sourceforge.subsonic.domain.AlbumListType;
|
||||
import net.sourceforge.subsonic.domain.AvatarScheme;
|
||||
import net.sourceforge.subsonic.domain.Theme;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate per-user settings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PersonalSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
|
||||
@Override
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
PersonalSettingsCommand command = new PersonalSettingsCommand();
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(user.getUsername());
|
||||
|
||||
command.setUser(user);
|
||||
command.setLocaleIndex("-1");
|
||||
command.setThemeIndex("-1");
|
||||
command.setAlbumLists(AlbumListType.values());
|
||||
command.setAlbumListId(userSettings.getDefaultAlbumList().getId());
|
||||
command.setAvatars(settingsService.getAllSystemAvatars());
|
||||
command.setCustomAvatar(settingsService.getCustomAvatar(user.getUsername()));
|
||||
command.setAvatarId(getAvatarId(userSettings));
|
||||
command.setPartyModeEnabled(userSettings.isPartyModeEnabled());
|
||||
command.setQueueFollowingSongs(userSettings.isQueueFollowingSongs());
|
||||
command.setShowNowPlayingEnabled(userSettings.isShowNowPlayingEnabled());
|
||||
command.setShowChatEnabled(userSettings.isShowChatEnabled());
|
||||
command.setShowArtistInfoEnabled(userSettings.isShowArtistInfoEnabled());
|
||||
command.setNowPlayingAllowed(userSettings.isNowPlayingAllowed());
|
||||
command.setMainVisibility(userSettings.getMainVisibility());
|
||||
command.setPlaylistVisibility(userSettings.getPlaylistVisibility());
|
||||
command.setFinalVersionNotificationEnabled(userSettings.isFinalVersionNotificationEnabled());
|
||||
command.setBetaVersionNotificationEnabled(userSettings.isBetaVersionNotificationEnabled());
|
||||
command.setSongNotificationEnabled(userSettings.isSongNotificationEnabled());
|
||||
command.setAutoHidePlayQueue(userSettings.isAutoHidePlayQueue());
|
||||
command.setLastFmEnabled(userSettings.isLastFmEnabled());
|
||||
command.setLastFmUsername(userSettings.getLastFmUsername());
|
||||
command.setLastFmPassword(userSettings.getLastFmPassword());
|
||||
|
||||
Locale currentLocale = userSettings.getLocale();
|
||||
Locale[] locales = settingsService.getAvailableLocales();
|
||||
String[] localeStrings = new String[locales.length];
|
||||
for (int i = 0; i < locales.length; i++) {
|
||||
localeStrings[i] = locales[i].getDisplayName(locales[i]);
|
||||
if (locales[i].equals(currentLocale)) {
|
||||
command.setLocaleIndex(String.valueOf(i));
|
||||
}
|
||||
}
|
||||
command.setLocales(localeStrings);
|
||||
|
||||
String currentThemeId = userSettings.getThemeId();
|
||||
Theme[] themes = settingsService.getAvailableThemes();
|
||||
command.setThemes(themes);
|
||||
for (int i = 0; i < themes.length; i++) {
|
||||
if (themes[i].getId().equals(currentThemeId)) {
|
||||
command.setThemeIndex(String.valueOf(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
PersonalSettingsCommand command = (PersonalSettingsCommand) comm;
|
||||
|
||||
int localeIndex = Integer.parseInt(command.getLocaleIndex());
|
||||
Locale locale = null;
|
||||
if (localeIndex != -1) {
|
||||
locale = settingsService.getAvailableLocales()[localeIndex];
|
||||
}
|
||||
|
||||
int themeIndex = Integer.parseInt(command.getThemeIndex());
|
||||
String themeId = null;
|
||||
if (themeIndex != -1) {
|
||||
themeId = settingsService.getAvailableThemes()[themeIndex].getId();
|
||||
}
|
||||
|
||||
String username = command.getUser().getUsername();
|
||||
UserSettings settings = settingsService.getUserSettings(username);
|
||||
|
||||
settings.setLocale(locale);
|
||||
settings.setThemeId(themeId);
|
||||
settings.setDefaultAlbumList(AlbumListType.fromId(command.getAlbumListId()));
|
||||
settings.setPartyModeEnabled(command.isPartyModeEnabled());
|
||||
settings.setQueueFollowingSongs(command.isQueueFollowingSongs());
|
||||
settings.setShowNowPlayingEnabled(command.isShowNowPlayingEnabled());
|
||||
settings.setShowChatEnabled(command.isShowChatEnabled());
|
||||
settings.setShowArtistInfoEnabled(command.isShowArtistInfoEnabled());
|
||||
settings.setNowPlayingAllowed(command.isNowPlayingAllowed());
|
||||
settings.setMainVisibility(command.getMainVisibility());
|
||||
settings.setPlaylistVisibility(command.getPlaylistVisibility());
|
||||
settings.setFinalVersionNotificationEnabled(command.isFinalVersionNotificationEnabled());
|
||||
settings.setBetaVersionNotificationEnabled(command.isBetaVersionNotificationEnabled());
|
||||
settings.setSongNotificationEnabled(command.isSongNotificationEnabled());
|
||||
settings.setAutoHidePlayQueue(command.isAutoHidePlayQueue());
|
||||
settings.setLastFmEnabled(command.isLastFmEnabled());
|
||||
settings.setLastFmUsername(command.getLastFmUsername());
|
||||
settings.setSystemAvatarId(getSystemAvatarId(command));
|
||||
settings.setAvatarScheme(getAvatarScheme(command));
|
||||
|
||||
if (StringUtils.isNotBlank(command.getLastFmPassword())) {
|
||||
settings.setLastFmPassword(command.getLastFmPassword());
|
||||
}
|
||||
|
||||
settings.setChanged(new Date());
|
||||
settingsService.updateUserSettings(settings);
|
||||
|
||||
command.setReloadNeeded(true);
|
||||
}
|
||||
|
||||
private int getAvatarId(UserSettings userSettings) {
|
||||
AvatarScheme avatarScheme = userSettings.getAvatarScheme();
|
||||
return avatarScheme == AvatarScheme.SYSTEM ? userSettings.getSystemAvatarId() : avatarScheme.getCode();
|
||||
}
|
||||
|
||||
private AvatarScheme getAvatarScheme(PersonalSettingsCommand command) {
|
||||
if (command.getAvatarId() == AvatarScheme.NONE.getCode()) {
|
||||
return AvatarScheme.NONE;
|
||||
}
|
||||
if (command.getAvatarId() == AvatarScheme.CUSTOM.getCode()) {
|
||||
return AvatarScheme.CUSTOM;
|
||||
}
|
||||
return AvatarScheme.SYSTEM;
|
||||
}
|
||||
|
||||
private Integer getSystemAvatarId(PersonalSettingsCommand command) {
|
||||
int avatarId = command.getAvatarId();
|
||||
if (avatarId == AvatarScheme.NONE.getCode() ||
|
||||
avatarId == AvatarScheme.CUSTOM.getCode()) {
|
||||
return null;
|
||||
}
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
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.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the playlist frame.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlayQueueController extends ParameterizableViewController {
|
||||
|
||||
private PlayerService playerService;
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(user.getUsername());
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("user", user);
|
||||
map.put("player", player);
|
||||
map.put("players", playerService.getPlayersForUserAndClientId(user.getUsername(), null));
|
||||
map.put("visibility", userSettings.getPlaylistVisibility());
|
||||
map.put("partyMode", userSettings.isPartyModeEnabled());
|
||||
map.put("notify", userSettings.isSongNotificationEnabled());
|
||||
map.put("autoHide", userSettings.isAutoHidePlayQueue());
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import net.sourceforge.subsonic.command.PlayerSettingsCommand;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.PlayerTechnology;
|
||||
import net.sourceforge.subsonic.domain.TranscodeScheme;
|
||||
import net.sourceforge.subsonic.domain.Transcoding;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.TranscodingService;
|
||||
|
||||
/**
|
||||
* Controller for the player settings page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlayerSettingsController extends SimpleFormController {
|
||||
|
||||
private PlayerService playerService;
|
||||
private SecurityService securityService;
|
||||
private TranscodingService transcodingService;
|
||||
|
||||
@Override
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
|
||||
handleRequestParameters(request);
|
||||
List<Player> players = getPlayers(request);
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
PlayerSettingsCommand command = new PlayerSettingsCommand();
|
||||
Player player = null;
|
||||
String playerId = request.getParameter("id");
|
||||
if (playerId != null) {
|
||||
player = playerService.getPlayerById(playerId);
|
||||
} else if (!players.isEmpty()) {
|
||||
player = players.get(0);
|
||||
}
|
||||
|
||||
if (player != null) {
|
||||
command.setPlayerId(player.getId());
|
||||
command.setName(player.getName());
|
||||
command.setDescription(player.toString());
|
||||
command.setType(player.getType());
|
||||
command.setLastSeen(player.getLastSeen());
|
||||
command.setDynamicIp(player.isDynamicIp());
|
||||
command.setAutoControlEnabled(player.isAutoControlEnabled());
|
||||
command.setTranscodeSchemeName(player.getTranscodeScheme().name());
|
||||
command.setTechnologyName(player.getTechnology().name());
|
||||
command.setAllTranscodings(transcodingService.getAllTranscodings());
|
||||
List<Transcoding> activeTranscodings = transcodingService.getTranscodingsForPlayer(player);
|
||||
int[] activeTranscodingIds = new int[activeTranscodings.size()];
|
||||
for (int i = 0; i < activeTranscodings.size(); i++) {
|
||||
activeTranscodingIds[i] = activeTranscodings.get(i).getId();
|
||||
}
|
||||
command.setActiveTranscodingIds(activeTranscodingIds);
|
||||
}
|
||||
|
||||
command.setTranscodingSupported(transcodingService.isDownsamplingSupported(null));
|
||||
command.setTranscodeDirectory(transcodingService.getTranscodeDirectory().getPath());
|
||||
command.setTranscodeSchemes(TranscodeScheme.values());
|
||||
command.setTechnologies(PlayerTechnology.values());
|
||||
command.setPlayers(players.toArray(new Player[players.size()]));
|
||||
command.setAdmin(user.isAdminRole());
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
PlayerSettingsCommand command = (PlayerSettingsCommand) comm;
|
||||
Player player = playerService.getPlayerById(command.getPlayerId());
|
||||
|
||||
player.setAutoControlEnabled(command.isAutoControlEnabled());
|
||||
player.setDynamicIp(command.isDynamicIp());
|
||||
player.setName(StringUtils.trimToNull(command.getName()));
|
||||
player.setTranscodeScheme(TranscodeScheme.valueOf(command.getTranscodeSchemeName()));
|
||||
player.setTechnology(PlayerTechnology.valueOf(command.getTechnologyName()));
|
||||
|
||||
playerService.updatePlayer(player);
|
||||
transcodingService.setTranscodingsForPlayer(player, command.getActiveTranscodingIds());
|
||||
|
||||
command.setReloadNeeded(true);
|
||||
}
|
||||
|
||||
private List<Player> getPlayers(HttpServletRequest request) {
|
||||
User user = securityService.getCurrentUser(request);
|
||||
String username = user.getUsername();
|
||||
List<Player> players = playerService.getAllPlayers();
|
||||
List<Player> authorizedPlayers = new ArrayList<Player>();
|
||||
|
||||
for (Player player : players) {
|
||||
// Only display authorized players.
|
||||
if (user.isAdminRole() || username.equals(player.getUsername())) {
|
||||
authorizedPlayers.add(player);
|
||||
}
|
||||
}
|
||||
return authorizedPlayers;
|
||||
}
|
||||
|
||||
private void handleRequestParameters(HttpServletRequest request) {
|
||||
if (request.getParameter("delete") != null) {
|
||||
playerService.removePlayerById(request.getParameter("delete"));
|
||||
} else if (request.getParameter("clone") != null) {
|
||||
playerService.clonePlayer(request.getParameter("clone"));
|
||||
}
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setTranscodingService(TranscodingService transcodingService) {
|
||||
this.transcodingService = transcodingService;
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.Playlist;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.PlaylistService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for the playlist page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlaylistController extends ParameterizableViewController {
|
||||
|
||||
private SecurityService securityService;
|
||||
private PlaylistService playlistService;
|
||||
private SettingsService settingsService;
|
||||
private PlayerService playerService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
User user = securityService.getCurrentUser(request);
|
||||
String username = user.getUsername();
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
Playlist playlist = playlistService.getPlaylist(id);
|
||||
if (playlist == null) {
|
||||
return new ModelAndView(new RedirectView("notFound.view"));
|
||||
}
|
||||
|
||||
map.put("playlist", playlist);
|
||||
map.put("user", user);
|
||||
map.put("player", player);
|
||||
map.put("editAllowed", username.equals(playlist.getUsername()) || securityService.isAdmin(username));
|
||||
map.put("partyMode", userSettings.isPartyModeEnabled());
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 2014 (C) Sindre Mehus
|
||||
*/
|
||||
package net.sourceforge.subsonic.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.Playlist;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.PlaylistService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the playlists page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlaylistsController extends ParameterizableViewController {
|
||||
|
||||
private SecurityService securityService;
|
||||
private PlaylistService playlistService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
List<Playlist> playlists = playlistService.getReadablePlaylistsForUser(user.getUsername());
|
||||
|
||||
map.put("playlists", playlists);
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 2015 (C) Sindre Mehus
|
||||
*/
|
||||
|
||||
package net.sourceforge.subsonic.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.service.PodcastService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
|
||||
/**
|
||||
* Controller for the "Podcast channel" page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastChannelController extends ParameterizableViewController {
|
||||
|
||||
private PodcastService podcastService;
|
||||
private SecurityService securityService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
|
||||
int channelId = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("channel", podcastService.getChannel(channelId));
|
||||
map.put("episodes", podcastService.getEpisodes(channelId));
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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 2015 (C) Sindre Mehus
|
||||
*/
|
||||
package net.sourceforge.subsonic.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.PodcastChannel;
|
||||
import net.sourceforge.subsonic.domain.PodcastEpisode;
|
||||
import net.sourceforge.subsonic.service.PodcastService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the "Podcast channels" page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastChannelsController extends ParameterizableViewController {
|
||||
|
||||
private PodcastService podcastService;
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
|
||||
Map<PodcastChannel, List<PodcastEpisode>> channels = new LinkedHashMap<PodcastChannel, List<PodcastEpisode>>();
|
||||
Map<Integer, PodcastChannel> channelMap = new HashMap<Integer, PodcastChannel>();
|
||||
for (PodcastChannel channel : podcastService.getAllChannels()) {
|
||||
channels.put(channel, podcastService.getEpisodes(channel.getId()));
|
||||
channelMap.put(channel.getId(), channel);
|
||||
}
|
||||
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("channels", channels);
|
||||
map.put("channelMap", channelMap);
|
||||
map.put("newestEpisodes", podcastService.getNewestEpisodes(10));
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.Playlist;
|
||||
import net.sourceforge.subsonic.service.PlaylistService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for the page used to generate the Podcast XML file.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastController extends ParameterizableViewController {
|
||||
|
||||
private static final DateFormat RSS_DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
|
||||
private PlaylistService playlistService;
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
String url = request.getRequestURL().toString();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<Playlist> playlists = playlistService.getReadablePlaylistsForUser(username);
|
||||
List<Podcast> podcasts = new ArrayList<Podcast>();
|
||||
|
||||
for (Playlist playlist : playlists) {
|
||||
|
||||
List<MediaFile> songs = playlistService.getFilesInPlaylist(playlist.getId());
|
||||
if (songs.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
long length = 0L;
|
||||
for (MediaFile song : songs) {
|
||||
length += song.getFileSize();
|
||||
}
|
||||
String publishDate = RSS_DATE_FORMAT.format(playlist.getCreated());
|
||||
|
||||
// Resolve content type.
|
||||
String suffix = songs.get(0).getFormat();
|
||||
String type = StringUtil.getMimeType(suffix);
|
||||
|
||||
// Rewrite URLs in case we're behind a proxy.
|
||||
if (settingsService.isRewriteUrlEnabled()) {
|
||||
String referer = request.getHeader("referer");
|
||||
url = StringUtil.rewriteUrl(url, referer);
|
||||
}
|
||||
|
||||
String enclosureUrl = url.replaceFirst("/podcast.*", "/stream?playlist=" + playlist.getId());
|
||||
enclosureUrl = settingsService.rewriteRemoteUrl(enclosureUrl);
|
||||
|
||||
podcasts.add(new Podcast(playlist.getName(), publishDate, enclosureUrl, length, type));
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
map.put("url", url);
|
||||
map.put("podcasts", podcasts);
|
||||
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains information about a single Podcast.
|
||||
*/
|
||||
public static class Podcast {
|
||||
private String name;
|
||||
private String publishDate;
|
||||
private String enclosureUrl;
|
||||
private long length;
|
||||
private String type;
|
||||
|
||||
public Podcast(String name, String publishDate, String enclosureUrl, long length, String type) {
|
||||
this.name = name;
|
||||
this.publishDate = publishDate;
|
||||
this.enclosureUrl = enclosureUrl;
|
||||
this.length = length;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getPublishDate() {
|
||||
return publishDate;
|
||||
}
|
||||
|
||||
public String getEnclosureUrl() {
|
||||
return enclosureUrl;
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.domain.PodcastEpisode;
|
||||
import net.sourceforge.subsonic.domain.PodcastStatus;
|
||||
import net.sourceforge.subsonic.service.PodcastService;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Controller for the "Podcast receiver" page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastReceiverAdminController extends AbstractController {
|
||||
|
||||
private PodcastService podcastService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Integer channelId = ServletRequestUtils.getIntParameter(request, "channelId");
|
||||
|
||||
if (request.getParameter("add") != null) {
|
||||
String url = StringUtils.trim(request.getParameter("add"));
|
||||
podcastService.createChannel(url);
|
||||
return new ModelAndView(new RedirectView("podcastChannels.view"));
|
||||
}
|
||||
if (request.getParameter("downloadEpisode") != null) {
|
||||
download(StringUtil.parseInts(request.getParameter("downloadEpisode")));
|
||||
return new ModelAndView(new RedirectView("podcastChannel.view?id=" + channelId));
|
||||
}
|
||||
if (request.getParameter("deleteChannel") != null) {
|
||||
podcastService.deleteChannel(channelId);
|
||||
return new ModelAndView(new RedirectView("podcastChannels.view"));
|
||||
}
|
||||
if (request.getParameter("deleteEpisode") != null) {
|
||||
for (int episodeId : StringUtil.parseInts(request.getParameter("deleteEpisode"))) {
|
||||
podcastService.deleteEpisode(episodeId, true);
|
||||
}
|
||||
return new ModelAndView(new RedirectView("podcastChannel.view?id=" + channelId));
|
||||
}
|
||||
if (request.getParameter("refresh") != null) {
|
||||
if (channelId != null) {
|
||||
podcastService.refreshChannel(channelId, true);
|
||||
return new ModelAndView(new RedirectView("podcastChannel.view?id=" + channelId));
|
||||
} else {
|
||||
podcastService.refreshAllChannels(true);
|
||||
return new ModelAndView(new RedirectView("podcastChannels.view"));
|
||||
}
|
||||
}
|
||||
|
||||
return new ModelAndView(new RedirectView("podcastChannels.view"));
|
||||
}
|
||||
|
||||
private void download(int[] episodeIds) {
|
||||
for (Integer episodeId : episodeIds) {
|
||||
PodcastEpisode episode = podcastService.getEpisode(episodeId, false);
|
||||
if (episode != null && episode.getUrl() != null &&
|
||||
(episode.getStatus() == PodcastStatus.NEW ||
|
||||
episode.getStatus() == PodcastStatus.ERROR ||
|
||||
episode.getStatus() == PodcastStatus.SKIPPED)) {
|
||||
|
||||
podcastService.downloadEpisode(episode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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 org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.PodcastService;
|
||||
import net.sourceforge.subsonic.command.PodcastSettingsCommand;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate the Podcast receiver.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private PodcastService podcastService;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
PodcastSettingsCommand command = new PodcastSettingsCommand();
|
||||
|
||||
command.setInterval(String.valueOf(settingsService.getPodcastUpdateInterval()));
|
||||
command.setEpisodeRetentionCount(String.valueOf(settingsService.getPodcastEpisodeRetentionCount()));
|
||||
command.setEpisodeDownloadCount(String.valueOf(settingsService.getPodcastEpisodeDownloadCount()));
|
||||
command.setFolder(settingsService.getPodcastFolder());
|
||||
return command;
|
||||
}
|
||||
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
PodcastSettingsCommand command = (PodcastSettingsCommand) comm;
|
||||
command.setToast(true);
|
||||
|
||||
settingsService.setPodcastUpdateInterval(Integer.parseInt(command.getInterval()));
|
||||
settingsService.setPodcastEpisodeRetentionCount(Integer.parseInt(command.getEpisodeRetentionCount()));
|
||||
settingsService.setPodcastEpisodeDownloadCount(Integer.parseInt(command.getEpisodeDownloadCount()));
|
||||
settingsService.setPodcastFolder(command.getFolder());
|
||||
settingsService.save();
|
||||
|
||||
podcastService.schedule();
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
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.util.Date;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import net.sourceforge.subsonic.command.PremiumSettingsCommand;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the Subsonic Premium page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PremiumSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
PremiumSettingsCommand command = new PremiumSettingsCommand();
|
||||
command.setPath(request.getParameter("path"));
|
||||
command.setForceChange(request.getParameter("change") != null);
|
||||
command.setLicenseInfo(settingsService.getLicenseInfo());
|
||||
command.setBrand(settingsService.getBrand());
|
||||
command.setUser(securityService.getCurrentUser(request));
|
||||
return command;
|
||||
}
|
||||
|
||||
protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object com, BindException errors)
|
||||
throws Exception {
|
||||
PremiumSettingsCommand command = (PremiumSettingsCommand) com;
|
||||
Date now = new Date();
|
||||
|
||||
settingsService.setLicenseCode(command.getLicenseCode());
|
||||
settingsService.setLicenseEmail(command.getLicenseInfo().getLicenseEmail());
|
||||
settingsService.setLicenseDate(now);
|
||||
settingsService.save();
|
||||
settingsService.scheduleLicenseValidation();
|
||||
|
||||
// Reflect changes in view. The validator will validate the license asynchronously.
|
||||
command.setLicenseInfo(settingsService.getLicenseInfo());
|
||||
command.setToast(true);
|
||||
|
||||
return new ModelAndView(getSuccessView(), errors.getModel());
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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.InputStream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
|
||||
/**
|
||||
* A proxy for external HTTP requests.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ProxyController implements Controller {
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String url = ServletRequestUtils.getRequiredStringParameter(request, "url");
|
||||
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
HttpConnectionParams.setConnectionTimeout(client.getParams(), 15000);
|
||||
HttpConnectionParams.setSoTimeout(client.getParams(), 15000);
|
||||
HttpGet method = new HttpGet(url);
|
||||
|
||||
InputStream in = null;
|
||||
try {
|
||||
HttpResponse resp = client.execute(method);
|
||||
int statusCode = resp.getStatusLine().getStatusCode();
|
||||
if (statusCode != HttpStatus.SC_OK) {
|
||||
response.sendError(statusCode);
|
||||
} else {
|
||||
in = resp.getEntity().getContent();
|
||||
IOUtils.copy(in, response.getOutputStream());
|
||||
}
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
client.getConnectionManager().shutdown();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
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.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.PlayQueue;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.RandomSearchCriteria;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SearchService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the creating a random play queue.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class RandomPlayQueueController extends ParameterizableViewController {
|
||||
|
||||
private PlayerService playerService;
|
||||
private List<ReloadFrame> reloadFrames;
|
||||
private SearchService searchService;
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
int size = ServletRequestUtils.getRequiredIntParameter(request, "size");
|
||||
String genre = request.getParameter("genre");
|
||||
if (StringUtils.equalsIgnoreCase("any", genre)) {
|
||||
genre = null;
|
||||
}
|
||||
|
||||
Integer fromYear = null;
|
||||
Integer toYear = null;
|
||||
|
||||
String year = request.getParameter("year");
|
||||
if (!StringUtils.equalsIgnoreCase("any", year)) {
|
||||
String[] tmp = StringUtils.split(year);
|
||||
fromYear = Integer.parseInt(tmp[0]);
|
||||
toYear = Integer.parseInt(tmp[1]);
|
||||
}
|
||||
|
||||
List<MusicFolder> musicFolders = getMusicFolders(request);
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
|
||||
RandomSearchCriteria criteria = new RandomSearchCriteria(size, genre, fromYear, toYear, musicFolders);
|
||||
playQueue.addFiles(false, searchService.getRandomSongs(criteria));
|
||||
|
||||
if (request.getParameter("autoRandom") != null) {
|
||||
playQueue.setRandomSearchCriteria(criteria);
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("reloadFrames", reloadFrames);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<MusicFolder> getMusicFolders(HttpServletRequest request) throws ServletRequestBindingException {
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
Integer selectedMusicFolderId = ServletRequestUtils.getRequiredIntParameter(request, "musicFolderId");
|
||||
if (selectedMusicFolderId == -1) {
|
||||
selectedMusicFolderId = null;
|
||||
}
|
||||
return settingsService.getMusicFoldersForUser(username, selectedMusicFolderId);
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setReloadFrames(List<ReloadFrame> reloadFrames) {
|
||||
this.reloadFrames = reloadFrames;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
/**
|
||||
* Used in subsonic-servlet.xml to specify frame reloading.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ReloadFrame {
|
||||
private String frame;
|
||||
private String view;
|
||||
|
||||
public ReloadFrame() {}
|
||||
|
||||
public ReloadFrame(String frame, String view) {
|
||||
this.frame = frame;
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public String getFrame() {
|
||||
return frame;
|
||||
}
|
||||
|
||||
public void setFrame(String frame) {
|
||||
this.frame = frame;
|
||||
}
|
||||
|
||||
public String getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
public void setView(String view) {
|
||||
this.view = view;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
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.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.VersionService;
|
||||
|
||||
/**
|
||||
* Controller for the right frame.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class RightController extends ParameterizableViewController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
private VersionService versionService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
|
||||
UserSettings userSettings = settingsService.getUserSettings(securityService.getCurrentUsername(request));
|
||||
if (userSettings.isFinalVersionNotificationEnabled() && versionService.isNewFinalVersionAvailable()) {
|
||||
map.put("newVersionAvailable", true);
|
||||
map.put("latestVersion", versionService.getLatestFinalVersion());
|
||||
|
||||
} else if (userSettings.isBetaVersionNotificationEnabled() && versionService.isNewBetaVersionAvailable()) {
|
||||
map.put("newVersionAvailable", true);
|
||||
map.put("latestVersion", versionService.getLatestBetaVersion());
|
||||
}
|
||||
|
||||
map.put("brand", settingsService.getBrand());
|
||||
map.put("showNowPlaying", userSettings.isShowNowPlayingEnabled());
|
||||
map.put("showChat", userSettings.isShowChatEnabled());
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setVersionService(VersionService versionService) {
|
||||
this.versionService = versionService;
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
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.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import net.sourceforge.subsonic.command.SearchCommand;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.SearchCriteria;
|
||||
import net.sourceforge.subsonic.domain.SearchResult;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SearchService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the search page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SearchController extends SimpleFormController {
|
||||
|
||||
private static final int MATCH_COUNT = 25;
|
||||
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
private PlayerService playerService;
|
||||
private SearchService searchService;
|
||||
|
||||
@Override
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
return new SearchCommand();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object com, BindException errors)
|
||||
throws Exception {
|
||||
SearchCommand command = (SearchCommand) com;
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(user.getUsername());
|
||||
command.setUser(user);
|
||||
command.setPartyModeEnabled(userSettings.isPartyModeEnabled());
|
||||
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(user.getUsername());
|
||||
String query = StringUtils.trimToNull(command.getQuery());
|
||||
|
||||
if (query != null) {
|
||||
|
||||
SearchCriteria criteria = new SearchCriteria();
|
||||
criteria.setCount(MATCH_COUNT);
|
||||
criteria.setQuery(query);
|
||||
|
||||
SearchResult artists = searchService.search(criteria, musicFolders, SearchService.IndexType.ARTIST);
|
||||
command.setArtists(artists.getMediaFiles());
|
||||
|
||||
SearchResult albums = searchService.search(criteria, musicFolders, SearchService.IndexType.ALBUM);
|
||||
command.setAlbums(albums.getMediaFiles());
|
||||
|
||||
SearchResult songs = searchService.search(criteria, musicFolders, SearchService.IndexType.SONG);
|
||||
command.setSongs(songs.getMediaFiles());
|
||||
|
||||
command.setPlayer(playerService.getPlayer(request, response));
|
||||
}
|
||||
|
||||
return new ModelAndView(getSuccessView(), errors.getModel());
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller for updating music file metadata.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SetMusicFileInfoController extends AbstractController {
|
||||
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
String action = request.getParameter("action");
|
||||
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(id);
|
||||
|
||||
if ("comment".equals(action)) {
|
||||
mediaFile.setComment(StringUtil.toHtml(request.getParameter("comment")));
|
||||
mediaFileService.updateMediaFile(mediaFile);
|
||||
}
|
||||
|
||||
String url = "main.view?id=" + id;
|
||||
return new ModelAndView(new RedirectView(url));
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
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 javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.RatingService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
|
||||
/**
|
||||
* Controller for updating music file ratings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SetRatingController extends AbstractController {
|
||||
|
||||
private RatingService ratingService;
|
||||
private SecurityService securityService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
Integer rating = ServletRequestUtils.getIntParameter(request, "rating");
|
||||
if (rating == 0) {
|
||||
rating = null;
|
||||
}
|
||||
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(id);
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
ratingService.setRatingForUser(username, mediaFile, rating);
|
||||
|
||||
return new ModelAndView(new RedirectView("main.view?id=" + id));
|
||||
}
|
||||
|
||||
public void setRatingService(RatingService ratingService) {
|
||||
this.ratingService = ratingService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.domain.*;
|
||||
import net.sourceforge.subsonic.service.*;
|
||||
import org.springframework.web.servlet.*;
|
||||
import org.springframework.web.servlet.view.*;
|
||||
import org.springframework.web.servlet.mvc.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/**
|
||||
* Controller for the main settings page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SettingsController extends AbstractController {
|
||||
|
||||
private SecurityService securityService;
|
||||
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
|
||||
// Redirect to music folder settings if admin.
|
||||
String view = user.isAdminRole() ? "musicFolderSettings.view" : "personalSettings.view";
|
||||
|
||||
return new ModelAndView(new RedirectView(view));
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.PlayQueue;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.Share;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.PlaylistService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.ShareService;
|
||||
import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for sharing music on Twitter, Facebook etc.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ShareManagementController extends MultiActionController {
|
||||
|
||||
private MediaFileService mediaFileService;
|
||||
private SettingsService settingsService;
|
||||
private ShareService shareService;
|
||||
private PlayerService playerService;
|
||||
private PlaylistService playlistService;
|
||||
private SecurityService securityService;
|
||||
|
||||
public ModelAndView createShare(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
List<MediaFile> files = getMediaFiles(request);
|
||||
MediaFile dir = null;
|
||||
if (!files.isEmpty()) {
|
||||
dir = files.get(0);
|
||||
if (!dir.isAlbum()) {
|
||||
dir = mediaFileService.getParentOf(dir);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("urlRedirectionEnabled", settingsService.isUrlRedirectionEnabled());
|
||||
map.put("dir", dir);
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
|
||||
Share share = shareService.createShare(request, files);
|
||||
String description = getDescription(request);
|
||||
if (description != null) {
|
||||
share.setDescription(description);
|
||||
shareService.updateShare(share);
|
||||
}
|
||||
|
||||
map.put("playUrl", shareService.getShareUrl(share));
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
|
||||
return new ModelAndView("createShare", "model", map);
|
||||
}
|
||||
|
||||
private String getDescription(HttpServletRequest request) throws ServletRequestBindingException {
|
||||
Integer playlistId = ServletRequestUtils.getIntParameter(request, "playlist");
|
||||
return playlistId == null ? null : playlistService.getPlaylist(playlistId).getName();
|
||||
}
|
||||
|
||||
private List<MediaFile> getMediaFiles(HttpServletRequest request) throws Exception {
|
||||
Integer id = ServletRequestUtils.getIntParameter(request, "id");
|
||||
String playerId = request.getParameter("player");
|
||||
Integer playlistId = ServletRequestUtils.getIntParameter(request, "playlist");
|
||||
|
||||
List<MediaFile> result = new ArrayList<MediaFile>();
|
||||
|
||||
if (id != null) {
|
||||
MediaFile album = mediaFileService.getMediaFile(id);
|
||||
int[] indexes = ServletRequestUtils.getIntParameters(request, "i");
|
||||
if (indexes.length == 0) {
|
||||
return Arrays.asList(album);
|
||||
}
|
||||
List<MediaFile> children = mediaFileService.getChildrenOf(album, true, false, true);
|
||||
for (int index : indexes) {
|
||||
result.add(children.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
else if (playerId != null) {
|
||||
Player player = playerService.getPlayerById(playerId);
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
result = playQueue.getFiles();
|
||||
}
|
||||
|
||||
else if (playlistId != null) {
|
||||
result = playlistService.getFilesInPlaylist(playlistId);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setShareService(ShareService shareService) {
|
||||
this.shareService = shareService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
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.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.Share;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.ShareService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate the set of shared media.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ShareSettingsController extends ParameterizableViewController {
|
||||
|
||||
private ShareService shareService;
|
||||
private SecurityService securityService;
|
||||
private MediaFileService mediaFileService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
if (isFormSubmission(request)) {
|
||||
handleParameters(request);
|
||||
map.put("toast", true);
|
||||
}
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
map.put("shareBaseUrl", shareService.getShareBaseUrl());
|
||||
map.put("shareInfos", getShareInfos(request));
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given request represents a form submission.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return if the request represents a form submission
|
||||
*/
|
||||
private boolean isFormSubmission(HttpServletRequest request) {
|
||||
return "POST".equals(request.getMethod());
|
||||
}
|
||||
|
||||
private void handleParameters(HttpServletRequest request) {
|
||||
User user = securityService.getCurrentUser(request);
|
||||
for (Share share : shareService.getSharesForUser(user)) {
|
||||
int id = share.getId();
|
||||
|
||||
String description = getParameter(request, "description", id);
|
||||
boolean delete = getParameter(request, "delete", id) != null;
|
||||
String expireIn = getParameter(request, "expireIn", id);
|
||||
|
||||
if (delete) {
|
||||
shareService.deleteShare(id);
|
||||
} else {
|
||||
if (expireIn != null) {
|
||||
share.setExpires(parseExpireIn(expireIn));
|
||||
}
|
||||
share.setDescription(description);
|
||||
shareService.updateShare(share);
|
||||
}
|
||||
}
|
||||
|
||||
boolean deleteExpired = ServletRequestUtils.getBooleanParameter(request, "deleteExpired", false);
|
||||
if (deleteExpired) {
|
||||
Date now = new Date();
|
||||
for (Share share : shareService.getSharesForUser(user)) {
|
||||
Date expires = share.getExpires();
|
||||
if (expires != null && expires.before(now)) {
|
||||
shareService.deleteShare(share.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<ShareInfo> getShareInfos(HttpServletRequest request) {
|
||||
List<ShareInfo> result = new ArrayList<ShareInfo>();
|
||||
User user = securityService.getCurrentUser(request);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(user.getUsername());
|
||||
|
||||
for (Share share : shareService.getSharesForUser(user)) {
|
||||
List<MediaFile> files = shareService.getSharedFiles(share.getId(), musicFolders);
|
||||
if (!files.isEmpty()) {
|
||||
MediaFile file = files.get(0);
|
||||
result.add(new ShareInfo(share, file.isDirectory() ? file : mediaFileService.getParentOf(file)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private String getParameter(HttpServletRequest request, String name, int id) {
|
||||
return StringUtils.trimToNull(request.getParameter(name + "[" + id + "]"));
|
||||
}
|
||||
|
||||
private Date parseExpireIn(String expireIn) {
|
||||
int days = Integer.parseInt(expireIn);
|
||||
if (days == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.DAY_OF_YEAR, days);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setShareService(ShareService shareService) {
|
||||
this.shareService = shareService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public static class ShareInfo {
|
||||
private final Share share;
|
||||
private final MediaFile dir;
|
||||
|
||||
public ShareInfo(Share share, MediaFile dir) {
|
||||
this.share = share;
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
public Share getShare() {
|
||||
return share;
|
||||
}
|
||||
|
||||
public MediaFile getDir() {
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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 2015 (C) Sindre Mehus
|
||||
*/
|
||||
package net.sourceforge.subsonic.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.SonosService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate the Sonos music service settings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SonosSettingsController extends ParameterizableViewController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SonosService sonosService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
if (isFormSubmission(request)) {
|
||||
handleParameters(request);
|
||||
map.put("toast", true);
|
||||
}
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
map.put("sonosEnabled", settingsService.isSonosEnabled());
|
||||
map.put("sonosServiceName", settingsService.getSonosServiceName());
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given request represents a form submission.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return if the request represents a form submission
|
||||
*/
|
||||
private boolean isFormSubmission(HttpServletRequest request) {
|
||||
return "POST".equals(request.getMethod());
|
||||
}
|
||||
|
||||
private void handleParameters(HttpServletRequest request) {
|
||||
boolean sonosEnabled = ServletRequestUtils.getBooleanParameter(request, "sonosEnabled", false);
|
||||
String sonosServiceName = StringUtils.trimToNull(request.getParameter("sonosServiceName"));
|
||||
if (sonosServiceName == null) {
|
||||
sonosServiceName = "Subsonic";
|
||||
}
|
||||
|
||||
settingsService.setSonosEnabled(sonosEnabled);
|
||||
settingsService.setSonosServiceName(sonosServiceName);
|
||||
settingsService.save();
|
||||
|
||||
sonosService.setMusicServiceEnabled(false);
|
||||
sonosService.setMusicServiceEnabled(sonosEnabled);
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSonosService(SonosService sonosService) {
|
||||
this.sonosService = sonosService;
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.dao.MediaFileDao;
|
||||
import net.sourceforge.subsonic.domain.CoverArtScheme;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for showing a user's starred items.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class StarredController extends ParameterizableViewController {
|
||||
|
||||
private PlayerService playerService;
|
||||
private MediaFileDao mediaFileDao;
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
String username = user.getUsername();
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
|
||||
List<MediaFile> artists = mediaFileDao.getStarredDirectories(0, Integer.MAX_VALUE, username, musicFolders);
|
||||
List<MediaFile> albums = mediaFileDao.getStarredAlbums(0, Integer.MAX_VALUE, username, musicFolders);
|
||||
List<MediaFile> files = mediaFileDao.getStarredFiles(0, Integer.MAX_VALUE, username, musicFolders);
|
||||
mediaFileService.populateStarredDate(artists, username);
|
||||
mediaFileService.populateStarredDate(albums, username);
|
||||
mediaFileService.populateStarredDate(files, username);
|
||||
|
||||
List<MediaFile> songs = new ArrayList<MediaFile>();
|
||||
List<MediaFile> videos = new ArrayList<MediaFile>();
|
||||
for (MediaFile file : files) {
|
||||
(file.isVideo() ? videos : songs).add(file);
|
||||
}
|
||||
|
||||
map.put("user", user);
|
||||
map.put("partyModeEnabled", userSettings.isPartyModeEnabled());
|
||||
map.put("player", playerService.getPlayer(request, response));
|
||||
map.put("coverArtSize", CoverArtScheme.MEDIUM.getSize());
|
||||
map.put("artists", artists);
|
||||
map.put("albums", albums);
|
||||
map.put("songs", songs);
|
||||
map.put("videos", videos);
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setMediaFileDao(MediaFileDao mediaFileDao) {
|
||||
this.mediaFileDao = mediaFileDao;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.domain.*;
|
||||
import net.sourceforge.subsonic.service.*;
|
||||
import org.jfree.chart.*;
|
||||
import org.jfree.chart.axis.*;
|
||||
import org.jfree.chart.plot.*;
|
||||
import org.jfree.chart.renderer.xy.*;
|
||||
import org.jfree.data.*;
|
||||
import org.jfree.data.time.*;
|
||||
import org.springframework.web.servlet.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Controller for generating a chart showing bitrate vs time.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class StatusChartController extends AbstractChartController {
|
||||
|
||||
private StatusService statusService;
|
||||
|
||||
public static final int IMAGE_WIDTH = 350;
|
||||
public static final int IMAGE_HEIGHT = 150;
|
||||
|
||||
public synchronized ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String type = request.getParameter("type");
|
||||
int index = Integer.parseInt(request.getParameter("index"));
|
||||
|
||||
List<TransferStatus> statuses = Collections.emptyList();
|
||||
if ("stream".equals(type)) {
|
||||
statuses = statusService.getAllStreamStatuses();
|
||||
} else if ("download".equals(type)) {
|
||||
statuses = statusService.getAllDownloadStatuses();
|
||||
} else if ("upload".equals(type)) {
|
||||
statuses = statusService.getAllUploadStatuses();
|
||||
}
|
||||
|
||||
if (index < 0 || index >= statuses.size()) {
|
||||
return null;
|
||||
}
|
||||
TransferStatus status = statuses.get(index);
|
||||
|
||||
TimeSeries series = new TimeSeries("Kbps", Millisecond.class);
|
||||
TransferStatus.SampleHistory history = status.getHistory();
|
||||
long to = System.currentTimeMillis();
|
||||
long from = to - status.getHistoryLengthMillis();
|
||||
Range range = new DateRange(from, to);
|
||||
|
||||
if (!history.isEmpty()) {
|
||||
|
||||
TransferStatus.Sample previous = history.get(0);
|
||||
|
||||
for (int i = 1; i < history.size(); i++) {
|
||||
TransferStatus.Sample sample = history.get(i);
|
||||
|
||||
long elapsedTimeMilis = sample.getTimestamp() - previous.getTimestamp();
|
||||
long bytesStreamed = Math.max(0L, sample.getBytesTransfered() - previous.getBytesTransfered());
|
||||
|
||||
double kbps = (8.0 * bytesStreamed / 1024.0) / (elapsedTimeMilis / 1000.0);
|
||||
series.addOrUpdate(new Millisecond(new Date(sample.getTimestamp())), kbps);
|
||||
|
||||
previous = sample;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute moving average.
|
||||
series = MovingAverage.createMovingAverage(series, "Kbps", 20000, 5000);
|
||||
|
||||
// Find min and max values.
|
||||
double min = 100;
|
||||
double max = 250;
|
||||
for (Object obj : series.getItems()) {
|
||||
TimeSeriesDataItem item = (TimeSeriesDataItem) obj;
|
||||
double value = item.getValue().doubleValue();
|
||||
if (item.getPeriod().getFirstMillisecond() > from) {
|
||||
min = Math.min(min, value);
|
||||
max = Math.max(max, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Add 10% to max value.
|
||||
max *= 1.1D;
|
||||
|
||||
// Subtract 10% from min value.
|
||||
min *= 0.9D;
|
||||
|
||||
TimeSeriesCollection dataset = new TimeSeriesCollection();
|
||||
dataset.addSeries(series);
|
||||
JFreeChart chart = ChartFactory.createTimeSeriesChart(null, null, null, dataset, false, false, false);
|
||||
XYPlot plot = (XYPlot) chart.getPlot();
|
||||
|
||||
plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT);
|
||||
Paint background = new GradientPaint(0, 0, Color.lightGray, 0, IMAGE_HEIGHT, Color.white);
|
||||
plot.setBackgroundPaint(background);
|
||||
|
||||
XYItemRenderer renderer = plot.getRendererForDataset(dataset);
|
||||
renderer.setSeriesPaint(0, Color.blue.darker());
|
||||
renderer.setSeriesStroke(0, new BasicStroke(2f));
|
||||
|
||||
// Set theme-specific colors.
|
||||
Color bgColor = getBackground(request);
|
||||
Color fgColor = getForeground(request);
|
||||
|
||||
chart.setBackgroundPaint(bgColor);
|
||||
|
||||
ValueAxis domainAxis = plot.getDomainAxis();
|
||||
domainAxis.setRange(range);
|
||||
domainAxis.setTickLabelPaint(fgColor);
|
||||
domainAxis.setTickMarkPaint(fgColor);
|
||||
domainAxis.setAxisLinePaint(fgColor);
|
||||
|
||||
ValueAxis rangeAxis = plot.getRangeAxis();
|
||||
rangeAxis.setRange(new Range(min, max));
|
||||
rangeAxis.setTickLabelPaint(fgColor);
|
||||
rangeAxis.setTickMarkPaint(fgColor);
|
||||
rangeAxis.setAxisLinePaint(fgColor);
|
||||
|
||||
ChartUtilities.writeChartAsPNG(response.getOutputStream(), chart, IMAGE_WIDTH, IMAGE_HEIGHT);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setStatusService(StatusService statusService) {
|
||||
this.statusService = statusService;
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.TransferStatus;
|
||||
import net.sourceforge.subsonic.service.StatusService;
|
||||
import net.sourceforge.subsonic.util.FileUtil;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for the status page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class StatusController extends ParameterizableViewController {
|
||||
|
||||
private StatusService statusService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
List<TransferStatus> streamStatuses = statusService.getAllStreamStatuses();
|
||||
List<TransferStatus> downloadStatuses = statusService.getAllDownloadStatuses();
|
||||
List<TransferStatus> uploadStatuses = statusService.getAllUploadStatuses();
|
||||
|
||||
Locale locale = RequestContextUtils.getLocale(request);
|
||||
List<TransferStatusHolder> transferStatuses = new ArrayList<TransferStatusHolder>();
|
||||
|
||||
for (int i = 0; i < streamStatuses.size(); i++) {
|
||||
long minutesAgo = streamStatuses.get(i).getMillisSinceLastUpdate() / 1000L / 60L;
|
||||
if (minutesAgo < 60L) {
|
||||
transferStatuses.add(new TransferStatusHolder(streamStatuses.get(i), true, false, false, i, locale));
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < downloadStatuses.size(); i++) {
|
||||
transferStatuses.add(new TransferStatusHolder(downloadStatuses.get(i), false, true, false, i, locale));
|
||||
}
|
||||
for (int i = 0; i < uploadStatuses.size(); i++) {
|
||||
transferStatuses.add(new TransferStatusHolder(uploadStatuses.get(i), false, false, true, i, locale));
|
||||
}
|
||||
|
||||
map.put("transferStatuses", transferStatuses);
|
||||
map.put("chartWidth", StatusChartController.IMAGE_WIDTH);
|
||||
map.put("chartHeight", StatusChartController.IMAGE_HEIGHT);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setStatusService(StatusService statusService) {
|
||||
this.statusService = statusService;
|
||||
}
|
||||
|
||||
public static class TransferStatusHolder {
|
||||
private TransferStatus transferStatus;
|
||||
private boolean isStream;
|
||||
private boolean isDownload;
|
||||
private boolean isUpload;
|
||||
private int index;
|
||||
private Locale locale;
|
||||
|
||||
public TransferStatusHolder(TransferStatus transferStatus, boolean isStream, boolean isDownload, boolean isUpload,
|
||||
int index, Locale locale) {
|
||||
this.transferStatus = transferStatus;
|
||||
this.isStream = isStream;
|
||||
this.isDownload = isDownload;
|
||||
this.isUpload = isUpload;
|
||||
this.index = index;
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
public boolean isStream() {
|
||||
return isStream;
|
||||
}
|
||||
|
||||
public boolean isDownload() {
|
||||
return isDownload;
|
||||
}
|
||||
|
||||
public boolean isUpload() {
|
||||
return isUpload;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return transferStatus.getPlayer();
|
||||
}
|
||||
|
||||
public String getPlayerType() {
|
||||
Player player = transferStatus.getPlayer();
|
||||
return player == null ? null : player.getType();
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
Player player = transferStatus.getPlayer();
|
||||
return player == null ? null : player.getUsername();
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return FileUtil.getShortPath(transferStatus.getFile());
|
||||
}
|
||||
|
||||
public String getBytes() {
|
||||
return StringUtil.formatBytes(transferStatus.getBytesTransfered(), locale);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+447
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
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.awt.Dimension;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.PlayQueue;
|
||||
import net.sourceforge.subsonic.domain.Player;
|
||||
import net.sourceforge.subsonic.domain.TransferStatus;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.VideoTranscodingSettings;
|
||||
import net.sourceforge.subsonic.io.PlayQueueInputStream;
|
||||
import net.sourceforge.subsonic.io.RangeOutputStream;
|
||||
import net.sourceforge.subsonic.io.ShoutCastOutputStream;
|
||||
import net.sourceforge.subsonic.service.AudioScrobblerService;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.PlaylistService;
|
||||
import net.sourceforge.subsonic.service.SearchService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.StatusService;
|
||||
import net.sourceforge.subsonic.service.TranscodingService;
|
||||
import net.sourceforge.subsonic.service.sonos.SonosHelper;
|
||||
import net.sourceforge.subsonic.util.HttpRange;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
import net.sourceforge.subsonic.util.Util;
|
||||
|
||||
/**
|
||||
* A controller which streams the content of a {@link net.sourceforge.subsonic.domain.PlayQueue} to a remote
|
||||
* {@link Player}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class StreamController implements Controller {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(StreamController.class);
|
||||
|
||||
private StatusService statusService;
|
||||
private PlayerService playerService;
|
||||
private PlaylistService playlistService;
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
private TranscodingService transcodingService;
|
||||
private AudioScrobblerService audioScrobblerService;
|
||||
private MediaFileService mediaFileService;
|
||||
private SearchService searchService;
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
TransferStatus status = null;
|
||||
PlayQueueInputStream in = null;
|
||||
Player player = playerService.getPlayer(request, response, false, true);
|
||||
User user = securityService.getUserByName(player.getUsername());
|
||||
|
||||
try {
|
||||
|
||||
if (!user.isStreamRole()) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Streaming is forbidden for user " + user.getUsername());
|
||||
return null;
|
||||
}
|
||||
|
||||
// If "playlist" request parameter is set, this is a Podcast request. In that case, create a separate
|
||||
// play queue (in order to support multiple parallel Podcast streams).
|
||||
Integer playlistId = ServletRequestUtils.getIntParameter(request, "playlist");
|
||||
boolean isPodcast = playlistId != null;
|
||||
if (isPodcast) {
|
||||
PlayQueue playQueue = new PlayQueue();
|
||||
playQueue.addFiles(false, playlistService.getFilesInPlaylist(playlistId));
|
||||
player.setPlayQueue(playQueue);
|
||||
Util.setContentLength(response, playQueue.length());
|
||||
LOG.info("Incoming Podcast request for playlist " + playlistId);
|
||||
}
|
||||
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
|
||||
String contentType = StringUtil.getMimeType(request.getParameter("suffix"));
|
||||
response.setContentType(contentType);
|
||||
|
||||
String preferredTargetFormat = request.getParameter("format");
|
||||
Integer maxBitRate = ServletRequestUtils.getIntParameter(request, "maxBitRate");
|
||||
if (Integer.valueOf(0).equals(maxBitRate)) {
|
||||
maxBitRate = null;
|
||||
}
|
||||
|
||||
VideoTranscodingSettings videoTranscodingSettings = null;
|
||||
|
||||
// Is this a request for a single file (typically from the embedded Flash player)?
|
||||
// In that case, create a separate playlist (in order to support multiple parallel streams).
|
||||
// Also, enable partial download (HTTP byte range).
|
||||
MediaFile file = getSingleFile(request);
|
||||
boolean isSingleFile = file != null;
|
||||
HttpRange range = null;
|
||||
|
||||
if (isSingleFile) {
|
||||
|
||||
if (!securityService.isFolderAccessAllowed(file, user.getUsername())) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||
"Access to file " + file.getId() + " is forbidden for user " + user.getUsername());
|
||||
return null;
|
||||
}
|
||||
|
||||
PlayQueue playQueue = new PlayQueue();
|
||||
playQueue.addFiles(true, file);
|
||||
player.setPlayQueue(playQueue);
|
||||
|
||||
if (!file.isVideo()) {
|
||||
response.setIntHeader("ETag", file.getId());
|
||||
response.setHeader("Accept-Ranges", "bytes");
|
||||
}
|
||||
|
||||
TranscodingService.Parameters parameters = transcodingService.getParameters(file, player, maxBitRate, preferredTargetFormat, null);
|
||||
long fileLength = getFileLength(parameters);
|
||||
boolean isConversion = parameters.isDownsample() || parameters.isTranscode();
|
||||
boolean estimateContentLength = ServletRequestUtils.getBooleanParameter(request, "estimateContentLength", false);
|
||||
boolean isHls = ServletRequestUtils.getBooleanParameter(request, "hls", false);
|
||||
|
||||
range = getRange(request, file);
|
||||
if (range != null && !file.isVideo()) {
|
||||
LOG.info("Got HTTP range: " + range);
|
||||
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
||||
Util.setContentLength(response, range.isClosed() ? range.size() : fileLength - range.getFirstBytePos());
|
||||
long lastBytePos = range.getLastBytePos() != null ? range.getLastBytePos() : fileLength - 1;
|
||||
response.setHeader("Content-Range", "bytes " + range.getFirstBytePos() + "-" + lastBytePos + "/" + fileLength);
|
||||
} else if (!isHls && (!isConversion || estimateContentLength)) {
|
||||
Util.setContentLength(response, fileLength);
|
||||
}
|
||||
|
||||
if (isHls) {
|
||||
response.setContentType(StringUtil.getMimeType("ts")); // HLS is always MPEG TS.
|
||||
} else {
|
||||
String transcodedSuffix = transcodingService.getSuffix(player, file, preferredTargetFormat);
|
||||
boolean sonos = SonosHelper.SUBSONIC_CLIENT_ID.equals(player.getClientId());
|
||||
response.setContentType(StringUtil.getMimeType(transcodedSuffix, sonos));
|
||||
setContentDuration(response, file);
|
||||
}
|
||||
|
||||
if (file.isVideo() || isHls) {
|
||||
videoTranscodingSettings = createVideoTranscodingSettings(file, request);
|
||||
}
|
||||
}
|
||||
|
||||
if (request.getMethod().equals("HEAD")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Terminate any other streams to this player.
|
||||
if (!isPodcast && !isSingleFile) {
|
||||
for (TransferStatus streamStatus : statusService.getStreamStatusesForPlayer(player)) {
|
||||
if (streamStatus.isActive()) {
|
||||
streamStatus.terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status = statusService.createStreamStatus(player);
|
||||
|
||||
in = new PlayQueueInputStream(player, status, maxBitRate, preferredTargetFormat, videoTranscodingSettings, transcodingService,
|
||||
audioScrobblerService, mediaFileService, searchService);
|
||||
OutputStream out = RangeOutputStream.wrap(response.getOutputStream(), range);
|
||||
|
||||
// Enabled SHOUTcast, if requested.
|
||||
boolean isShoutCastRequested = "1".equals(request.getHeader("icy-metadata"));
|
||||
if (isShoutCastRequested && !isSingleFile) {
|
||||
response.setHeader("icy-metaint", "" + ShoutCastOutputStream.META_DATA_INTERVAL);
|
||||
response.setHeader("icy-notice1", "This stream is served using Subsonic");
|
||||
response.setHeader("icy-notice2", "Subsonic - Free media streamer - subsonic.org");
|
||||
response.setHeader("icy-name", "Subsonic");
|
||||
response.setHeader("icy-genre", "Mixed");
|
||||
response.setHeader("icy-url", "http://subsonic.org/");
|
||||
out = new ShoutCastOutputStream(out, player.getPlayQueue(), settingsService);
|
||||
}
|
||||
|
||||
final int BUFFER_SIZE = 2048;
|
||||
byte[] buf = new byte[BUFFER_SIZE];
|
||||
|
||||
while (true) {
|
||||
|
||||
// Check if stream has been terminated.
|
||||
if (status.terminated()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (player.getPlayQueue().getStatus() == PlayQueue.Status.STOPPED) {
|
||||
if (isPodcast || isSingleFile) {
|
||||
break;
|
||||
} else {
|
||||
sendDummy(buf, out);
|
||||
}
|
||||
} else {
|
||||
|
||||
int n = in.read(buf);
|
||||
if (n == -1) {
|
||||
if (isPodcast || isSingleFile) {
|
||||
break;
|
||||
} else {
|
||||
sendDummy(buf, out);
|
||||
}
|
||||
} else {
|
||||
out.write(buf, 0, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (status != null) {
|
||||
securityService.updateUserByteCounts(user, status.getBytesTransfered(), 0L, 0L);
|
||||
statusService.removeStreamStatus(status);
|
||||
}
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setContentDuration(HttpServletResponse response, MediaFile file) {
|
||||
if (file.getDurationSeconds() != null) {
|
||||
response.setHeader("X-Content-Duration", String.format("%.1f", file.getDurationSeconds().doubleValue()));
|
||||
}
|
||||
}
|
||||
|
||||
private MediaFile getSingleFile(HttpServletRequest request) throws ServletRequestBindingException {
|
||||
String path = request.getParameter("path");
|
||||
if (path != null) {
|
||||
return mediaFileService.getMediaFile(path);
|
||||
}
|
||||
Integer id = ServletRequestUtils.getIntParameter(request, "id");
|
||||
if (id != null) {
|
||||
return mediaFileService.getMediaFile(id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private long getFileLength(TranscodingService.Parameters parameters) {
|
||||
MediaFile file = parameters.getMediaFile();
|
||||
|
||||
if (!parameters.isDownsample() && !parameters.isTranscode()) {
|
||||
return file.getFileSize();
|
||||
}
|
||||
Integer duration = file.getDurationSeconds();
|
||||
Integer maxBitRate = parameters.getMaxBitRate();
|
||||
|
||||
if (duration == null) {
|
||||
LOG.warn("Unknown duration for " + file + ". Unable to estimate transcoded size.");
|
||||
return file.getFileSize();
|
||||
}
|
||||
|
||||
if (maxBitRate == null) {
|
||||
LOG.error("Unknown bit rate for " + file + ". Unable to estimate transcoded size.");
|
||||
return file.getFileSize();
|
||||
}
|
||||
|
||||
return duration * maxBitRate * 1000L / 8L;
|
||||
}
|
||||
|
||||
private HttpRange getRange(HttpServletRequest request, MediaFile file) {
|
||||
|
||||
// First, look for "Range" HTTP header.
|
||||
HttpRange range = HttpRange.valueOf(request.getHeader("Range"));
|
||||
if (range != null) {
|
||||
return range;
|
||||
}
|
||||
|
||||
// Second, look for "offsetSeconds" request parameter.
|
||||
String offsetSeconds = request.getParameter("offsetSeconds");
|
||||
range = parseAndConvertOffsetSeconds(offsetSeconds, file);
|
||||
if (range != null) {
|
||||
return range;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRange parseAndConvertOffsetSeconds(String offsetSeconds, MediaFile file) {
|
||||
if (offsetSeconds == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Integer duration = file.getDurationSeconds();
|
||||
Long fileSize = file.getFileSize();
|
||||
if (duration == null || fileSize == null) {
|
||||
return null;
|
||||
}
|
||||
float offset = Float.parseFloat(offsetSeconds);
|
||||
|
||||
// Convert from time offset to byte offset.
|
||||
long byteOffset = (long) (fileSize * (offset / duration));
|
||||
return new HttpRange(byteOffset, null);
|
||||
|
||||
} catch (Exception x) {
|
||||
LOG.error("Failed to parse and convert time offset: " + offsetSeconds, x);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private VideoTranscodingSettings createVideoTranscodingSettings(MediaFile file, HttpServletRequest request) throws ServletRequestBindingException {
|
||||
Integer existingWidth = file.getWidth();
|
||||
Integer existingHeight = file.getHeight();
|
||||
Integer maxBitRate = ServletRequestUtils.getIntParameter(request, "maxBitRate");
|
||||
int timeOffset = ServletRequestUtils.getIntParameter(request, "timeOffset", 0);
|
||||
int defaultDuration = file.getDurationSeconds() == null ? Integer.MAX_VALUE : file.getDurationSeconds() - timeOffset;
|
||||
int duration = ServletRequestUtils.getIntParameter(request, "duration", defaultDuration);
|
||||
boolean hls = ServletRequestUtils.getBooleanParameter(request, "hls", false);
|
||||
|
||||
Dimension dim = getRequestedVideoSize(request.getParameter("size"));
|
||||
if (dim == null) {
|
||||
dim = getSuitableVideoSize(existingWidth, existingHeight, maxBitRate);
|
||||
}
|
||||
|
||||
return new VideoTranscodingSettings(dim.width, dim.height, timeOffset, duration, hls);
|
||||
}
|
||||
|
||||
protected Dimension getRequestedVideoSize(String sizeSpec) {
|
||||
if (sizeSpec == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Pattern pattern = Pattern.compile("^(\\d+)x(\\d+)$");
|
||||
Matcher matcher = pattern.matcher(sizeSpec);
|
||||
if (matcher.find()) {
|
||||
int w = Integer.parseInt(matcher.group(1));
|
||||
int h = Integer.parseInt(matcher.group(2));
|
||||
if (w >= 0 && h >= 0 && w <= 2000 && h <= 2000) {
|
||||
return new Dimension(w, h);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Dimension getSuitableVideoSize(Integer existingWidth, Integer existingHeight, Integer maxBitRate) {
|
||||
if (maxBitRate == null) {
|
||||
return new Dimension(400, 224);
|
||||
}
|
||||
|
||||
int w;
|
||||
if (maxBitRate < 400) {
|
||||
w = 400;
|
||||
} else if (maxBitRate < 600) {
|
||||
w = 480;
|
||||
} else if (maxBitRate < 1800) {
|
||||
w = 640;
|
||||
} else {
|
||||
w = 960;
|
||||
}
|
||||
int h = even(w * 9 / 16);
|
||||
|
||||
if (existingWidth == null || existingHeight == null) {
|
||||
return new Dimension(w, h);
|
||||
}
|
||||
|
||||
if (existingWidth < w || existingHeight < h) {
|
||||
return new Dimension(even(existingWidth), even(existingHeight));
|
||||
}
|
||||
|
||||
double aspectRate = existingWidth.doubleValue() / existingHeight.doubleValue();
|
||||
h = (int) Math.round(w / aspectRate);
|
||||
|
||||
return new Dimension(even(w), even(h));
|
||||
}
|
||||
|
||||
// Make sure width and height are multiples of two, as some versions of ffmpeg require it.
|
||||
private int even(int size) {
|
||||
return size + (size % 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed the other end with some dummy data to keep it from reconnecting.
|
||||
*/
|
||||
private void sendDummy(byte[] buf, OutputStream out) throws IOException {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException x) {
|
||||
LOG.warn("Interrupted in sleep.", x);
|
||||
}
|
||||
Arrays.fill(buf, (byte) 0xFF);
|
||||
out.write(buf);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public void setStatusService(StatusService statusService) {
|
||||
this.statusService = statusService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setTranscodingService(TranscodingService transcodingService) {
|
||||
this.transcodingService = transcodingService;
|
||||
}
|
||||
|
||||
public void setAudioScrobblerService(AudioScrobblerService audioScrobblerService) {
|
||||
this.audioScrobblerService = audioScrobblerService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.AvatarScheme;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the top frame.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class TopController extends ParameterizableViewController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(user.getUsername());
|
||||
|
||||
map.put("user", user);
|
||||
map.put("showSideBar", userSettings.isShowSideBar());
|
||||
map.put("showAvatar", userSettings.getAvatarScheme() != AvatarScheme.NONE);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.domain.Transcoding;
|
||||
import net.sourceforge.subsonic.service.TranscodingService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate the set of transcoding configurations.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class TranscodingSettingsController extends ParameterizableViewController {
|
||||
|
||||
private TranscodingService transcodingService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
if (isFormSubmission(request)) {
|
||||
handleParameters(request, map);
|
||||
map.put("toast", true);
|
||||
}
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
map.put("transcodings", transcodingService.getAllTranscodings());
|
||||
map.put("transcodeDirectory", transcodingService.getTranscodeDirectory());
|
||||
map.put("downsampleCommand", settingsService.getDownsamplingCommand());
|
||||
map.put("hlsCommand", settingsService.getHlsCommand());
|
||||
map.put("brand", settingsService.getBrand());
|
||||
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given request represents a form submission.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return if the request represents a form submission
|
||||
*/
|
||||
private boolean isFormSubmission(HttpServletRequest request) {
|
||||
return "POST".equals(request.getMethod());
|
||||
}
|
||||
|
||||
private void handleParameters(HttpServletRequest request, Map<String, Object> map) {
|
||||
|
||||
for (Transcoding transcoding : transcodingService.getAllTranscodings()) {
|
||||
Integer id = transcoding.getId();
|
||||
String name = getParameter(request, "name", id);
|
||||
String sourceFormats = getParameter(request, "sourceFormats", id);
|
||||
String targetFormat = getParameter(request, "targetFormat", id);
|
||||
String step1 = getParameter(request, "step1", id);
|
||||
String step2 = getParameter(request, "step2", id);
|
||||
boolean delete = getParameter(request, "delete", id) != null;
|
||||
|
||||
if (delete) {
|
||||
transcodingService.deleteTranscoding(id);
|
||||
} else if (name == null) {
|
||||
map.put("error", "transcodingsettings.noname");
|
||||
} else if (sourceFormats == null) {
|
||||
map.put("error", "transcodingsettings.nosourceformat");
|
||||
} else if (targetFormat == null) {
|
||||
map.put("error", "transcodingsettings.notargetformat");
|
||||
} else if (step1 == null) {
|
||||
map.put("error", "transcodingsettings.nostep1");
|
||||
} else {
|
||||
transcoding.setName(name);
|
||||
transcoding.setSourceFormats(sourceFormats);
|
||||
transcoding.setTargetFormat(targetFormat);
|
||||
transcoding.setStep1(step1);
|
||||
transcoding.setStep2(step2);
|
||||
transcodingService.updateTranscoding(transcoding);
|
||||
}
|
||||
}
|
||||
|
||||
String name = StringUtils.trimToNull(request.getParameter("name"));
|
||||
String sourceFormats = StringUtils.trimToNull(request.getParameter("sourceFormats"));
|
||||
String targetFormat = StringUtils.trimToNull(request.getParameter("targetFormat"));
|
||||
String step1 = StringUtils.trimToNull(request.getParameter("step1"));
|
||||
String step2 = StringUtils.trimToNull(request.getParameter("step2"));
|
||||
boolean defaultActive = request.getParameter("defaultActive") != null;
|
||||
|
||||
if (name != null || sourceFormats != null || targetFormat != null || step1 != null || step2 != null) {
|
||||
Transcoding transcoding = new Transcoding(null, name, sourceFormats, targetFormat, step1, step2, null, defaultActive);
|
||||
if (name == null) {
|
||||
map.put("error", "transcodingsettings.noname");
|
||||
} else if (sourceFormats == null) {
|
||||
map.put("error", "transcodingsettings.nosourceformat");
|
||||
} else if (targetFormat == null) {
|
||||
map.put("error", "transcodingsettings.notargetformat");
|
||||
} else if (step1 == null) {
|
||||
map.put("error", "transcodingsettings.nostep1");
|
||||
} else {
|
||||
transcodingService.createTranscoding(transcoding);
|
||||
}
|
||||
if (map.containsKey("error")) {
|
||||
map.put("newTranscoding", transcoding);
|
||||
}
|
||||
}
|
||||
settingsService.setDownsamplingCommand(StringUtils.trim(request.getParameter("downsampleCommand")));
|
||||
settingsService.setHlsCommand(StringUtils.trim(request.getParameter("hlsCommand")));
|
||||
settingsService.save();
|
||||
}
|
||||
|
||||
private String getParameter(HttpServletRequest request, String name, Integer id) {
|
||||
return StringUtils.trimToNull(request.getParameter(name + "[" + id + "]"));
|
||||
}
|
||||
|
||||
public void setTranscodingService(TranscodingService transcodingService) {
|
||||
this.transcodingService = transcodingService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.*;
|
||||
import net.sourceforge.subsonic.domain.*;
|
||||
import net.sourceforge.subsonic.upload.*;
|
||||
import net.sourceforge.subsonic.service.*;
|
||||
import net.sourceforge.subsonic.util.*;
|
||||
import org.apache.commons.fileupload.*;
|
||||
import org.apache.commons.fileupload.servlet.*;
|
||||
import org.apache.commons.io.*;
|
||||
import org.apache.tools.zip.*;
|
||||
import org.springframework.web.servlet.*;
|
||||
import org.springframework.web.servlet.mvc.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Controller which receives uploaded files.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class UploadController extends ParameterizableViewController {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(UploadController.class);
|
||||
|
||||
private SecurityService securityService;
|
||||
private PlayerService playerService;
|
||||
private StatusService statusService;
|
||||
private SettingsService settingsService;
|
||||
public static final String UPLOAD_STATUS = "uploadStatus";
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
List<File> uploadedFiles = new ArrayList<File>();
|
||||
List<File> unzippedFiles = new ArrayList<File>();
|
||||
TransferStatus status = null;
|
||||
|
||||
try {
|
||||
|
||||
status = statusService.createUploadStatus(playerService.getPlayer(request, response, false, false));
|
||||
status.setBytesTotal(request.getContentLength());
|
||||
|
||||
request.getSession().setAttribute(UPLOAD_STATUS, status);
|
||||
|
||||
// Check that we have a file upload request
|
||||
if (!ServletFileUpload.isMultipartContent(request)) {
|
||||
throw new Exception("Illegal request.");
|
||||
}
|
||||
|
||||
File dir = null;
|
||||
boolean unzip = false;
|
||||
|
||||
UploadListener listener = new UploadListenerImpl(status);
|
||||
|
||||
FileItemFactory factory = new MonitoredDiskFileItemFactory(listener);
|
||||
ServletFileUpload upload = new ServletFileUpload(factory);
|
||||
|
||||
List<?> items = upload.parseRequest(request);
|
||||
|
||||
// First, look for "dir" and "unzip" parameters.
|
||||
for (Object o : items) {
|
||||
FileItem item = (FileItem) o;
|
||||
|
||||
if (item.isFormField() && "dir".equals(item.getFieldName())) {
|
||||
dir = new File(item.getString());
|
||||
} else if (item.isFormField() && "unzip".equals(item.getFieldName())) {
|
||||
unzip = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (dir == null) {
|
||||
throw new Exception("Missing 'dir' parameter.");
|
||||
}
|
||||
|
||||
// Look for file items.
|
||||
for (Object o : items) {
|
||||
FileItem item = (FileItem) o;
|
||||
|
||||
if (!item.isFormField()) {
|
||||
String fileName = item.getName();
|
||||
if (fileName.trim().length() > 0) {
|
||||
|
||||
File targetFile = new File(dir, new File(fileName).getName());
|
||||
|
||||
if (!securityService.isUploadAllowed(targetFile)) {
|
||||
throw new Exception("Permission denied: " + StringUtil.toHtml(targetFile.getPath()));
|
||||
}
|
||||
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
item.write(targetFile);
|
||||
uploadedFiles.add(targetFile);
|
||||
LOG.info("Uploaded " + targetFile);
|
||||
|
||||
if (unzip && targetFile.getName().toLowerCase().endsWith(".zip")) {
|
||||
unzip(targetFile, unzippedFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Uploading failed.", x);
|
||||
map.put("exception", x);
|
||||
} finally {
|
||||
if (status != null) {
|
||||
statusService.removeUploadStatus(status);
|
||||
request.getSession().removeAttribute(UPLOAD_STATUS);
|
||||
User user = securityService.getCurrentUser(request);
|
||||
securityService.updateUserByteCounts(user, 0L, 0L, status.getBytesTransfered());
|
||||
}
|
||||
}
|
||||
|
||||
map.put("uploadedFiles", uploadedFiles);
|
||||
map.put("unzippedFiles", unzippedFiles);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void unzip(File file, List<File> unzippedFiles) throws Exception {
|
||||
LOG.info("Unzipping " + file);
|
||||
|
||||
ZipFile zipFile = new ZipFile(file);
|
||||
|
||||
try {
|
||||
|
||||
Enumeration<?> entries = zipFile.getEntries();
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipEntry entry = (ZipEntry) entries.nextElement();
|
||||
File entryFile = new File(file.getParentFile(), entry.getName());
|
||||
|
||||
if (!entry.isDirectory()) {
|
||||
|
||||
if (!securityService.isUploadAllowed(entryFile)) {
|
||||
throw new Exception("Permission denied: " + StringUtil.toHtml(entryFile.getPath()));
|
||||
}
|
||||
|
||||
entryFile.getParentFile().mkdirs();
|
||||
InputStream inputStream = null;
|
||||
OutputStream outputStream = null;
|
||||
try {
|
||||
inputStream = zipFile.getInputStream(entry);
|
||||
outputStream = new FileOutputStream(entryFile);
|
||||
|
||||
byte[] buf = new byte[8192];
|
||||
while (true) {
|
||||
int n = inputStream.read(buf);
|
||||
if (n == -1) {
|
||||
break;
|
||||
}
|
||||
outputStream.write(buf, 0, n);
|
||||
}
|
||||
|
||||
LOG.info("Unzipped " + entryFile);
|
||||
unzippedFiles.add(entryFile);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(inputStream);
|
||||
IOUtils.closeQuietly(outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zipFile.close();
|
||||
file.delete();
|
||||
|
||||
} finally {
|
||||
zipFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setStatusService(StatusService statusService) {
|
||||
this.statusService = statusService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives callbacks as the file upload progresses.
|
||||
*/
|
||||
private class UploadListenerImpl implements UploadListener {
|
||||
private TransferStatus status;
|
||||
private long start;
|
||||
|
||||
private UploadListenerImpl(TransferStatus status) {
|
||||
this.status = status;
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void start(String fileName) {
|
||||
status.setFile(new File(fileName));
|
||||
}
|
||||
|
||||
public void bytesRead(long bytesRead) {
|
||||
|
||||
// Throttle bitrate.
|
||||
|
||||
long byteCount = status.getBytesTransfered() + bytesRead;
|
||||
long bitCount = byteCount * 8L;
|
||||
|
||||
float elapsedMillis = Math.max(1, System.currentTimeMillis() - start);
|
||||
float elapsedSeconds = elapsedMillis / 1000.0F;
|
||||
long maxBitsPerSecond = getBitrateLimit();
|
||||
|
||||
status.setBytesTransfered(byteCount);
|
||||
|
||||
if (maxBitsPerSecond > 0) {
|
||||
float sleepMillis = 1000.0F * (bitCount / maxBitsPerSecond - elapsedSeconds);
|
||||
if (sleepMillis > 0) {
|
||||
try {
|
||||
Thread.sleep((long) sleepMillis);
|
||||
} catch (InterruptedException x) {
|
||||
LOG.warn("Failed to sleep.", x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long getBitrateLimit() {
|
||||
return 1024L * settingsService.getUploadBitrateLimit() / Math.max(1, statusService.getAllUploadStatuses().size());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
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.awt.Color;
|
||||
import java.awt.GradientPaint;
|
||||
import java.awt.Paint;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.jfree.chart.ChartFactory;
|
||||
import org.jfree.chart.ChartUtilities;
|
||||
import org.jfree.chart.JFreeChart;
|
||||
import org.jfree.chart.axis.AxisLocation;
|
||||
import org.jfree.chart.axis.CategoryAxis;
|
||||
import org.jfree.chart.axis.CategoryLabelPositions;
|
||||
import org.jfree.chart.axis.LogarithmicAxis;
|
||||
import org.jfree.chart.plot.CategoryPlot;
|
||||
import org.jfree.chart.plot.PlotOrientation;
|
||||
import org.jfree.chart.renderer.category.BarRenderer;
|
||||
import org.jfree.data.category.CategoryDataset;
|
||||
import org.jfree.data.category.DefaultCategoryDataset;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
|
||||
/**
|
||||
* Controller for generating a chart showing bitrate vs time.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class UserChartController extends AbstractChartController {
|
||||
|
||||
private SecurityService securityService;
|
||||
|
||||
public static final int IMAGE_WIDTH = 400;
|
||||
public static final int IMAGE_MIN_HEIGHT = 200;
|
||||
private static final long BYTES_PER_MB = 1024L * 1024L;
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String type = request.getParameter("type");
|
||||
CategoryDataset dataset = createDataset(type);
|
||||
JFreeChart chart = createChart(dataset, request);
|
||||
|
||||
int imageHeight = Math.max(IMAGE_MIN_HEIGHT, 15 * dataset.getColumnCount());
|
||||
|
||||
ChartUtilities.writeChartAsPNG(response.getOutputStream(), chart, IMAGE_WIDTH, imageHeight);
|
||||
return null;
|
||||
}
|
||||
|
||||
private CategoryDataset createDataset(String type) {
|
||||
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
|
||||
List<User> users = securityService.getAllUsers();
|
||||
for (User user : users) {
|
||||
double value;
|
||||
if ("stream".equals(type)) {
|
||||
value = user.getBytesStreamed();
|
||||
} else if ("download".equals(type)) {
|
||||
value = user.getBytesDownloaded();
|
||||
} else if ("upload".equals(type)) {
|
||||
value = user.getBytesUploaded();
|
||||
} else if ("total".equals(type)) {
|
||||
value = user.getBytesStreamed() + user.getBytesDownloaded() + user.getBytesUploaded();
|
||||
} else {
|
||||
throw new RuntimeException("Illegal chart type: " + type);
|
||||
}
|
||||
|
||||
value /= BYTES_PER_MB;
|
||||
dataset.addValue(value, "Series", user.getUsername());
|
||||
}
|
||||
|
||||
return dataset;
|
||||
}
|
||||
|
||||
private JFreeChart createChart(CategoryDataset dataset, HttpServletRequest request) {
|
||||
JFreeChart chart = ChartFactory.createBarChart(null, null, null, dataset, PlotOrientation.HORIZONTAL, false, false, false);
|
||||
|
||||
CategoryPlot plot = chart.getCategoryPlot();
|
||||
Paint background = new GradientPaint(0, 0, Color.lightGray, 0, IMAGE_MIN_HEIGHT, Color.white);
|
||||
plot.setBackgroundPaint(background);
|
||||
plot.setDomainGridlinePaint(Color.white);
|
||||
plot.setDomainGridlinesVisible(true);
|
||||
plot.setRangeGridlinePaint(Color.white);
|
||||
plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
|
||||
|
||||
LogarithmicAxis rangeAxis = new LogarithmicAxis(null);
|
||||
rangeAxis.setStrictValuesFlag(false);
|
||||
rangeAxis.setAllowNegativesFlag(true);
|
||||
plot.setRangeAxis(rangeAxis);
|
||||
|
||||
// Disable bar outlines.
|
||||
BarRenderer renderer = (BarRenderer) plot.getRenderer();
|
||||
renderer.setDrawBarOutline(false);
|
||||
|
||||
// Set up gradient paint for series.
|
||||
GradientPaint gp0 = new GradientPaint(
|
||||
0.0f, 0.0f, Color.blue,
|
||||
0.0f, 0.0f, new Color(0, 0, 64)
|
||||
);
|
||||
renderer.setSeriesPaint(0, gp0);
|
||||
|
||||
// Rotate labels.
|
||||
CategoryAxis domainAxis = plot.getDomainAxis();
|
||||
domainAxis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(Math.PI / 6.0));
|
||||
|
||||
// Set theme-specific colors.
|
||||
Color bgColor = getBackground(request);
|
||||
Color fgColor = getForeground(request);
|
||||
|
||||
chart.setBackgroundPaint(bgColor);
|
||||
|
||||
domainAxis.setTickLabelPaint(fgColor);
|
||||
domainAxis.setTickMarkPaint(fgColor);
|
||||
domainAxis.setAxisLinePaint(fgColor);
|
||||
|
||||
rangeAxis.setTickLabelPaint(fgColor);
|
||||
rangeAxis.setTickMarkPaint(fgColor);
|
||||
rangeAxis.setAxisLinePaint(fgColor);
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
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 net.sourceforge.subsonic.command.UserSettingsCommand;
|
||||
import net.sourceforge.subsonic.domain.MusicFolder;
|
||||
import net.sourceforge.subsonic.domain.TranscodeScheme;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.domain.UserSettings;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.service.TranscodingService;
|
||||
import net.sourceforge.subsonic.util.Util;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate users.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class UserSettingsController extends SimpleFormController {
|
||||
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
private TranscodingService transcodingService;
|
||||
|
||||
@Override
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
UserSettingsCommand command = new UserSettingsCommand();
|
||||
|
||||
User user = getUser(request);
|
||||
if (user != null) {
|
||||
command.setUser(user);
|
||||
command.setEmail(user.getEmail());
|
||||
command.setAdmin(User.USERNAME_ADMIN.equals(user.getUsername()));
|
||||
UserSettings userSettings = settingsService.getUserSettings(user.getUsername());
|
||||
command.setTranscodeSchemeName(userSettings.getTranscodeScheme().name());
|
||||
|
||||
} else {
|
||||
command.setNewUser(true);
|
||||
command.setStreamRole(true);
|
||||
command.setSettingsRole(true);
|
||||
}
|
||||
|
||||
command.setUsers(securityService.getAllUsers());
|
||||
command.setTranscodingSupported(transcodingService.isDownsamplingSupported(null));
|
||||
command.setTranscodeDirectory(transcodingService.getTranscodeDirectory().getPath());
|
||||
command.setTranscodeSchemes(TranscodeScheme.values());
|
||||
command.setLdapEnabled(settingsService.isLdapEnabled());
|
||||
command.setAllMusicFolders(settingsService.getAllMusicFolders());
|
||||
command.setAllowedMusicFolderIds(Util.toIntArray(getAllowedMusicFolderIds(user)));
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
private User getUser(HttpServletRequest request) throws ServletRequestBindingException {
|
||||
Integer userIndex = ServletRequestUtils.getIntParameter(request, "userIndex");
|
||||
if (userIndex != null) {
|
||||
List<User> allUsers = securityService.getAllUsers();
|
||||
if (userIndex >= 0 && userIndex < allUsers.size()) {
|
||||
return allUsers.get(userIndex);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Integer> getAllowedMusicFolderIds(User user) {
|
||||
List<Integer> result = new ArrayList<Integer>();
|
||||
List<MusicFolder> allowedMusicFolders = user == null
|
||||
? settingsService.getAllMusicFolders()
|
||||
: settingsService.getMusicFoldersForUser(user.getUsername());
|
||||
|
||||
for (MusicFolder musicFolder : allowedMusicFolders) {
|
||||
result.add(musicFolder.getId());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
UserSettingsCommand command = (UserSettingsCommand) comm;
|
||||
|
||||
if (command.isDeleteUser()) {
|
||||
deleteUser(command);
|
||||
} else if (command.isNewUser()) {
|
||||
createUser(command);
|
||||
} else {
|
||||
updateUser(command);
|
||||
}
|
||||
resetCommand(command);
|
||||
}
|
||||
|
||||
private void deleteUser(UserSettingsCommand command) {
|
||||
securityService.deleteUser(command.getUsername());
|
||||
}
|
||||
|
||||
public void createUser(UserSettingsCommand command) {
|
||||
User user = new User(command.getUsername(), command.getPassword(), StringUtils.trimToNull(command.getEmail()));
|
||||
user.setLdapAuthenticated(command.isLdapAuthenticated());
|
||||
securityService.createUser(user);
|
||||
updateUser(command);
|
||||
}
|
||||
|
||||
public void updateUser(UserSettingsCommand command) {
|
||||
User user = securityService.getUserByName(command.getUsername());
|
||||
user.setEmail(StringUtils.trimToNull(command.getEmail()));
|
||||
user.setLdapAuthenticated(command.isLdapAuthenticated());
|
||||
user.setAdminRole(command.isAdminRole());
|
||||
user.setDownloadRole(command.isDownloadRole());
|
||||
user.setUploadRole(command.isUploadRole());
|
||||
user.setCoverArtRole(command.isCoverArtRole());
|
||||
user.setCommentRole(command.isCommentRole());
|
||||
user.setPodcastRole(command.isPodcastRole());
|
||||
user.setStreamRole(command.isStreamRole());
|
||||
user.setJukeboxRole(command.isJukeboxRole());
|
||||
user.setSettingsRole(command.isSettingsRole());
|
||||
user.setShareRole(command.isShareRole());
|
||||
|
||||
if (command.isPasswordChange()) {
|
||||
user.setPassword(command.getPassword());
|
||||
}
|
||||
|
||||
securityService.updateUser(user);
|
||||
|
||||
UserSettings userSettings = settingsService.getUserSettings(command.getUsername());
|
||||
userSettings.setTranscodeScheme(TranscodeScheme.valueOf(command.getTranscodeSchemeName()));
|
||||
userSettings.setChanged(new Date());
|
||||
settingsService.updateUserSettings(userSettings);
|
||||
|
||||
List<Integer> allowedMusicFolderIds = Util.toIntegerList(command.getAllowedMusicFolderIds());
|
||||
settingsService.setMusicFoldersForUser(command.getUsername(), allowedMusicFolderIds);
|
||||
}
|
||||
|
||||
private void resetCommand(UserSettingsCommand command) {
|
||||
command.setUser(null);
|
||||
command.setUsers(securityService.getAllUsers());
|
||||
command.setDeleteUser(false);
|
||||
command.setPasswordChange(false);
|
||||
command.setNewUser(true);
|
||||
command.setStreamRole(true);
|
||||
command.setSettingsRole(true);
|
||||
command.setPassword(null);
|
||||
command.setConfirmPassword(null);
|
||||
command.setEmail(null);
|
||||
command.setTranscodeSchemeName(null);
|
||||
command.setAllMusicFolders(settingsService.getAllMusicFolders());
|
||||
command.setAllowedMusicFolderIds(Util.toIntArray(getAllowedMusicFolderIds(null)));
|
||||
command.setToast(true);
|
||||
command.setReload(true);
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setTranscodingService(TranscodingService transcodingService) {
|
||||
this.transcodingService = transcodingService;
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
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.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import net.sourceforge.subsonic.domain.MediaFile;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
import net.sourceforge.subsonic.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller for the page used to play videos.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class VideoPlayerController extends ParameterizableViewController {
|
||||
|
||||
public static final int DEFAULT_BIT_RATE = 2000;
|
||||
public static final int[] BIT_RATES = {200, 300, 400, 500, 700, 1000, 1200, 1500, 2000, 3000, 5000};
|
||||
|
||||
private MediaFileService mediaFileService;
|
||||
private SettingsService settingsService;
|
||||
private PlayerService playerService;
|
||||
private SecurityService securityService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
MediaFile file = mediaFileService.getMediaFile(id);
|
||||
mediaFileService.populateStarredDate(file, user.getUsername());
|
||||
|
||||
Integer duration = file.getDurationSeconds();
|
||||
String playerId = playerService.getPlayer(request, response).getId();
|
||||
String url = request.getRequestURL().toString();
|
||||
String streamUrl = url.replaceFirst("/videoPlayer.view.*", "/stream?id=" + file.getId() + "&player=" + playerId);
|
||||
String coverArtUrl = url.replaceFirst("/videoPlayer.view.*", "/coverArt.view?id=" + file.getId());
|
||||
|
||||
// Rewrite URLs in case we're behind a proxy.
|
||||
if (settingsService.isRewriteUrlEnabled()) {
|
||||
String referer = request.getHeader("referer");
|
||||
streamUrl = StringUtil.rewriteUrl(streamUrl, referer);
|
||||
coverArtUrl = StringUtil.rewriteUrl(coverArtUrl, referer);
|
||||
}
|
||||
|
||||
String remoteStreamUrl = settingsService.rewriteRemoteUrl(streamUrl);
|
||||
String remoteCoverArtUrl = settingsService.rewriteRemoteUrl(coverArtUrl);
|
||||
|
||||
map.put("video", file);
|
||||
map.put("streamUrl", streamUrl);
|
||||
map.put("remoteStreamUrl", remoteStreamUrl);
|
||||
map.put("remoteCoverArtUrl", remoteCoverArtUrl);
|
||||
map.put("duration", duration);
|
||||
map.put("bitRates", BIT_RATES);
|
||||
map.put("defaultBitRate", DEFAULT_BIT_RATE);
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
map.put("user", user);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Map<String, Integer> createSkipOffsets(int durationSeconds) {
|
||||
LinkedHashMap<String, Integer> result = new LinkedHashMap<String, Integer>();
|
||||
for (int i = 0; i < durationSeconds; i += 60) {
|
||||
result.put(StringUtil.formatDuration(i), i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
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.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
|
||||
|
||||
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.RandomSearchCriteria;
|
||||
import net.sourceforge.subsonic.domain.SearchCriteria;
|
||||
import net.sourceforge.subsonic.domain.SearchResult;
|
||||
import net.sourceforge.subsonic.domain.User;
|
||||
import net.sourceforge.subsonic.service.MediaFileService;
|
||||
import net.sourceforge.subsonic.service.MusicIndexService;
|
||||
import net.sourceforge.subsonic.service.PlayerService;
|
||||
import net.sourceforge.subsonic.service.PlaylistService;
|
||||
import net.sourceforge.subsonic.service.SearchService;
|
||||
import net.sourceforge.subsonic.service.SecurityService;
|
||||
import net.sourceforge.subsonic.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Multi-controller used for wap pages.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class WapController extends MultiActionController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private PlayerService playerService;
|
||||
private PlaylistService playlistService;
|
||||
private SecurityService securityService;
|
||||
private MusicIndexService musicIndexService;
|
||||
private MediaFileService mediaFileService;
|
||||
private SearchService searchService;
|
||||
|
||||
public ModelAndView index(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
return wap(request, response);
|
||||
}
|
||||
|
||||
public ModelAndView wap(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MusicFolder> folders = settingsService.getMusicFoldersForUser(username);
|
||||
|
||||
if (folders.isEmpty()) {
|
||||
map.put("noMusic", true);
|
||||
} else {
|
||||
|
||||
SortedMap<MusicIndex, List<MusicIndex.SortableArtistWithMediaFiles>> allArtists = musicIndexService.getIndexedArtists(folders, false);
|
||||
|
||||
// If an index is given as parameter, only show music files for this index.
|
||||
String index = request.getParameter("index");
|
||||
if (index != null) {
|
||||
List<MusicIndex.SortableArtistWithMediaFiles> artists = allArtists.get(new MusicIndex(index));
|
||||
if (artists == null) {
|
||||
map.put("noMusic", true);
|
||||
} else {
|
||||
map.put("artists", artists);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, list all indexes.
|
||||
else {
|
||||
map.put("indexes", allArtists.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
return new ModelAndView("wap/index", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView browse(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String path = request.getParameter("path");
|
||||
MediaFile parent = mediaFileService.getMediaFile(path);
|
||||
|
||||
// Create array of file(s) to display.
|
||||
List<MediaFile> children;
|
||||
if (parent.isDirectory()) {
|
||||
children = mediaFileService.getChildrenOf(parent, true, true, true);
|
||||
} else {
|
||||
children = new ArrayList<MediaFile>();
|
||||
children.add(parent);
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("parent", parent);
|
||||
map.put("children", children);
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
|
||||
return new ModelAndView("wap/browse", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView playlist(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
// Create array of players to control. If the "player" attribute is set for this session,
|
||||
// only the player with this ID is controlled. Otherwise, all players are controlled.
|
||||
List<Player> players = playerService.getAllPlayers();
|
||||
|
||||
String playerId = (String) request.getSession().getAttribute("player");
|
||||
if (playerId != null) {
|
||||
Player player = playerService.getPlayerById(playerId);
|
||||
if (player != null) {
|
||||
players = Arrays.asList(player);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
for (Player player : players) {
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
map.put("playlist", playQueue);
|
||||
|
||||
if (request.getParameter("play") != null) {
|
||||
MediaFile file = mediaFileService.getMediaFile(request.getParameter("play"));
|
||||
playQueue.addFiles(false, file);
|
||||
} else if (request.getParameter("add") != null) {
|
||||
MediaFile file = mediaFileService.getMediaFile(request.getParameter("add"));
|
||||
playQueue.addFiles(true, file);
|
||||
} else if (request.getParameter("skip") != null) {
|
||||
playQueue.setIndex(Integer.parseInt(request.getParameter("skip")));
|
||||
} else if (request.getParameter("clear") != null) {
|
||||
playQueue.clear();
|
||||
} else if (request.getParameter("load") != null) {
|
||||
List<MediaFile> songs = playlistService.getFilesInPlaylist(ServletRequestUtils.getIntParameter(request, "id"));
|
||||
playQueue.addFiles(false, songs);
|
||||
} else if (request.getParameter("random") != null) {
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(securityService.getCurrentUsername(request));
|
||||
List<MediaFile> randomFiles = searchService.getRandomSongs(new RandomSearchCriteria(20, null, null, null, musicFolders));
|
||||
playQueue.addFiles(false, randomFiles);
|
||||
}
|
||||
}
|
||||
|
||||
map.put("players", players);
|
||||
return new ModelAndView("wap/playlist", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView loadPlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("playlists", playlistService.getReadablePlaylistsForUser(securityService.getCurrentUsername(request)));
|
||||
return new ModelAndView("wap/loadPlaylist", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView search(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
return new ModelAndView("wap/search");
|
||||
}
|
||||
|
||||
public ModelAndView searchResult(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
String query = request.getParameter("query");
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("hits", search(query, username));
|
||||
|
||||
return new ModelAndView("wap/searchResult", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView settings(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String playerId = (String) request.getSession().getAttribute("player");
|
||||
|
||||
List<Player> allPlayers = playerService.getAllPlayers();
|
||||
User user = securityService.getCurrentUser(request);
|
||||
List<Player> players = new ArrayList<Player>();
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
for (Player player : allPlayers) {
|
||||
// Only display authorized players.
|
||||
if (user.isAdminRole() || user.getUsername().equals(player.getUsername())) {
|
||||
players.add(player);
|
||||
}
|
||||
|
||||
}
|
||||
map.put("playerId", playerId);
|
||||
map.put("players", players);
|
||||
return new ModelAndView("wap/settings", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView selectPlayer(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
request.getSession().setAttribute("player", request.getParameter("playerId"));
|
||||
return settings(request, response);
|
||||
}
|
||||
|
||||
private List<MediaFile> search(String query, String username) throws IOException {
|
||||
SearchCriteria criteria = new SearchCriteria();
|
||||
criteria.setQuery(query);
|
||||
criteria.setOffset(0);
|
||||
criteria.setCount(50);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
|
||||
SearchResult result = searchService.search(criteria, musicFolders, SearchService.IndexType.SONG);
|
||||
return result.getMediaFiles();
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMusicIndexService(MusicIndexService musicIndexService) {
|
||||
this.musicIndexService = musicIndexService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
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.dao;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.jdbc.core.*;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
|
||||
import net.sourceforge.subsonic.Logger;
|
||||
|
||||
/**
|
||||
* Abstract superclass for all DAO's.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class AbstractDao {
|
||||
private static final Logger LOG = Logger.getLogger(AbstractDao.class);
|
||||
|
||||
private DaoHelper daoHelper;
|
||||
|
||||
/**
|
||||
* Returns a JDBC template for performing database operations.
|
||||
* @return A JDBC template.
|
||||
*/
|
||||
public JdbcTemplate getJdbcTemplate() {
|
||||
return daoHelper.getJdbcTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #getJdbcTemplate()}, but with named parameters.
|
||||
*/
|
||||
public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
|
||||
return daoHelper.getNamedParameterJdbcTemplate();
|
||||
}
|
||||
|
||||
protected String questionMarks(String columns) {
|
||||
int count = columns.split(", ").length;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < count; i++) {
|
||||
builder.append('?');
|
||||
if (i < count - 1) {
|
||||
builder.append(", ");
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected String prefix(String columns, String prefix) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String s : columns.split(", ")) {
|
||||
builder.append(prefix).append(".").append(s).append(",");
|
||||
}
|
||||
if (builder.length() > 0) {
|
||||
builder.setLength(builder.length() - 1);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected int update(String sql, Object... args) {
|
||||
long t = System.nanoTime();
|
||||
int result = getJdbcTemplate().update(sql, args);
|
||||
log(sql, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void log(String sql, long startTimeNano) {
|
||||
long millis = (System.nanoTime() - startTimeNano) / 1000000L;
|
||||
|
||||
// Log queries that take more than 2 seconds.
|
||||
if (millis > TimeUnit.SECONDS.toMillis(2L)) {
|
||||
LOG.debug(millis + " ms: " + sql);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> List<T> query(String sql, RowMapper rowMapper, Object... args) {
|
||||
long t = System.nanoTime();
|
||||
List<T> result = getJdbcTemplate().query(sql, args, rowMapper);
|
||||
log(sql, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected <T> List<T> namedQuery(String sql, RowMapper rowMapper, Map<String, Object> args) {
|
||||
long t = System.nanoTime();
|
||||
List<T> result = getNamedParameterJdbcTemplate().query(sql, args, rowMapper);
|
||||
log(sql, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected List<String> queryForStrings(String sql, Object... args) {
|
||||
long t = System.nanoTime();
|
||||
List<String> result = getJdbcTemplate().queryForList(sql, args, String.class);
|
||||
log(sql, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected List<Integer> queryForInts(String sql, Object... args) {
|
||||
long t = System.nanoTime();
|
||||
List<Integer> result = getJdbcTemplate().queryForList(sql, args, Integer.class);
|
||||
log(sql, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected List<String> namedQueryForStrings(String sql, Map<String, Object> args) {
|
||||
long t = System.nanoTime();
|
||||
List<String> result = getNamedParameterJdbcTemplate().queryForList(sql, args, String.class);
|
||||
log(sql, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Integer queryForInt(String sql, Integer defaultValue, Object... args) {
|
||||
long t = System.nanoTime();
|
||||
List<Integer> list = getJdbcTemplate().queryForList(sql, args, Integer.class);
|
||||
Integer result = list.isEmpty() ? defaultValue : list.get(0) == null ? defaultValue : list.get(0);
|
||||
log(sql, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Integer namedQueryForInt(String sql, Integer defaultValue, Map<String, Object> args) {
|
||||
long t = System.nanoTime();
|
||||
List<Integer> list = getNamedParameterJdbcTemplate().queryForList(sql, args, Integer.class);
|
||||
Integer result = list.isEmpty() ? defaultValue : list.get(0) == null ? defaultValue : list.get(0);
|
||||
log(sql, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Date queryForDate(String sql, Date defaultValue, Object... args) {
|
||||
long t = System.nanoTime();
|
||||
List<Date> list = getJdbcTemplate().queryForList(sql, args, Date.class);
|
||||
Date result = list.isEmpty() ? defaultValue : list.get(0) == null ? defaultValue : list.get(0);
|
||||
log(sql, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Long queryForLong(String sql, Long defaultValue, Object... args) {
|
||||
long t = System.nanoTime();
|
||||
List<Long> list = getJdbcTemplate().queryForList(sql, args, Long.class);
|
||||
Long result = list.isEmpty() ? defaultValue : list.get(0) == null ? defaultValue : list.get(0);
|
||||
log(sql, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected <T> T queryOne(String sql, RowMapper rowMapper, Object... args) {
|
||||
List<T> list = query(sql, rowMapper, args);
|
||||
return list.isEmpty() ? null : list.get(0);
|
||||
}
|
||||
|
||||
protected <T> T namedQueryOne(String sql, RowMapper rowMapper, Map<String, Object> args) {
|
||||
List<T> list = namedQuery(sql, rowMapper, args);
|
||||
return list.isEmpty() ? null : list.get(0);
|
||||
}
|
||||
|
||||
public void setDaoHelper(DaoHelper daoHelper) {
|
||||
this.daoHelper = daoHelper;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user