aboutsummaryrefslogtreecommitdiff
path: root/subsonic-main/src/main/java/net/sourceforge/subsonic/domain
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2012-07-02 21:24:02 -0700
committerScott Jackson <daneren2005@gmail.com>2012-07-02 21:24:02 -0700
commita1a18f77a50804e0127dfa4b0f5240c49c541184 (patch)
tree19a38880afe505beddb5590379a8134d7730a277 /subsonic-main/src/main/java/net/sourceforge/subsonic/domain
parentb61d787706979e7e20f4c3c4f93c1f129d92273f (diff)
downloaddsub-a1a18f77a50804e0127dfa4b0f5240c49c541184.tar.gz
dsub-a1a18f77a50804e0127dfa4b0f5240c49c541184.tar.bz2
dsub-a1a18f77a50804e0127dfa4b0f5240c49c541184.zip
Initial Commit
Diffstat (limited to 'subsonic-main/src/main/java/net/sourceforge/subsonic/domain')
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Album.java166
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Artist.java95
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Avatar.java75
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/AvatarScheme.java52
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/CacheElement.java65
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/CoverArtScheme.java48
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/InternetRadio.java168
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaFile.java449
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaFileComparator.java99
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaLibraryStatistics.java62
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MusicFolder.java148
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MusicIndex.java167
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/NATPMPRouter.java61
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PlayQueue.java417
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Player.java338
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PlayerTechnology.java49
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Playlist.java141
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastChannel.java96
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastEpisode.java172
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastStatus.java29
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/RandomSearchCriteria.java70
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Router.java43
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SBBIRouter.java63
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SearchCriteria.java59
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SearchResult.java69
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Share.java100
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Theme.java42
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/TranscodeScheme.java104
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Transcoding.java221
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/TransferStatus.java303
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/User.java245
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/UserSettings.java328
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Version.java141
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/VideoTranscodingSettings.java50
-rw-r--r--subsonic-main/src/main/java/net/sourceforge/subsonic/domain/WeUPnPRouter.java56
35 files changed, 4791 insertions, 0 deletions
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Album.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Album.java
new file mode 100644
index 00000000..23e8afaf
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Album.java
@@ -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.domain;
+
+import java.util.Date;
+
+/**
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public class Album {
+
+ private int id;
+ private String path;
+ private String name;
+ private String artist;
+ private int songCount;
+ private int durationSeconds;
+ private String coverArtPath;
+ private int playCount;
+ private Date lastPlayed;
+ private String comment;
+ private Date created;
+ private Date lastScanned;
+ private boolean present;
+
+ public Album() {
+ }
+
+ public Album(int id, String path, String name, String artist, int songCount, int durationSeconds, String coverArtPath,
+ int playCount, Date lastPlayed, String comment, Date created, Date lastScanned, boolean present) {
+ this.id = id;
+ this.path = path;
+ this.name = name;
+ this.artist = artist;
+ this.songCount = songCount;
+ this.durationSeconds = durationSeconds;
+ this.coverArtPath = coverArtPath;
+ this.playCount = playCount;
+ this.lastPlayed = lastPlayed;
+ this.comment = comment;
+ this.created = created;
+ this.lastScanned = lastScanned;
+ this.present = present;
+ }
+
+ 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 getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getArtist() {
+ return artist;
+ }
+
+ public void setArtist(String artist) {
+ this.artist = artist;
+ }
+
+ public int getSongCount() {
+ return songCount;
+ }
+
+ public void setSongCount(int songCount) {
+ this.songCount = songCount;
+ }
+
+ public int getDurationSeconds() {
+ return durationSeconds;
+ }
+
+ public void setDurationSeconds(int durationSeconds) {
+ this.durationSeconds = durationSeconds;
+ }
+
+ public String getCoverArtPath() {
+ return coverArtPath;
+ }
+
+ public void setCoverArtPath(String coverArtPath) {
+ this.coverArtPath = coverArtPath;
+ }
+
+ public int getPlayCount() {
+ return playCount;
+ }
+
+ public void setPlayCount(int playCount) {
+ this.playCount = playCount;
+ }
+
+ public Date getLastPlayed() {
+ return lastPlayed;
+ }
+
+ public void setLastPlayed(Date lastPlayed) {
+ this.lastPlayed = lastPlayed;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+
+ public Date getLastScanned() {
+ return lastScanned;
+ }
+
+ public void setLastScanned(Date lastScanned) {
+ this.lastScanned = lastScanned;
+ }
+
+ public boolean isPresent() {
+ return present;
+ }
+
+ public void setPresent(boolean present) {
+ this.present = present;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Artist.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Artist.java
new file mode 100644
index 00000000..e6141f78
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Artist.java
@@ -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 2009 (C) Sindre Mehus
+ */
+package net.sourceforge.subsonic.domain;
+
+import java.util.Date;
+
+/**
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public class Artist {
+
+ private int id;
+ private String name;
+ private String coverArtPath;
+ private int albumCount;
+ private Date lastScanned;
+ private boolean present;
+
+ public Artist() {
+ }
+
+ public Artist(int id, String name, String coverArtPath, int albumCount, Date lastScanned, boolean present) {
+ this.id = id;
+ this.name = name;
+ this.coverArtPath = coverArtPath;
+ this.albumCount = albumCount;
+ this.lastScanned = lastScanned;
+ this.present = present;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getCoverArtPath() {
+ return coverArtPath;
+ }
+
+ public void setCoverArtPath(String coverArtPath) {
+ this.coverArtPath = coverArtPath;
+ }
+
+ public int getAlbumCount() {
+ return albumCount;
+ }
+
+ public void setAlbumCount(int albumCount) {
+ this.albumCount = albumCount;
+ }
+
+ public Date getLastScanned() {
+ return lastScanned;
+ }
+
+ public void setLastScanned(Date lastScanned) {
+ this.lastScanned = lastScanned;
+ }
+
+ public boolean isPresent() {
+ return present;
+ }
+
+ public void setPresent(boolean present) {
+ this.present = present;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Avatar.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Avatar.java
new file mode 100644
index 00000000..0089a8a3
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Avatar.java
@@ -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.domain;
+
+import java.util.Date;
+
+/**
+ * An icon representing a user.
+ *
+ * @author Sindre Mehus
+ */
+public class Avatar {
+
+ private int id;
+ private String name;
+ private Date createdDate;
+ private String mimeType;
+ private int width;
+ private int height;
+ private byte[] data;
+
+ public Avatar(int id, String name, Date createdDate, String mimeType, int width, int height, byte[] data) {
+ this.id = id;
+ this.name = name;
+ this.createdDate = createdDate;
+ this.mimeType = mimeType;
+ this.width = width;
+ this.height = height;
+ this.data = data;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Date getCreatedDate() {
+ return createdDate;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/AvatarScheme.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/AvatarScheme.java
new file mode 100644
index 00000000..024dcb24
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/AvatarScheme.java
@@ -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.domain;
+
+/**
+ * Enumeration of avatar schemes.
+ *
+ * @author Sindre Mehus
+ */
+public enum AvatarScheme {
+
+ /**
+ * No avatar should be displayed.
+ */
+ NONE(-1),
+
+ /**
+ * One of the system avatars should be displayed.
+ */
+ SYSTEM(0),
+
+ /**
+ * The custom avatar should be displayed.
+ */
+ CUSTOM(-2);
+
+ private final int code;
+
+ AvatarScheme(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/CacheElement.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/CacheElement.java
new file mode 100644
index 00000000..bb52eff7
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/CacheElement.java
@@ -0,0 +1,65 @@
+/*
+ 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.domain;
+
+/**
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public class CacheElement {
+
+ private final long id;
+ private final int type;
+ private final String key;
+ private final Object value;
+ private final long created;
+
+ public CacheElement(int type, String key, Object value, long created) {
+ this.type = type;
+ this.key = key;
+ this.value = value;
+ this.created = created;
+
+ id = createId(type, key);
+ }
+
+ public static long createId(int type, String key) {
+ return ((long) type << 32) | Math.abs(key.hashCode());
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public long getCreated() {
+ return created;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/CoverArtScheme.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/CoverArtScheme.java
new file mode 100644
index 00000000..91293e9f
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/CoverArtScheme.java
@@ -0,0 +1,48 @@
+/*
+ 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.domain;
+
+/**
+ * Enumeration of cover art schemes. Each value contains a size, which indicates how big the
+ * scaled covert art images should be.
+ *
+ * @author Sindre Mehus
+ * @version $Revision: 1.3 $ $Date: 2005/06/15 18:10:40 $
+ */
+public enum CoverArtScheme {
+
+ OFF(0),
+ SMALL(70),
+ MEDIUM(100),
+ LARGE(150);
+
+ private int size;
+
+ CoverArtScheme(int size) {
+ this.size = size;
+ }
+
+ /**
+ * Returns the covert art size for this scheme.
+ * @return the covert art size for this scheme.
+ */
+ public int getSize() {
+ return size;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/InternetRadio.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/InternetRadio.java
new file mode 100644
index 00000000..ae0c1f67
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/InternetRadio.java
@@ -0,0 +1,168 @@
+/*
+ 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.domain;
+
+import java.util.Date;
+
+/**
+ * Represents an internet radio station.
+ *
+ * @author Sindre Mehus
+ * @version $Revision: 1.2 $ $Date: 2005/12/25 13:48:46 $
+ */
+public class InternetRadio {
+
+ private Integer id;
+ private String name;
+ private String streamUrl;
+ private String homepageUrl;
+ private boolean isEnabled;
+ private Date changed;
+
+ /**
+ * Creates a new internet radio station.
+ *
+ * @param id The system-generated ID.
+ * @param name The user-defined name.
+ * @param streamUrl The stream URL for the station.
+ * @param homepageUrl The home page URL for the station.
+ * @param isEnabled Whether the station is enabled.
+ * @param changed When the corresponding database entry was last changed.
+ */
+ public InternetRadio(Integer id, String name, String streamUrl, String homepageUrl, boolean isEnabled, Date changed) {
+ this.id = id;
+ this.name = name;
+ this.streamUrl = streamUrl;
+ this.homepageUrl = homepageUrl;
+ this.isEnabled = isEnabled;
+ this.changed = changed;
+ }
+
+ /**
+ * Creates a new internet radio station.
+ *
+ * @param name The user-defined name.
+ * @param streamUrl The URL for the station.
+ * @param homepageUrl The home page URL for the station.
+ * @param isEnabled Whether the station is enabled.
+ * @param changed When the corresponding database entry was last changed.
+ */
+ public InternetRadio(String name, String streamUrl, String homepageUrl, boolean isEnabled, Date changed) {
+ this(null, name, streamUrl, homepageUrl, isEnabled, changed);
+ }
+
+ /**
+ * Returns the system-generated ID.
+ *
+ * @return The system-generated ID.
+ */
+ public Integer getId() {
+ return id;
+ }
+
+ /**
+ * Returns the user-defined name.
+ *
+ * @return The user-defined name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the user-defined name.
+ *
+ * @param name The user-defined name.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the stream URL of the radio station.
+ *
+ * @return The stream URL of the radio station.
+ */
+ public String getStreamUrl() {
+ return streamUrl;
+ }
+
+ /**
+ * Sets the stream URL of the radio station.
+ *
+ * @param streamUrl The stream URL of the radio station.
+ */
+ public void setStreamUrl(String streamUrl) {
+ this.streamUrl = streamUrl;
+ }
+
+ /**
+ * Returns the homepage URL of the radio station.
+ *
+ * @return The homepage URL of the radio station.
+ */
+ public String getHomepageUrl() {
+ return homepageUrl;
+ }
+
+ /**
+ * Sets the home page URL of the radio station.
+ *
+ * @param homepageUrl The home page URL of the radio station.
+ */
+ public void setHomepageUrl(String homepageUrl) {
+ this.homepageUrl = homepageUrl;
+ }
+
+ /**
+ * Returns whether the radio station is enabled.
+ *
+ * @return Whether the radio station is enabled.
+ */
+ public boolean isEnabled() {
+ return isEnabled;
+ }
+
+ /**
+ * Sets whether the radio station is enabled.
+ *
+ * @param enabled Whether the radio station is enabled.
+ */
+ public void setEnabled(boolean enabled) {
+ isEnabled = enabled;
+ }
+
+ /**
+ * Returns when the corresponding database entry was last changed.
+ *
+ * @return When the corresponding database entry was last changed.
+ */
+ public Date getChanged() {
+ return changed;
+ }
+
+ /**
+ * Sets when the corresponding database entry was last changed.
+ *
+ * @param changed When the corresponding database entry was last changed.
+ */
+ public void setChanged(Date changed) {
+ this.changed = changed;
+ }
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaFile.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaFile.java
new file mode 100644
index 00000000..4f315028
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaFile.java
@@ -0,0 +1,449 @@
+/*
+ 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.domain;
+
+import net.sourceforge.subsonic.util.FileUtil;
+import org.apache.commons.io.FilenameUtils;
+
+import java.io.File;
+import java.util.Date;
+
+/**
+ * A media file (audio, video or directory) with an assortment of its meta data.
+ *
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public class MediaFile {
+
+ private int id;
+ private String path;
+ private String folder;
+ private MediaType mediaType;
+ private String format;
+ private String title;
+ private String albumName;
+ private String artist;
+ private String albumArtist;
+ private Integer discNumber;
+ private Integer trackNumber;
+ private Integer year;
+ private String genre;
+ private Integer bitRate;
+ private boolean variableBitRate;
+ private Integer durationSeconds;
+ private Long fileSize;
+ private Integer width;
+ private Integer height;
+ private String coverArtPath;
+ private String parentPath;
+ private int playCount;
+ private Date lastPlayed;
+ private String comment;
+ private Date created;
+ private Date changed;
+ private Date lastScanned;
+ private Date starredDate;
+ private Date childrenLastUpdated;
+ private boolean present;
+
+ public MediaFile(int id, String path, String folder, MediaType mediaType, String format, String title,
+ String albumName, String artist, String albumArtist, Integer discNumber, Integer trackNumber, Integer year, String genre, Integer bitRate,
+ boolean variableBitRate, Integer durationSeconds, Long fileSize, Integer width, Integer height, String coverArtPath,
+ String parentPath, int playCount, Date lastPlayed, String comment, Date created, Date changed, Date lastScanned,
+ Date childrenLastUpdated, boolean present) {
+ this.id = id;
+ this.path = path;
+ this.folder = folder;
+ this.mediaType = mediaType;
+ this.format = format;
+ this.title = title;
+ this.albumName = albumName;
+ this.artist = artist;
+ this.albumArtist = albumArtist;
+ this.discNumber = discNumber;
+ this.trackNumber = trackNumber;
+ this.year = year;
+ this.genre = genre;
+ this.bitRate = bitRate;
+ this.variableBitRate = variableBitRate;
+ this.durationSeconds = durationSeconds;
+ this.fileSize = fileSize;
+ this.width = width;
+ this.height = height;
+ this.coverArtPath = coverArtPath;
+ this.parentPath = parentPath;
+ this.playCount = playCount;
+ this.lastPlayed = lastPlayed;
+ this.comment = comment;
+ this.created = created;
+ this.changed = changed;
+ this.lastScanned = lastScanned;
+ this.childrenLastUpdated = childrenLastUpdated;
+ this.present = present;
+ }
+
+ public MediaFile() {
+ }
+
+ 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 getFolder() {
+ return folder;
+ }
+
+ public void setFolder(String folder) {
+ this.folder = folder;
+ }
+
+ public File getFile() {
+ // TODO: Optimize
+ return new File(path);
+ }
+
+ public boolean exists() {
+ return FileUtil.exists(getFile());
+ }
+
+ public MediaType getMediaType() {
+ return mediaType;
+ }
+
+ public void setMediaType(MediaType mediaType) {
+ this.mediaType = mediaType;
+ }
+
+ public boolean isVideo() {
+ return mediaType == MediaType.VIDEO;
+ }
+
+ public boolean isAudio() {
+ return mediaType == MediaType.MUSIC || mediaType == MediaType.AUDIOBOOK || mediaType == MediaType.PODCAST;
+ }
+
+ public String getFormat() {
+ return format;
+ }
+
+ public void setFormat(String format) {
+ this.format = format;
+ }
+
+ public boolean isDirectory() {
+ return !isFile();
+ }
+
+ public boolean isFile() {
+ return mediaType != MediaType.DIRECTORY && mediaType != MediaType.ALBUM;
+ }
+
+ public boolean isAlbum() {
+ return mediaType == MediaType.ALBUM;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getAlbumName() {
+ return albumName;
+ }
+
+ public void setAlbumName(String album) {
+ this.albumName = album;
+ }
+
+ public String getArtist() {
+ return artist;
+ }
+
+ public void setArtist(String artist) {
+ this.artist = artist;
+ }
+
+ public String getAlbumArtist() {
+ return albumArtist;
+ }
+
+ public void setAlbumArtist(String albumArtist) {
+ this.albumArtist = albumArtist;
+ }
+
+ public String getName() {
+ if (isFile()) {
+ return title != null ? title : FilenameUtils.getBaseName(path);
+ }
+
+ return FilenameUtils.getName(path);
+ }
+
+ public Integer getDiscNumber() {
+ return discNumber;
+ }
+
+ public void setDiscNumber(Integer discNumber) {
+ this.discNumber = discNumber;
+ }
+
+ public Integer getTrackNumber() {
+ return trackNumber;
+ }
+
+ public void setTrackNumber(Integer trackNumber) {
+ this.trackNumber = trackNumber;
+ }
+
+ 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;
+ }
+
+ public Integer getBitRate() {
+ return bitRate;
+ }
+
+ public void setBitRate(Integer bitRate) {
+ this.bitRate = bitRate;
+ }
+
+ public boolean isVariableBitRate() {
+ return variableBitRate;
+ }
+
+ public void setVariableBitRate(boolean variableBitRate) {
+ this.variableBitRate = variableBitRate;
+ }
+
+ public Integer getDurationSeconds() {
+ return durationSeconds;
+ }
+
+ public void setDurationSeconds(Integer durationSeconds) {
+ this.durationSeconds = durationSeconds;
+ }
+
+ public String getDurationString() {
+ if (durationSeconds == null) {
+ return null;
+ }
+
+ StringBuilder result = new StringBuilder(8);
+
+ int seconds = durationSeconds;
+
+ int hours = seconds / 3600;
+ seconds -= hours * 3600;
+
+ int minutes = seconds / 60;
+ seconds -= minutes * 60;
+
+ if (hours > 0) {
+ result.append(hours).append(':');
+ if (minutes < 10) {
+ result.append('0');
+ }
+ }
+
+ result.append(minutes).append(':');
+ if (seconds < 10) {
+ result.append('0');
+ }
+ result.append(seconds);
+
+ return result.toString();
+ }
+
+ public Long getFileSize() {
+ return fileSize;
+ }
+
+ public void setFileSize(Long fileSize) {
+ this.fileSize = fileSize;
+ }
+
+ public Integer getWidth() {
+ return width;
+ }
+
+ public void setWidth(Integer width) {
+ this.width = width;
+ }
+
+ public Integer getHeight() {
+ return height;
+ }
+
+ public void setHeight(Integer height) {
+ this.height = height;
+ }
+
+ public String getCoverArtPath() {
+ return coverArtPath;
+ }
+
+ public void setCoverArtPath(String coverArtPath) {
+ this.coverArtPath = coverArtPath;
+ }
+
+
+ public String getParentPath() {
+ return parentPath;
+ }
+
+ public void setParentPath(String parentPath) {
+ this.parentPath = parentPath;
+ }
+
+ public File getParentFile() {
+ return getFile().getParentFile();
+ }
+
+ public int getPlayCount() {
+ return playCount;
+ }
+
+ public void setPlayCount(int playCount) {
+ this.playCount = playCount;
+ }
+
+ public Date getLastPlayed() {
+ return lastPlayed;
+ }
+
+ public void setLastPlayed(Date lastPlayed) {
+ this.lastPlayed = lastPlayed;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+
+ public Date getChanged() {
+ return changed;
+ }
+
+ public void setChanged(Date changed) {
+ this.changed = changed;
+ }
+
+ public Date getLastScanned() {
+ return lastScanned;
+ }
+
+ public void setLastScanned(Date lastScanned) {
+ this.lastScanned = lastScanned;
+ }
+
+ public Date getStarredDate() {
+ return starredDate;
+ }
+
+ public void setStarredDate(Date starredDate) {
+ this.starredDate = starredDate;
+ }
+
+ /**
+ * Returns when the children was last updated in the database.
+ */
+ public Date getChildrenLastUpdated() {
+ return childrenLastUpdated;
+ }
+
+ public void setChildrenLastUpdated(Date childrenLastUpdated) {
+ this.childrenLastUpdated = childrenLastUpdated;
+ }
+
+ public boolean isPresent() {
+ return present;
+ }
+
+ public void setPresent(boolean present) {
+ this.present = present;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MediaFile && ((MediaFile) o).path.equals(path);
+ }
+
+ @Override
+ public int hashCode() {
+ return path.hashCode();
+ }
+
+ public File getCoverArtFile() {
+ // TODO: Optimize
+ return coverArtPath == null ? null : new File(coverArtPath);
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+
+ public static enum MediaType {
+ MUSIC,
+ PODCAST,
+ AUDIOBOOK,
+ VIDEO,
+ DIRECTORY,
+ ALBUM
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaFileComparator.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaFileComparator.java
new file mode 100644
index 00000000..13b5bbbd
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaFileComparator.java
@@ -0,0 +1,99 @@
+/*
+ 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.domain;
+
+import java.util.Comparator;
+
+import static net.sourceforge.subsonic.domain.MediaFile.MediaType.DIRECTORY;
+
+/**
+ * Comparator for sorting media files.
+ */
+public class MediaFileComparator implements Comparator<MediaFile> {
+
+ private final boolean sortAlbumsByYear;
+
+ public MediaFileComparator(boolean sortAlbumsByYear) {
+ this.sortAlbumsByYear = sortAlbumsByYear;
+ }
+
+ public int compare(MediaFile a, MediaFile b) {
+
+ // Directories before files.
+ if (a.isFile() && b.isDirectory()) {
+ return 1;
+ }
+ if (a.isDirectory() && b.isFile()) {
+ return -1;
+ }
+
+ // Non-album directories before album directories.
+ if (a.isAlbum() && b.getMediaType() == DIRECTORY) {
+ return 1;
+ }
+ if (a.getMediaType() == DIRECTORY && b.isAlbum()) {
+ return -1;
+ }
+
+ // Sort albums by year
+ if (sortAlbumsByYear && a.isAlbum() && b.isAlbum()) {
+ int i = nullSafeCompare(a.getYear(), b.getYear(), false);
+ if (i != 0) {
+ return i;
+ }
+ }
+
+ if (a.isDirectory() && b.isDirectory()) {
+ return a.getName().compareToIgnoreCase(b.getName());
+ }
+
+ // Compare by disc and track numbers, if present.
+ Integer trackA = getSortableDiscAndTrackNumber(a);
+ Integer trackB = getSortableDiscAndTrackNumber(b);
+ int i = nullSafeCompare(trackA, trackB, false);
+ if (i != 0) {
+ return i;
+ }
+
+ return a.getName().compareToIgnoreCase(b.getName());
+ }
+
+ private <T extends Comparable<T>> int nullSafeCompare(T a, T b, boolean nullIsSmaller) {
+ if (a == null && b == null) {
+ return 0;
+ }
+ if (a == null) {
+ return nullIsSmaller ? -1 : 1;
+ }
+ if (b == null) {
+ return nullIsSmaller ? 1 : -1;
+ }
+ return a.compareTo(b);
+ }
+
+ private Integer getSortableDiscAndTrackNumber(MediaFile file) {
+ if (file.getTrackNumber() == null) {
+ return null;
+ }
+
+ int discNumber = file.getDiscNumber() == null ? 1 : file.getDiscNumber();
+ return discNumber * 1000 + file.getTrackNumber();
+ }
+}
+
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaLibraryStatistics.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaLibraryStatistics.java
new file mode 100644
index 00000000..c8b0cdd9
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MediaLibraryStatistics.java
@@ -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.domain;
+
+/**
+ * Contains media libaray statistics, including the number of artists, albums and songs.
+ *
+ * @author Sindre Mehus
+ * @version $Revision: 1.1 $ $Date: 2005/11/17 18:29:03 $
+ */
+public class MediaLibraryStatistics {
+
+ private int artistCount;
+ private int albumCount;
+ private int songCount;
+ private long totalLengthInBytes;
+ private long totalDurationInSeconds;
+
+ public MediaLibraryStatistics(int artistCount, int albumCount, int songCount, long totalLengthInBytes, long totalDurationInSeconds) {
+ this.artistCount = artistCount;
+ this.albumCount = albumCount;
+ this.songCount = songCount;
+ this.totalLengthInBytes = totalLengthInBytes;
+ this.totalDurationInSeconds = totalDurationInSeconds;
+ }
+
+ public int getArtistCount() {
+ return artistCount;
+ }
+
+ public int getAlbumCount() {
+ return albumCount;
+ }
+
+ public int getSongCount() {
+ return songCount;
+ }
+
+ public long getTotalLengthInBytes() {
+ return totalLengthInBytes;
+ }
+
+ public long getTotalDurationInSeconds() {
+ return totalDurationInSeconds;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MusicFolder.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MusicFolder.java
new file mode 100644
index 00000000..613b0a7f
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MusicFolder.java
@@ -0,0 +1,148 @@
+/*
+ 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.domain;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * Represents a top level directory in which music or other media is stored.
+ *
+ * @author Sindre Mehus
+ * @version $Revision: 1.1 $ $Date: 2005/11/27 14:32:05 $
+ */
+public class MusicFolder implements Serializable {
+
+ private Integer id;
+ private File path;
+ private String name;
+ private boolean isEnabled;
+ private Date changed;
+
+ /**
+ * Creates a new music folder.
+ *
+ * @param id The system-generated ID.
+ * @param path The path of the music folder.
+ * @param name The user-defined name.
+ * @param enabled Whether the folder is enabled.
+ * @param changed When the corresponding database entry was last changed.
+ */
+ public MusicFolder(Integer id, File path, String name, boolean enabled, Date changed) {
+ this.id = id;
+ this.path = path;
+ this.name = name;
+ isEnabled = enabled;
+ this.changed = changed;
+ }
+
+ /**
+ * Creates a new music folder.
+ *
+ * @param path The path of the music folder.
+ * @param name The user-defined name.
+ * @param enabled Whether the folder is enabled.
+ * @param changed When the corresponding database entry was last changed.
+ */
+ public MusicFolder(File path, String name, boolean enabled, Date changed) {
+ this(null, path, name, enabled, changed);
+ }
+
+ /**
+ * Returns the system-generated ID.
+ *
+ * @return The system-generated ID.
+ */
+ public Integer getId() {
+ return id;
+ }
+
+ /**
+ * Returns the path of the music folder.
+ *
+ * @return The path of the music folder.
+ */
+ public File getPath() {
+ return path;
+ }
+
+ /**
+ * Sets the path of the music folder.
+ *
+ * @param path The path of the music folder.
+ */
+ public void setPath(File path) {
+ this.path = path;
+ }
+
+ /**
+ * Returns the user-defined name.
+ *
+ * @return The user-defined name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the user-defined name.
+ *
+ * @param name The user-defined name.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns whether the folder is enabled.
+ *
+ * @return Whether the folder is enabled.
+ */
+ public boolean isEnabled() {
+ return isEnabled;
+ }
+
+ /**
+ * Sets whether the folder is enabled.
+ *
+ * @param enabled Whether the folder is enabled.
+ */
+ public void setEnabled(boolean enabled) {
+ isEnabled = enabled;
+ }
+
+ /**
+ * Returns when the corresponding database entry was last changed.
+ *
+ * @return When the corresponding database entry was last changed.
+ */
+ public Date getChanged() {
+ return changed;
+ }
+
+ /**
+ * Sets when the corresponding database entry was last changed.
+ *
+ * @param changed When the corresponding database entry was last changed.
+ */
+ public void setChanged(Date changed) {
+ this.changed = changed;
+ }
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MusicIndex.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MusicIndex.java
new file mode 100644
index 00000000..5753fa4d
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/MusicIndex.java
@@ -0,0 +1,167 @@
+/*
+ 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.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * A music index is a mapping from an index string to a list of prefixes. A complete index consists of a list of
+ * <code>MusicIndex</code> instances.<p/>
+ * <p/>
+ * For a normal alphabetical index, such a mapping would typically be <em>"A" -&gt; ["A"]</em>. The index can also be used
+ * to group less frequently used letters, such as <em>"X-&Aring;" -&gt; ["X", "Y", "Z", "&AElig;", "&Oslash;", "&Aring;"]</em>, or to make multiple
+ * indexes for frequently used letters, such as <em>"SA" -&gt; ["SA"]</em> and <em>"SO" -&gt; ["SO"]</em><p/>
+ * <p/>
+ * Clicking on an index in the user interface will typically bring up a list of all music files that are categorized
+ * under that index.
+ *
+ * @author Sindre Mehus
+ */
+public class MusicIndex implements Serializable {
+
+ public static final MusicIndex OTHER = new MusicIndex("#");
+
+ private final String index;
+ private final List<String> prefixes = new ArrayList<String>();
+
+ /**
+ * Creates a new index with the given index string.
+ *
+ * @param index The index string, e.g., "A" or "The".
+ */
+ public MusicIndex(String index) {
+ this.index = index;
+ }
+
+ /**
+ * Adds a prefix to this index. Music files that starts with this prefix will be categorized under this index entry.
+ *
+ * @param prefix The prefix.
+ */
+ public void addPrefix(String prefix) {
+ prefixes.add(prefix);
+ }
+
+ /**
+ * Returns the index name.
+ *
+ * @return The index name.
+ */
+ public String getIndex() {
+ return index;
+ }
+
+ /**
+ * Returns the list of prefixes.
+ *
+ * @return The list of prefixes.
+ */
+ public List<String> getPrefixes() {
+ return prefixes;
+ }
+
+ /**
+ * Returns whether this object is equal to another one.
+ *
+ * @param o Object to compare to.
+ * @return <code>true</code> if, and only if, the other object is a <code>MusicIndex</code> with the same
+ * index name as this one.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof MusicIndex)) {
+ return false;
+ }
+
+ final MusicIndex musicIndex = (MusicIndex) o;
+
+ if (index != null ? !index.equals(musicIndex.index) : musicIndex.index != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a hash code for this object.
+ *
+ * @return A hash code for this object.
+ */
+ @Override
+ public int hashCode() {
+ return (index != null ? index.hashCode() : 0);
+ }
+
+ /**
+ * An artist in an index.
+ */
+ public static class Artist implements Comparable<Artist>, Serializable {
+
+ private final String name;
+ private final String sortableName;
+ private final List<MediaFile> mediaFiles = new ArrayList<MediaFile>();
+
+ public Artist(String name, String sortableName) {
+ this.name = name;
+ this.sortableName = sortableName;
+ }
+
+ public void addMediaFile(MediaFile mediaFile) {
+ mediaFiles.add(mediaFile);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getSortableName() {
+ return sortableName;
+ }
+
+ public List<MediaFile> getMediaFiles() {
+ return mediaFiles;
+ }
+
+ public int compareTo(Artist artist) {
+ return sortableName.compareToIgnoreCase(artist.sortableName);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Artist artist = (Artist) o;
+ return sortableName.equalsIgnoreCase(artist.sortableName);
+ }
+
+ @Override
+ public int hashCode() {
+ return sortableName.hashCode();
+ }
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/NATPMPRouter.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/NATPMPRouter.java
new file mode 100644
index 00000000..ef3821a1
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/NATPMPRouter.java
@@ -0,0 +1,61 @@
+/*
+ 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.domain;
+
+import com.hoodcomputing.natpmp.MapRequestMessage;
+import com.hoodcomputing.natpmp.NatPmpDevice;
+
+/**
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public class NATPMPRouter implements Router {
+
+ private final NatPmpDevice device;
+
+ private NATPMPRouter(NatPmpDevice device) {
+ this.device = device;
+ }
+
+ public static NATPMPRouter findRouter() {
+ try {
+ return new NATPMPRouter(new NatPmpDevice(false));
+ } catch (Exception x) {
+ return null;
+ }
+ }
+
+ public void addPortMapping(int externalPort, int internalPort, int leaseDuration) throws Exception {
+
+ // Use one week if lease duration is "forever".
+ if (leaseDuration == 0) {
+ leaseDuration = 7 * 24 * 3600;
+ }
+
+ MapRequestMessage map = new MapRequestMessage(true, internalPort, externalPort, leaseDuration, null);
+ device.enqueueMessage(map);
+ device.waitUntilQueueEmpty();
+ }
+
+ public void deletePortMapping(int externalPort, int internalPort) throws Exception {
+ MapRequestMessage map = new MapRequestMessage(true, internalPort, externalPort, 0, null);
+ device.enqueueMessage(map);
+ device.waitUntilQueueEmpty();
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PlayQueue.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PlayQueue.java
new file mode 100644
index 00000000..97748f72
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PlayQueue.java
@@ -0,0 +1,417 @@
+/*
+ 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.domain;
+
+import net.sourceforge.subsonic.util.FileUtil;
+import org.apache.commons.lang.StringUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * A playlist is a list of music files that are associated to a remote player.
+ *
+ * @author Sindre Mehus
+ */
+public class PlayQueue {
+
+ private List<MediaFile> files = new ArrayList<MediaFile>();
+ private boolean repeatEnabled;
+ private String name = "(unnamed)";
+ private Status status = Status.PLAYING;
+ private RandomSearchCriteria randomSearchCriteria;
+
+ /**
+ * The index of the current song, or -1 is the end of the playlist is reached.
+ * Note that both the index and the playlist size can be zero.
+ */
+ private int index = 0;
+
+ /**
+ * Used for undo functionality.
+ */
+ private List<MediaFile> filesBackup = new ArrayList<MediaFile>();
+ private int indexBackup = 0;
+
+ /**
+ * Returns the user-defined name of the playlist.
+ *
+ * @return The name of the playlist, or <code>null</code> if no name has been assigned.
+ */
+ public synchronized String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the user-defined name of the playlist.
+ *
+ * @param name The name of the playlist.
+ */
+ public synchronized void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the current song in the playlist.
+ *
+ * @return The current song in the playlist, or <code>null</code> if no current song exists.
+ */
+ public synchronized MediaFile getCurrentFile() {
+ if (index == -1 || index == 0 && size() == 0) {
+ setStatus(Status.STOPPED);
+ return null;
+ } else {
+ MediaFile file = files.get(index);
+
+ // Remove file from playlist if it doesn't exist.
+ if (!file.exists()) {
+ files.remove(index);
+ index = Math.max(0, Math.min(index, size() - 1));
+ return getCurrentFile();
+ }
+
+ return file;
+ }
+ }
+
+ /**
+ * Returns all music files in the playlist.
+ *
+ * @return All music files in the playlist.
+ */
+ public synchronized List<MediaFile> getFiles() {
+ return files;
+ }
+
+ /**
+ * Returns the music file at the given index.
+ *
+ * @param index The index.
+ * @return The music file at the given index.
+ * @throws IndexOutOfBoundsException If the index is out of range.
+ */
+ public synchronized MediaFile getFile(int index) {
+ return files.get(index);
+ }
+
+ /**
+ * Skip to the next song in the playlist.
+ */
+ public synchronized void next() {
+ index++;
+
+ // Reached the end?
+ if (index >= size()) {
+ index = isRepeatEnabled() ? 0 : -1;
+ }
+ }
+
+ /**
+ * Returns the number of songs in the playlists.
+ *
+ * @return The number of songs in the playlists.
+ */
+ public synchronized int size() {
+ return files.size();
+ }
+
+ /**
+ * Returns whether the playlist is empty.
+ *
+ * @return Whether the playlist is empty.
+ */
+ public synchronized boolean isEmpty() {
+ return files.isEmpty();
+ }
+
+ /**
+ * Returns the index of the current song.
+ *
+ * @return The index of the current song, or -1 if the end of the playlist is reached.
+ */
+ public synchronized int getIndex() {
+ return index;
+ }
+
+ /**
+ * Sets the index of the current song.
+ *
+ * @param index The index of the current song.
+ */
+ public synchronized void setIndex(int index) {
+ makeBackup();
+ this.index = Math.max(0, Math.min(index, size() - 1));
+ setStatus(Status.PLAYING);
+ }
+
+ /**
+ * Adds one or more music file to the playlist.
+ *
+ * @param append Whether existing songs in the playlist should be kept.
+ * @param mediaFiles The music files to add.
+ * @throws IOException If an I/O error occurs.
+ */
+ public synchronized void addFiles(boolean append, Iterable<MediaFile> mediaFiles) throws IOException {
+ makeBackup();
+ if (!append) {
+ index = 0;
+ files.clear();
+ }
+ for (MediaFile mediaFile : mediaFiles) {
+ files.add(mediaFile);
+ }
+ setStatus(Status.PLAYING);
+ }
+
+ /**
+ * Convenience method, equivalent to {@link #addFiles(boolean, Iterable)}.
+ */
+ public synchronized void addFiles(boolean append, MediaFile... mediaFiles) throws IOException {
+ addFiles(append, Arrays.asList(mediaFiles));
+ }
+
+ /**
+ * Removes the music file at the given index.
+ *
+ * @param index The playlist index.
+ */
+ public synchronized void removeFileAt(int index) {
+ makeBackup();
+ index = Math.max(0, Math.min(index, size() - 1));
+ if (this.index > index) {
+ this.index--;
+ }
+ files.remove(index);
+
+ if (index != -1) {
+ this.index = Math.max(0, Math.min(this.index, size() - 1));
+ }
+ }
+
+ /**
+ * Clears the playlist.
+ */
+ public synchronized void clear() {
+ makeBackup();
+ files.clear();
+ index = 0;
+ }
+
+ /**
+ * Shuffles the playlist.
+ */
+ public synchronized void shuffle() {
+ makeBackup();
+ MediaFile currentFile = getCurrentFile();
+ Collections.shuffle(files);
+ if (currentFile != null) {
+ index = files.indexOf(currentFile);
+ }
+ }
+
+ /**
+ * Sorts the playlist according to the given sort order.
+ */
+ public synchronized void sort(final SortOrder sortOrder) {
+ makeBackup();
+ MediaFile currentFile = getCurrentFile();
+
+ Comparator<MediaFile> comparator = new Comparator<MediaFile>() {
+ public int compare(MediaFile a, MediaFile b) {
+ switch (sortOrder) {
+ case TRACK:
+ Integer trackA = a.getTrackNumber();
+ Integer trackB = b.getTrackNumber();
+ if (trackA == null) {
+ trackA = 0;
+ }
+ if (trackB == null) {
+ trackB = 0;
+ }
+ return trackA.compareTo(trackB);
+
+ case ARTIST:
+ String artistA = StringUtils.trimToEmpty(a.getArtist());
+ String artistB = StringUtils.trimToEmpty(b.getArtist());
+ return artistA.compareTo(artistB);
+
+ case ALBUM:
+ String albumA = StringUtils.trimToEmpty(a.getAlbumName());
+ String albumB = StringUtils.trimToEmpty(b.getAlbumName());
+ return albumA.compareTo(albumB);
+ default:
+ return 0;
+ }
+ }
+ };
+
+ Collections.sort(files, comparator);
+ if (currentFile != null) {
+ index = files.indexOf(currentFile);
+ }
+ }
+
+ /**
+ * Moves the song at the given index one step up.
+ *
+ * @param index The playlist index.
+ */
+ public synchronized void moveUp(int index) {
+ makeBackup();
+ if (index <= 0 || index >= size()) {
+ return;
+ }
+ Collections.swap(files, index, index - 1);
+
+ if (this.index == index) {
+ this.index--;
+ } else if (this.index == index - 1) {
+ this.index++;
+ }
+ }
+
+ /**
+ * Moves the song at the given index one step down.
+ *
+ * @param index The playlist index.
+ */
+ public synchronized void moveDown(int index) {
+ makeBackup();
+ if (index < 0 || index >= size() - 1) {
+ return;
+ }
+ Collections.swap(files, index, index + 1);
+
+ if (this.index == index) {
+ this.index++;
+ } else if (this.index == index + 1) {
+ this.index--;
+ }
+ }
+
+ /**
+ * Returns whether the playlist is repeating.
+ *
+ * @return Whether the playlist is repeating.
+ */
+ public synchronized boolean isRepeatEnabled() {
+ return repeatEnabled;
+ }
+
+ /**
+ * Sets whether the playlist is repeating.
+ *
+ * @param repeatEnabled Whether the playlist is repeating.
+ */
+ public synchronized void setRepeatEnabled(boolean repeatEnabled) {
+ this.repeatEnabled = repeatEnabled;
+ }
+
+ /**
+ * Revert the last operation.
+ */
+ public synchronized void undo() {
+ List<MediaFile> filesTmp = new ArrayList<MediaFile>(files);
+ int indexTmp = index;
+
+ index = indexBackup;
+ files = filesBackup;
+
+ indexBackup = indexTmp;
+ filesBackup = filesTmp;
+ }
+
+ /**
+ * Returns the playlist status.
+ *
+ * @return The playlist status.
+ */
+ public synchronized Status getStatus() {
+ return status;
+ }
+
+ /**
+ * Sets the playlist status.
+ *
+ * @param status The playlist status.
+ */
+ public synchronized void setStatus(Status status) {
+ this.status = status;
+ if (index == -1) {
+ index = Math.max(0, Math.min(index, size() - 1));
+ }
+ }
+
+ /**
+ * Returns the criteria used to generate this random playlist.
+ *
+ * @return The search criteria, or <code>null</code> if this is not a random playlist.
+ */
+ public synchronized RandomSearchCriteria getRandomSearchCriteria() {
+ return randomSearchCriteria;
+ }
+
+ /**
+ * Sets the criteria used to generate this random playlist.
+ *
+ * @param randomSearchCriteria The search criteria, or <code>null</code> if this is not a random playlist.
+ */
+ public synchronized void setRandomSearchCriteria(RandomSearchCriteria randomSearchCriteria) {
+ this.randomSearchCriteria = randomSearchCriteria;
+ }
+
+ /**
+ * Returns the total length in bytes.
+ *
+ * @return The total length in bytes.
+ */
+ public synchronized long length() {
+ long length = 0;
+ for (MediaFile mediaFile : files) {
+ length += mediaFile.getFileSize();
+ }
+ return length;
+ }
+
+ private void makeBackup() {
+ filesBackup = new ArrayList<MediaFile>(files);
+ indexBackup = index;
+ }
+
+ /**
+ * Playlist status.
+ */
+ public enum Status {
+ PLAYING,
+ STOPPED
+ }
+
+ /**
+ * Playlist sort order.
+ */
+ public enum SortOrder {
+ TRACK,
+ ARTIST,
+ ALBUM
+ }
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Player.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Player.java
new file mode 100644
index 00000000..e1780936
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Player.java
@@ -0,0 +1,338 @@
+/*
+ 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.domain;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.Date;
+
+/**
+ * Represens a remote player. A player has a unique ID, a user-defined name, a logged-on user,
+ * miscellaneous identifiers, and an associated playlist.
+ *
+ * @author Sindre Mehus
+ */
+public class Player {
+
+ private String id;
+ private String name;
+ private PlayerTechnology technology = PlayerTechnology.WEB;
+ private String clientId;
+ private String type;
+ private String username;
+ private String ipAddress;
+ private boolean isDynamicIp = true;
+ private boolean isAutoControlEnabled = true;
+ private Date lastSeen;
+ private CoverArtScheme coverArtScheme = CoverArtScheme.MEDIUM;
+ private TranscodeScheme transcodeScheme = TranscodeScheme.OFF;
+ private PlayQueue playQueue;
+
+ /**
+ * Returns the player ID.
+ *
+ * @return The player ID.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Sets the player ID.
+ *
+ * @param id The player ID.
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the user-defined player name.
+ *
+ * @return The user-defined player name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the user-defined player name.
+ *
+ * @param name The user-defined player name.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the player "technology", e.g., web, external or jukebox.
+ *
+ * @return The player technology.
+ */
+ public PlayerTechnology getTechnology() {
+ return technology;
+ }
+
+ /**
+ * Returns the third-party client ID (used if this player is managed over the
+ * Subsonic REST API).
+ *
+ * @return The client ID.
+ */
+ public String getClientId() {
+ return clientId;
+ }
+
+ /**
+ * Sets the third-party client ID (used if this player is managed over the
+ * Subsonic REST API).
+ *
+ * @param clientId The client ID.
+ */
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ /**
+ * Sets the player "technology", e.g., web, external or jukebox.
+ *
+ * @param technology The player technology.
+ */
+ public void setTechnology(PlayerTechnology technology) {
+ this.technology = technology;
+ }
+
+ public boolean isJukebox() {
+ return technology == PlayerTechnology.JUKEBOX;
+ }
+
+ public boolean isExternal() {
+ return technology == PlayerTechnology.EXTERNAL;
+ }
+
+ public boolean isExternalWithPlaylist() {
+ return technology == PlayerTechnology.EXTERNAL_WITH_PLAYLIST;
+ }
+
+ public boolean isWeb() {
+ return technology == PlayerTechnology.WEB;
+ }
+
+ /**
+ * Returns the player type, e.g., WinAmp, iTunes.
+ *
+ * @return The player type.
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Sets the player type, e.g., WinAmp, iTunes.
+ *
+ * @param type The player type.
+ */
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ /**
+ * Returns the logged-in user.
+ *
+ * @return The logged-in user.
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * Sets the logged-in username.
+ *
+ * @param username The logged-in username.
+ */
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ /**
+ * Returns whether the player is automatically started.
+ *
+ * @return Whether the player is automatically started.
+ */
+ public boolean isAutoControlEnabled() {
+ return isAutoControlEnabled;
+ }
+
+ /**
+ * Sets whether the player is automatically started.
+ *
+ * @param isAutoControlEnabled Whether the player is automatically started.
+ */
+ public void setAutoControlEnabled(boolean isAutoControlEnabled) {
+ this.isAutoControlEnabled = isAutoControlEnabled;
+ }
+
+ /**
+ * Returns the time when the player was last seen.
+ *
+ * @return The time when the player was last seen.
+ */
+ public Date getLastSeen() {
+ return lastSeen;
+ }
+
+ /**
+ * Sets the time when the player was last seen.
+ *
+ * @param lastSeen The time when the player was last seen.
+ */
+ public void setLastSeen(Date lastSeen) {
+ this.lastSeen = lastSeen;
+ }
+
+ /**
+ * Returns the cover art scheme.
+ *
+ * @return The cover art scheme.
+ */
+ public CoverArtScheme getCoverArtScheme() {
+ return coverArtScheme;
+ }
+
+ /**
+ * Sets the cover art scheme.
+ *
+ * @param coverArtScheme The cover art scheme.
+ */
+ public void setCoverArtScheme(CoverArtScheme coverArtScheme) {
+ this.coverArtScheme = coverArtScheme;
+ }
+
+ /**
+ * Returns the transcode scheme.
+ *
+ * @return The transcode scheme.
+ */
+ public TranscodeScheme getTranscodeScheme() {
+ return transcodeScheme;
+ }
+
+ /**
+ * Sets the transcode scheme.
+ *
+ * @param transcodeScheme The transcode scheme.
+ */
+ public void setTranscodeScheme(TranscodeScheme transcodeScheme) {
+ this.transcodeScheme = transcodeScheme;
+ }
+
+ /**
+ * Returns the IP address of the player.
+ *
+ * @return The IP address of the player.
+ */
+ public String getIpAddress() {
+ return ipAddress;
+ }
+
+ /**
+ * Sets the IP address of the player.
+ *
+ * @param ipAddress The IP address of the player.
+ */
+ public void setIpAddress(String ipAddress) {
+ this.ipAddress = ipAddress;
+ }
+
+ /**
+ * Returns whether this player has a dynamic IP address.
+ *
+ * @return Whether this player has a dynamic IP address.
+ */
+ public boolean isDynamicIp() {
+ return isDynamicIp;
+ }
+
+ /**
+ * Sets whether this player has a dynamic IP address.
+ *
+ * @param dynamicIp Whether this player has a dynamic IP address.
+ */
+ public void setDynamicIp(boolean dynamicIp) {
+ isDynamicIp = dynamicIp;
+ }
+
+ /**
+ * Returns the player's playlist.
+ *
+ * @return The player's playlist
+ */
+ public PlayQueue getPlayQueue() {
+ return playQueue;
+ }
+
+ /**
+ * Sets the player's playlist.
+ *
+ * @param playQueue The player's playlist.
+ */
+ public void setPlayQueue(PlayQueue playQueue) {
+ this.playQueue = playQueue;
+ }
+
+ /**
+ * Returns a long description of the player, e.g., <code>Player 3 [admin]</code>
+ *
+ * @return A long description of the player.
+ */
+ public String getDescription() {
+ StringBuilder builder = new StringBuilder();
+ if (name != null) {
+ builder.append(name);
+ } else {
+ builder.append("Player ").append(id);
+ }
+
+ builder.append(" [").append(username).append(']');
+ return builder.toString();
+ }
+
+ /**
+ * Returns a short description of the player, e.g., <code>Player 3</code>
+ *
+ * @return A short description of the player.
+ */
+ public String getShortDescription() {
+ if (StringUtils.isNotBlank(name)) {
+ return name;
+ }
+ return "Player " + id;
+ }
+
+ /**
+ * Returns a string representation of the player.
+ *
+ * @return A string representation of the player.
+ * @see #getDescription()
+ */
+ @Override
+ public String toString() {
+ return getDescription();
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PlayerTechnology.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PlayerTechnology.java
new file mode 100644
index 00000000..5ba3ff71
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PlayerTechnology.java
@@ -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.domain;
+
+/**
+ * Enumeration of player technologies.
+ *
+ * @author Sindre Mehus
+ */
+public enum PlayerTechnology {
+
+ /**
+ * Plays music directly in the web browser using the integrated Flash player.
+ */
+ WEB,
+
+ /**
+ * Plays music in an external player, such as WinAmp or Windows Media Player.
+ */
+ EXTERNAL,
+
+ /**
+ * Same as above, but the playlist is managed by the player, rather than the Subsonic server.
+ * In this mode, skipping within songs is possible.
+ */
+ EXTERNAL_WITH_PLAYLIST,
+
+ /**
+ * Plays music directly on the audio device of the Subsonic server.
+ */
+ JUKEBOX
+
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Playlist.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Playlist.java
new file mode 100644
index 00000000..80555ec7
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Playlist.java
@@ -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.domain;
+
+import net.sourceforge.subsonic.util.StringUtil;
+
+import java.util.Date;
+
+/**
+ * @author Sindre Mehus
+ */
+public class Playlist {
+
+ private int id;
+ private String username;
+ private boolean isPublic;
+ private String name;
+ private String comment;
+ private int fileCount;
+ private int durationSeconds;
+ private Date created;
+ private Date changed;
+ private String importedFrom;
+
+ public Playlist() {
+ }
+
+ public Playlist(int id, String username, boolean isPublic, String name, String comment, int fileCount,
+ int durationSeconds, Date created, Date changed, String importedFrom) {
+ this.id = id;
+ this.username = username;
+ this.isPublic = isPublic;
+ this.name = name;
+ this.comment = comment;
+ this.fileCount = fileCount;
+ this.durationSeconds = durationSeconds;
+ this.created = created;
+ this.changed = changed;
+ this.importedFrom = importedFrom;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public boolean isPublic() {
+ return isPublic;
+ }
+
+ public void setPublic(boolean isPublic) {
+ this.isPublic = isPublic;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public int getFileCount() {
+ return fileCount;
+ }
+
+ public void setFileCount(int fileCount) {
+ this.fileCount = fileCount;
+ }
+
+ public int getDurationSeconds() {
+ return durationSeconds;
+ }
+
+ public void setDurationSeconds(int durationSeconds) {
+ this.durationSeconds = durationSeconds;
+ }
+
+ public String getDurationAsString() {
+ return StringUtil.formatDuration(durationSeconds);
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+
+ public Date getChanged() {
+ return changed;
+ }
+
+ public void setChanged(Date changed) {
+ this.changed = changed;
+ }
+
+ public String getImportedFrom() {
+ return importedFrom;
+ }
+
+ public void setImportedFrom(String importedFrom) {
+ this.importedFrom = importedFrom;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastChannel.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastChannel.java
new file mode 100644
index 00000000..1127a5cb
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastChannel.java
@@ -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.domain;
+
+import net.sourceforge.subsonic.util.StringUtil;
+
+/**
+ * A Podcast channel. Each channel contain several episodes.
+ *
+ * @author Sindre Mehus
+ * @see PodcastEpisode
+ */
+public class PodcastChannel {
+
+ private Integer id;
+ private String url;
+ private String title;
+ private String description;
+ private PodcastStatus status;
+ private String errorMessage;
+
+ public PodcastChannel(Integer id, String url, String title, String description,
+ PodcastStatus status, String errorMessage) {
+ this.id = id;
+ this.url = url;
+ this.title = StringUtil.removeMarkup(title);
+ this.description = StringUtil.removeMarkup(description);
+ this.status = status;
+ this.errorMessage = errorMessage;
+ }
+
+ public PodcastChannel(String url) {
+ this.url = url;
+ status = PodcastStatus.NEW;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public PodcastStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(PodcastStatus status) {
+ this.status = status;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastEpisode.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastEpisode.java
new file mode 100644
index 00000000..b5a835cb
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastEpisode.java
@@ -0,0 +1,172 @@
+/*
+ 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.domain;
+
+import java.util.Date;
+
+import net.sourceforge.subsonic.util.StringUtil;
+
+/**
+ * A Podcast episode belonging to a channel.
+ *
+ * @author Sindre Mehus
+ * @see PodcastChannel
+ */
+public class PodcastEpisode {
+
+ private Integer id;
+ private Integer mediaFileId;
+ private Integer channelId;
+ private String url;
+ private String path;
+ private String title;
+ private String description;
+ private Date publishDate;
+ private String duration;
+ private Long bytesTotal;
+ private Long bytesDownloaded;
+ private PodcastStatus status;
+ private String errorMessage;
+
+ public PodcastEpisode(Integer id, Integer channelId, String url, String path, String title,
+ String description, Date publishDate, String duration, Long length, Long bytesDownloaded,
+ PodcastStatus status, String errorMessage) {
+ this.id = id;
+ this.channelId = channelId;
+ this.url = url;
+ this.path = path;
+ this.title = StringUtil.removeMarkup(title);
+ this.description = StringUtil.removeMarkup(description);
+ this.publishDate = publishDate;
+ this.duration = duration;
+ this.bytesTotal = length;
+ this.bytesDownloaded = bytesDownloaded;
+ this.status = status;
+ this.errorMessage = errorMessage;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public Integer getChannelId() {
+ return channelId;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Date getPublishDate() {
+ return publishDate;
+ }
+
+ public void setPublishDate(Date publishDate) {
+ this.publishDate = publishDate;
+ }
+
+ public String getDuration() {
+ return duration;
+ }
+
+ public void setDuration(String duration) {
+ this.duration = duration;
+ }
+
+ public Long getBytesTotal() {
+ return bytesTotal;
+ }
+
+ public void setBytesTotal(Long bytesTotal) {
+ this.bytesTotal = bytesTotal;
+ }
+
+ public Long getBytesDownloaded() {
+ return bytesDownloaded;
+ }
+
+ public Double getCompletionRate() {
+ if (bytesTotal == null || bytesTotal == 0) {
+ return null;
+ }
+ if (bytesDownloaded == null) {
+ return 0.0;
+ }
+
+ double d = bytesDownloaded;
+ double t = bytesTotal;
+ return d / t;
+ }
+
+ public void setBytesDownloaded(Long bytesDownloaded) {
+ this.bytesDownloaded = bytesDownloaded;
+ }
+
+ public PodcastStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(PodcastStatus status) {
+ this.status = status;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ public Integer getMediaFileId() {
+ return mediaFileId;
+ }
+
+ public void setMediaFileId(Integer mediaFileId) {
+ this.mediaFileId = mediaFileId;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastStatus.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastStatus.java
new file mode 100644
index 00000000..57cad155
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/PodcastStatus.java
@@ -0,0 +1,29 @@
+/*
+ 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.domain;
+
+/**
+ * Enumeration of statuses for {@link PodcastChannel} and
+ * {@link PodcastEpisode}.
+ *
+ * @author Sindre Mehus
+ */
+public enum PodcastStatus {
+ NEW, DOWNLOADING, COMPLETED, ERROR, DELETED, SKIPPED
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/RandomSearchCriteria.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/RandomSearchCriteria.java
new file mode 100644
index 00000000..d52cec39
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/RandomSearchCriteria.java
@@ -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.domain;
+
+/**
+ * Defines criteria used when generating random playlists.
+ *
+ * @author Sindre Mehus
+ * @see net.sourceforge.subsonic.service.SearchService#getRandomSongs
+ */
+public class RandomSearchCriteria {
+ private final int count;
+ private final String genre;
+ private final Integer fromYear;
+ private final Integer toYear;
+ private final Integer musicFolderId;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param count Maximum number of songs to return.
+ * @param genre Only return songs of the given genre. May be <code>null</code>.
+ * @param fromYear Only return songs released after (or in) this year. May be <code>null</code>.
+ * @param toYear Only return songs released before (or in) this year. May be <code>null</code>.
+ * @param musicFolderId Only return songs from this music folder. May be <code>null</code>.
+ */
+ public RandomSearchCriteria(int count, String genre, Integer fromYear, Integer toYear, Integer musicFolderId) {
+ this.count = count;
+ this.genre = genre;
+ this.fromYear = fromYear;
+ this.toYear = toYear;
+ this.musicFolderId = musicFolderId;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public String getGenre() {
+ return genre;
+ }
+
+ public Integer getFromYear() {
+ return fromYear;
+ }
+
+ public Integer getToYear() {
+ return toYear;
+ }
+
+ public Integer getMusicFolderId() {
+ return musicFolderId;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Router.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Router.java
new file mode 100644
index 00000000..ede9d19e
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Router.java
@@ -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.domain;
+
+/**
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public interface Router {
+
+ /**
+ * Adds a NAT entry on the UPNP device.
+ *
+ * @param externalPort The external port to open on the UPNP device an map on the internal client.
+ * @param internalPort The internal client port where data should be redirected.
+ * @param leaseDuration Seconds the lease duration in seconds, or 0 for an infinite time.
+ */
+ void addPortMapping(int externalPort, int internalPort, int leaseDuration) throws Exception;
+
+ /**
+ * Deletes a NAT entry on the UPNP device.
+ *
+ * @param externalPort The external port of the NAT entry to delete.
+ * @param internalPort The internal port of the NAT entry to delete.
+ */
+ void deletePortMapping(int externalPort, int internalPort) throws Exception;
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SBBIRouter.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SBBIRouter.java
new file mode 100644
index 00000000..a639e665
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SBBIRouter.java
@@ -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.domain;
+
+import net.sbbi.upnp.impls.InternetGatewayDevice;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+/**
+ * @author Sindre Mehus
+ */
+public class SBBIRouter implements Router {
+
+ // The timeout in milliseconds for finding a router device.
+ private static final int DISCOVERY_TIMEOUT = 3000;
+
+ private final InternetGatewayDevice device;
+
+ private SBBIRouter(InternetGatewayDevice device) {
+ this.device = device;
+ }
+
+ public static SBBIRouter findRouter() throws Exception {
+ InternetGatewayDevice[] devices;
+ try {
+ devices = InternetGatewayDevice.getDevices(DISCOVERY_TIMEOUT);
+ } catch (IOException e) {
+ throw new Exception("Could not find router", e);
+ }
+
+ if (devices == null || devices.length == 0) {
+ return null;
+ }
+
+ return new SBBIRouter(devices[0]);
+ }
+
+ public void addPortMapping(int externalPort, int internalPort, int leaseDuration) throws Exception {
+ String localIp = InetAddress.getLocalHost().getHostAddress();
+ device.addPortMapping("Subsonic", null, internalPort, externalPort, localIp, leaseDuration, "TCP");
+ }
+
+ public void deletePortMapping(int externalPort, int internal) throws Exception {
+ device.deletePortMapping(null, externalPort, "TCP");
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SearchCriteria.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SearchCriteria.java
new file mode 100644
index 00000000..f06a6512
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SearchCriteria.java
@@ -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.domain;
+
+import net.sourceforge.subsonic.service.MediaScannerService;
+import net.sourceforge.subsonic.service.SearchService;
+
+/**
+ * Defines criteria used when searching.
+ *
+ * @author Sindre Mehus
+ * @see SearchService#search
+ */
+public class SearchCriteria {
+
+ private String query;
+ private int offset;
+ private int count;
+
+ public void setQuery(String query) {
+ this.query = query;
+ }
+
+ public String getQuery() {
+ return query;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SearchResult.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SearchResult.java
new file mode 100644
index 00000000..bf4b370a
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/SearchResult.java
@@ -0,0 +1,69 @@
+/*
+ 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.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sourceforge.subsonic.service.MediaScannerService;
+import net.sourceforge.subsonic.service.SearchService;
+
+/**
+ * The outcome of a search.
+ *
+ * @author Sindre Mehus
+ * @see SearchService#search
+ */
+public class SearchResult {
+
+ private final List<MediaFile> mediaFiles = new ArrayList<MediaFile>();
+ private final List<Artist> artists = new ArrayList<Artist>();
+ private final List<Album> albums = new ArrayList<Album>();
+
+ private int offset;
+ private int totalHits;
+
+ public List<MediaFile> getMediaFiles() {
+ return mediaFiles;
+ }
+
+ public List<Artist> getArtists() {
+ return artists;
+ }
+
+ public List<Album> getAlbums() {
+ return albums;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ public int getTotalHits() {
+ return totalHits;
+ }
+
+ public void setTotalHits(int totalHits) {
+ this.totalHits = totalHits;
+ }
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Share.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Share.java
new file mode 100644
index 00000000..7494769b
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Share.java
@@ -0,0 +1,100 @@
+package net.sourceforge.subsonic.domain;
+
+import java.util.Date;
+
+/**
+ * A collection of media files that is shared with someone, and accessible via a direct URL.
+ *
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public class Share {
+
+ private int id;
+ private String name;
+ private String description;
+ private String username;
+ private Date created;
+ private Date expires;
+ private Date lastVisited;
+ private int visitCount;
+
+ public Share() {
+ }
+
+ public Share(int id, String name, String description, String username, Date created,
+ Date expires, Date lastVisited, int visitCount) {
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ this.username = username;
+ this.created = created;
+ this.expires = expires;
+ this.lastVisited = lastVisited;
+ this.visitCount = visitCount;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ 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 getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+
+ public Date getExpires() {
+ return expires;
+ }
+
+ public void setExpires(Date expires) {
+ this.expires = expires;
+ }
+
+ public Date getLastVisited() {
+ return lastVisited;
+ }
+
+ public void setLastVisited(Date lastVisited) {
+ this.lastVisited = lastVisited;
+ }
+
+ public int getVisitCount() {
+ return visitCount;
+ }
+
+ public void setVisitCount(int visitCount) {
+ this.visitCount = visitCount;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Theme.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Theme.java
new file mode 100644
index 00000000..f8bd66bd
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Theme.java
@@ -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.domain;
+
+/**
+ * Contains the ID and name for a theme.
+ *
+ * @author Sindre Mehus
+ */
+public class Theme {
+ private String id;
+ private String name;
+
+ public Theme(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/TranscodeScheme.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/TranscodeScheme.java
new file mode 100644
index 00000000..f45b452a
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/TranscodeScheme.java
@@ -0,0 +1,104 @@
+/*
+ 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.domain;
+
+/**
+ * Enumeration of transcoding schemes. Transcoding is the process of
+ * converting an audio stream to a lower bit rate.
+ *
+ * @author Sindre Mehus
+ */
+public enum TranscodeScheme {
+
+ OFF(0),
+ MAX_32(32),
+ MAX_40(40),
+ MAX_48(48),
+ MAX_56(56),
+ MAX_64(64),
+ MAX_80(80),
+ MAX_96(96),
+ MAX_112(112),
+ MAX_128(128),
+ MAX_160(160),
+ MAX_192(192),
+ MAX_224(224),
+ MAX_256(256),
+ MAX_320(320);
+
+ private int maxBitRate;
+
+ TranscodeScheme(int maxBitRate) {
+ this.maxBitRate = maxBitRate;
+ }
+
+ /**
+ * Returns the maximum bit rate for this transcoding scheme.
+ *
+ * @return The maximum bit rate for this transcoding scheme.
+ */
+ public int getMaxBitRate() {
+ return maxBitRate;
+ }
+
+ /**
+ * Returns the strictest transcode scheme (i.e., the scheme with the lowest max bitrate).
+ *
+ * @param other The other transcode scheme. May be <code>null</code>, in which case 'this' is returned.
+ * @return The strictest scheme.
+ */
+ public TranscodeScheme strictest(TranscodeScheme other) {
+ if (other == null || other == TranscodeScheme.OFF) {
+ return this;
+ }
+
+ if (this == TranscodeScheme.OFF) {
+ return other;
+ }
+
+ return maxBitRate < other.maxBitRate ? this : other;
+ }
+
+ /**
+ * Returns a human-readable string representation of this object.
+ *
+ * @return A human-readable string representation of this object.
+ */
+ public String toString() {
+ if (this == OFF) {
+ return "No limit";
+ }
+ return "" + getMaxBitRate() + " Kbps";
+ }
+
+ /**
+ * Returns the enum constant which corresponds to the given max bit rate.
+ *
+ * @param maxBitRate The max bit rate.
+ * @return The corresponding enum, or <code>null</code> if not found.
+ */
+ public static TranscodeScheme valueOf(int maxBitRate) {
+ for (TranscodeScheme scheme : values()) {
+ if (scheme.getMaxBitRate() == maxBitRate) {
+ return scheme;
+ }
+ }
+ return null;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Transcoding.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Transcoding.java
new file mode 100644
index 00000000..57c8316f
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Transcoding.java
@@ -0,0 +1,221 @@
+/*
+ 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.domain;
+
+import net.sourceforge.subsonic.util.StringUtil;
+
+/**
+ * Contains the configuration for a transcoding, i.e., a specification of how a given media format
+ * should be converted to another.
+ * <br/>
+ * A transcoding may contain up to three steps. Typically you need to convert in several steps, for
+ * instance from OGG to WAV to MP3.
+ *
+ * @author Sindre Mehus
+ */
+public class Transcoding {
+
+ private Integer id;
+ private String name;
+ private String sourceFormats;
+ private String targetFormat;
+ private String step1;
+ private String step2;
+ private String step3;
+ private boolean defaultActive;
+
+ /**
+ * Creates a new transcoding specification.
+ *
+ * @param id The system-generated ID.
+ * @param name The user-defined name.
+ * @param sourceFormats The source formats, e.g., "ogg wav aac".
+ * @param targetFormat The target format, e.g., "mp3".
+ * @param step1 The command to execute in step 1.
+ * @param step2 The command to execute in step 2.
+ * @param step3 The command to execute in step 3.
+ * @param defaultActive Whether the transcoding should be automatically activated for all players.
+ */
+ public Transcoding(Integer id, String name, String sourceFormats, String targetFormat, String step1,
+ String step2, String step3, boolean defaultActive) {
+ this.id = id;
+ this.name = name;
+ this.sourceFormats = sourceFormats;
+ this.targetFormat = targetFormat;
+ this.step1 = step1;
+ this.step2 = step2;
+ this.step3 = step3;
+ this.defaultActive = defaultActive;
+ }
+
+ /**
+ * Returns the system-generated ID.
+ *
+ * @return The system-generated ID.
+ */
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the user-defined name.
+ *
+ * @return The user-defined name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the user-defined name.
+ *
+ * @param name The user-defined name.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the source format, e.g., "ogg wav aac".
+ *
+ * @return The source format, e.g., "ogg wav aac".
+ */
+ public String getSourceFormats() {
+ return sourceFormats;
+ }
+
+ public String[] getSourceFormatsAsArray() {
+ return StringUtil.split(sourceFormats);
+ }
+
+ /**
+ * Sets the source formats, e.g., "ogg wav aac".
+ *
+ * @param sourceFormats The source formats, e.g., "ogg wav aac".
+ */
+ public void setSourceFormats(String sourceFormats) {
+ this.sourceFormats = sourceFormats;
+ }
+
+ /**
+ * Returns the target format, e.g., mp3.
+ *
+ * @return The target format, e.g., mp3.
+ */
+ public String getTargetFormat() {
+ return targetFormat;
+ }
+
+ /**
+ * Sets the target format, e.g., mp3.
+ *
+ * @param targetFormat The target format, e.g., mp3.
+ */
+ public void setTargetFormat(String targetFormat) {
+ this.targetFormat = targetFormat;
+ }
+
+ /**
+ * Returns the command to execute in step 1.
+ *
+ * @return The command to execute in step 1.
+ */
+ public String getStep1() {
+ return step1;
+ }
+
+ /**
+ * Sets the command to execute in step 1.
+ *
+ * @param step1 The command to execute in step 1.
+ */
+ public void setStep1(String step1) {
+ this.step1 = step1;
+ }
+
+ /**
+ * Returns the command to execute in step 2.
+ *
+ * @return The command to execute in step 2.
+ */
+ public String getStep2() {
+ return step2;
+ }
+
+ /**
+ * Sets the command to execute in step 2.
+ *
+ * @param step2 The command to execute in step 2.
+ */
+ public void setStep2(String step2) {
+ this.step2 = step2;
+ }
+
+ /**
+ * Returns the command to execute in step 3.
+ *
+ * @return The command to execute in step 3.
+ */
+ public String getStep3() {
+ return step3;
+ }
+
+ /**
+ * Sets the command to execute in step 3.
+ *
+ * @param step3 The command to execute in step 3.
+ */
+ public void setStep3(String step3) {
+ this.step3 = step3;
+ }
+
+ /**
+ * Returns whether the transcoding should be automatically activated for all players
+ */
+ public boolean isDefaultActive() {
+ return defaultActive;
+ }
+
+ /**
+ * Sets whether the transcoding should be automatically activated for all players
+ */
+ public void setDefaultActive(boolean defaultActive) {
+ this.defaultActive = defaultActive;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Transcoding that = (Transcoding) o;
+ return !(id != null ? !id.equals(that.id) : that.id != null);
+ }
+
+ public int hashCode() {
+ return (id != null ? id.hashCode() : 0);
+ }
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/TransferStatus.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/TransferStatus.java
new file mode 100644
index 00000000..06930ae3
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/TransferStatus.java
@@ -0,0 +1,303 @@
+/*
+ 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.domain;
+
+import java.io.File;
+
+import net.sourceforge.subsonic.util.BoundedList;
+
+/**
+ * Status for a single transfer (stream, download or upload).
+ *
+ * @author Sindre Mehus
+ */
+public class TransferStatus {
+
+ private static final int HISTORY_LENGTH = 200;
+ private static final long SAMPLE_INTERVAL_MILLIS = 5000;
+
+ private Player player;
+ private File file;
+ private long bytesTransfered;
+ private long bytesSkipped;
+ private long bytesTotal;
+ private final SampleHistory history = new SampleHistory();
+ private boolean terminated;
+ private boolean active = true;
+
+ /**
+ * Return the number of bytes transferred.
+ *
+ * @return The number of bytes transferred.
+ */
+ public synchronized long getBytesTransfered() {
+ return bytesTransfered;
+ }
+
+ /**
+ * Adds the given byte count to the total number of bytes transferred.
+ *
+ * @param byteCount The byte count.
+ */
+ public synchronized void addBytesTransfered(long byteCount) {
+ setBytesTransfered(bytesTransfered + byteCount);
+ }
+
+ /**
+ * Sets the number of bytes transferred.
+ *
+ * @param bytesTransfered The number of bytes transferred.
+ */
+ public synchronized void setBytesTransfered(long bytesTransfered) {
+ this.bytesTransfered = bytesTransfered;
+ createSample(bytesTransfered, false);
+ }
+
+ private void createSample(long bytesTransfered, boolean force) {
+ long now = System.currentTimeMillis();
+
+ if (history.isEmpty()) {
+ history.add(new Sample(bytesTransfered, now));
+ } else {
+ Sample lastSample = history.getLast();
+ if (force || now - lastSample.getTimestamp() > TransferStatus.SAMPLE_INTERVAL_MILLIS) {
+ history.add(new Sample(bytesTransfered, now));
+ }
+ }
+ }
+
+ /**
+ * Returns the number of milliseconds since the transfer status was last updated.
+ *
+ * @return Number of milliseconds, or <code>0</code> if never updated.
+ */
+ public synchronized long getMillisSinceLastUpdate() {
+ if (history.isEmpty()) {
+ return 0L;
+ }
+ return System.currentTimeMillis() - history.getLast().timestamp;
+ }
+
+ /**
+ * Returns the total number of bytes, or 0 if unknown.
+ *
+ * @return The total number of bytes, or 0 if unknown.
+ */
+ public long getBytesTotal() {
+ return bytesTotal;
+ }
+
+ /**
+ * Sets the total number of bytes, or 0 if unknown.
+ *
+ * @param bytesTotal The total number of bytes, or 0 if unknown.
+ */
+ public void setBytesTotal(long bytesTotal) {
+ this.bytesTotal = bytesTotal;
+ }
+
+ /**
+ * Returns the number of bytes that has been skipped (for instance when
+ * resuming downloads).
+ *
+ * @return The number of skipped bytes.
+ */
+ public synchronized long getBytesSkipped() {
+ return bytesSkipped;
+ }
+
+ /**
+ * Sets the number of bytes that has been skipped (for instance when
+ * resuming downloads).
+ *
+ * @param bytesSkipped The number of skipped bytes.
+ */
+ public synchronized void setBytesSkipped(long bytesSkipped) {
+ this.bytesSkipped = bytesSkipped;
+ }
+
+
+ /**
+ * Adds the given byte count to the total number of bytes skipped.
+ *
+ * @param byteCount The byte count.
+ */
+ public synchronized void addBytesSkipped(long byteCount) {
+ bytesSkipped += byteCount;
+ }
+
+ /**
+ * Returns the file that is currently being transferred.
+ *
+ * @return The file that is currently being transferred.
+ */
+ public synchronized File getFile() {
+ return file;
+ }
+
+ /**
+ * Sets the file that is currently being transferred.
+ *
+ * @param file The file that is currently being transferred.
+ */
+ public synchronized void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Returns the remote player for the stream.
+ *
+ * @return The remote player for the stream.
+ */
+ public synchronized Player getPlayer() {
+ return player;
+ }
+
+ /**
+ * Sets the remote player for the stream.
+ *
+ * @param player The remote player for the stream.
+ */
+ public synchronized void setPlayer(Player player) {
+ this.player = player;
+ }
+
+ /**
+ * Returns a history of samples for the stream
+ *
+ * @return A (copy of) the history list of samples.
+ */
+ public synchronized SampleHistory getHistory() {
+ return new SampleHistory(history);
+ }
+
+ /**
+ * Returns the history length in milliseconds.
+ *
+ * @return The history length in milliseconds.
+ */
+ public long getHistoryLengthMillis() {
+ return TransferStatus.SAMPLE_INTERVAL_MILLIS * (TransferStatus.HISTORY_LENGTH - 1);
+ }
+
+ /**
+ * Indicate that the stream should be terminated.
+ */
+ public void terminate() {
+ terminated = true;
+ }
+
+ /**
+ * Returns whether this stream has been terminated.
+ * Not that the <em>terminated status</em> is cleared by this method.
+ *
+ * @return Whether this stream has been terminated.
+ */
+ public boolean terminated() {
+ boolean result = terminated;
+ terminated = false;
+ return result;
+ }
+
+ /**
+ * Returns whether this transfer is active, i.e., if the connection is still established.
+ *
+ * @return Whether this transfer is active.
+ */
+ public boolean isActive() {
+ return active;
+ }
+
+ /**
+ * Sets whether this transfer is active, i.e., if the connection is still established.
+ *
+ * @param active Whether this transfer is active.
+ */
+ public void setActive(boolean active) {
+ this.active = active;
+
+ if (active) {
+ setBytesSkipped(0L);
+ setBytesTotal(0L);
+ setBytesTransfered(0L);
+ } else {
+ createSample(getBytesTransfered(), true);
+ }
+ }
+
+ /**
+ * A sample containing a timestamp and the number of bytes transferred up to that point in time.
+ */
+ public static class Sample {
+ private long bytesTransfered;
+ private long timestamp;
+
+ /**
+ * Creates a new sample.
+ *
+ * @param bytesTransfered The total number of bytes transferred.
+ * @param timestamp A point in time, in milliseconds.
+ */
+ public Sample(long bytesTransfered, long timestamp) {
+ this.bytesTransfered = bytesTransfered;
+ this.timestamp = timestamp;
+ }
+
+ /**
+ * Returns the number of bytes transferred.
+ *
+ * @return The number of bytes transferred.
+ */
+ public long getBytesTransfered() {
+ return bytesTransfered;
+ }
+
+ /**
+ * Returns the timestamp of the sample.
+ *
+ * @return The timestamp in milliseconds.
+ */
+ public long getTimestamp() {
+ return timestamp;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("TransferStatus-").append(hashCode()).append(" [player: ").append(player.getId()).append(", file: ");
+ builder.append(file).append(", terminated: ").append(terminated).append(", active: ").append(active).append("]");
+ return builder.toString();
+ }
+
+ /**
+ * Contains recent history of samples.
+ */
+ public static class SampleHistory extends BoundedList<Sample> {
+
+ public SampleHistory() {
+ super(HISTORY_LENGTH);
+ }
+
+ public SampleHistory(SampleHistory other) {
+ super(HISTORY_LENGTH);
+ addAll(other);
+ }
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/User.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/User.java
new file mode 100644
index 00000000..95e51004
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/User.java
@@ -0,0 +1,245 @@
+/*
+ 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.domain;
+
+/**
+ * Represent a user.
+ *
+ * @author Sindre Mehus
+ */
+public class User {
+
+ public static final String USERNAME_ADMIN = "admin";
+
+ private final String username;
+ private String password;
+ private String email;
+ private boolean ldapAuthenticated;
+ private long bytesStreamed;
+ private long bytesDownloaded;
+ private long bytesUploaded;
+
+ private boolean isAdminRole;
+ private boolean isSettingsRole;
+ private boolean isDownloadRole;
+ private boolean isUploadRole;
+ private boolean isPlaylistRole;
+ private boolean isCoverArtRole;
+ private boolean isCommentRole;
+ private boolean isPodcastRole;
+ private boolean isStreamRole;
+ private boolean isJukeboxRole;
+ private boolean isShareRole;
+
+ public User(String username, String password, String email, boolean ldapAuthenticated,
+ long bytesStreamed, long bytesDownloaded, long bytesUploaded) {
+ this.username = username;
+ this.password = password;
+ this.email = email;
+ this.ldapAuthenticated = ldapAuthenticated;
+ this.bytesStreamed = bytesStreamed;
+ this.bytesDownloaded = bytesDownloaded;
+ this.bytesUploaded = bytesUploaded;
+ }
+
+ public User(String username, String password, String email) {
+ this(username, password, email, false, 0, 0, 0);
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public boolean isLdapAuthenticated() {
+ return ldapAuthenticated;
+ }
+
+ public void setLdapAuthenticated(boolean ldapAuthenticated) {
+ this.ldapAuthenticated = ldapAuthenticated;
+ }
+
+ public long getBytesStreamed() {
+ return bytesStreamed;
+ }
+
+ public void setBytesStreamed(long bytesStreamed) {
+ this.bytesStreamed = bytesStreamed;
+ }
+
+ public long getBytesDownloaded() {
+ return bytesDownloaded;
+ }
+
+ public void setBytesDownloaded(long bytesDownloaded) {
+ this.bytesDownloaded = bytesDownloaded;
+ }
+
+ public long getBytesUploaded() {
+ return bytesUploaded;
+ }
+
+ public void setBytesUploaded(long bytesUploaded) {
+ this.bytesUploaded = bytesUploaded;
+ }
+
+ public boolean isAdminRole() {
+ return isAdminRole;
+ }
+
+ public void setAdminRole(boolean isAdminRole) {
+ this.isAdminRole = isAdminRole;
+ }
+
+ public boolean isSettingsRole() {
+ return isSettingsRole;
+ }
+
+ public void setSettingsRole(boolean isSettingsRole) {
+ this.isSettingsRole = isSettingsRole;
+ }
+
+ public boolean isCommentRole() {
+ return isCommentRole;
+ }
+
+ public void setCommentRole(boolean isCommentRole) {
+ this.isCommentRole = isCommentRole;
+ }
+
+ public boolean isDownloadRole() {
+ return isDownloadRole;
+ }
+
+ public void setDownloadRole(boolean isDownloadRole) {
+ this.isDownloadRole = isDownloadRole;
+ }
+
+ public boolean isUploadRole() {
+ return isUploadRole;
+ }
+
+ public void setUploadRole(boolean isUploadRole) {
+ this.isUploadRole = isUploadRole;
+ }
+
+ public boolean isPlaylistRole() {
+ return isPlaylistRole;
+ }
+
+ public void setPlaylistRole(boolean isPlaylistRole) {
+ this.isPlaylistRole = isPlaylistRole;
+ }
+
+ public boolean isCoverArtRole() {
+ return isCoverArtRole;
+ }
+
+ public void setCoverArtRole(boolean isCoverArtRole) {
+ this.isCoverArtRole = isCoverArtRole;
+ }
+
+ public boolean isPodcastRole() {
+ return isPodcastRole;
+ }
+
+ public void setPodcastRole(boolean isPodcastRole) {
+ this.isPodcastRole = isPodcastRole;
+ }
+
+ 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 isShareRole() {
+ return isShareRole;
+ }
+
+ public void setShareRole(boolean shareRole) {
+ isShareRole = shareRole;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer result = new StringBuffer(username);
+
+ if (isAdminRole) {
+ result.append(" [admin]");
+ }
+ if (isSettingsRole) {
+ result.append(" [settings]");
+ }
+ if (isDownloadRole) {
+ result.append(" [download]");
+ }
+ if (isUploadRole) {
+ result.append(" [upload]");
+ }
+ if (isPlaylistRole) {
+ result.append(" [playlist]");
+ }
+ if (isCoverArtRole) {
+ result.append(" [coverart]");
+ }
+ if (isCommentRole) {
+ result.append(" [comment]");
+ }
+ if (isPodcastRole) {
+ result.append(" [podcast]");
+ }
+ if (isStreamRole) {
+ result.append(" [stream]");
+ }
+ if (isJukeboxRole) {
+ result.append(" [jukebox]");
+ }
+ if (isShareRole) {
+ result.append(" [share]");
+ }
+
+ return result.toString();
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/UserSettings.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/UserSettings.java
new file mode 100644
index 00000000..856591bc
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/UserSettings.java
@@ -0,0 +1,328 @@
+/*
+ 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.domain;
+
+import java.util.*;
+
+/**
+ * Represent user-specific settings.
+ *
+ * @author Sindre Mehus
+ */
+public class UserSettings {
+
+ private String username;
+ private Locale locale;
+ private String themeId;
+ private boolean showNowPlayingEnabled;
+ private boolean showChatEnabled;
+ private boolean finalVersionNotificationEnabled;
+ private boolean betaVersionNotificationEnabled;
+ private Visibility mainVisibility = new Visibility();
+ private Visibility playlistVisibility = new Visibility();
+ private boolean lastFmEnabled;
+ private String lastFmUsername;
+ private String lastFmPassword;
+ private TranscodeScheme transcodeScheme = TranscodeScheme.OFF;
+ private int selectedMusicFolderId = -1;
+ private boolean partyModeEnabled;
+ private boolean nowPlayingAllowed;
+ private AvatarScheme avatarScheme = AvatarScheme.NONE;
+ private Integer systemAvatarId;
+ private Date changed = new Date();
+
+ public UserSettings(String username) {
+ this.username = username;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Locale getLocale() {
+ return locale;
+ }
+
+ public void setLocale(Locale locale) {
+ this.locale = locale;
+ }
+
+ public String getThemeId() {
+ return themeId;
+ }
+
+ public void setThemeId(String themeId) {
+ this.themeId = themeId;
+ }
+
+ 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 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 Visibility getMainVisibility() {
+ return mainVisibility;
+ }
+
+ public void setMainVisibility(Visibility mainVisibility) {
+ this.mainVisibility = mainVisibility;
+ }
+
+ public Visibility getPlaylistVisibility() {
+ return playlistVisibility;
+ }
+
+ public void setPlaylistVisibility(Visibility playlistVisibility) {
+ this.playlistVisibility = playlistVisibility;
+ }
+
+ 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 TranscodeScheme getTranscodeScheme() {
+ return transcodeScheme;
+ }
+
+ public void setTranscodeScheme(TranscodeScheme transcodeScheme) {
+ this.transcodeScheme = transcodeScheme;
+ }
+
+ public int getSelectedMusicFolderId() {
+ return selectedMusicFolderId;
+ }
+
+ public void setSelectedMusicFolderId(int selectedMusicFolderId) {
+ this.selectedMusicFolderId = selectedMusicFolderId;
+ }
+
+ public boolean isPartyModeEnabled() {
+ return partyModeEnabled;
+ }
+
+ public void setPartyModeEnabled(boolean partyModeEnabled) {
+ this.partyModeEnabled = partyModeEnabled;
+ }
+
+ public boolean isNowPlayingAllowed() {
+ return nowPlayingAllowed;
+ }
+
+ public void setNowPlayingAllowed(boolean nowPlayingAllowed) {
+ this.nowPlayingAllowed = nowPlayingAllowed;
+ }
+
+ public AvatarScheme getAvatarScheme() {
+ return avatarScheme;
+ }
+
+ public void setAvatarScheme(AvatarScheme avatarScheme) {
+ this.avatarScheme = avatarScheme;
+ }
+
+ public Integer getSystemAvatarId() {
+ return systemAvatarId;
+ }
+
+ public void setSystemAvatarId(Integer systemAvatarId) {
+ this.systemAvatarId = systemAvatarId;
+ }
+
+ /**
+ * Returns when the corresponding database entry was last changed.
+ *
+ * @return When the corresponding database entry was last changed.
+ */
+ public Date getChanged() {
+ return changed;
+ }
+
+ /**
+ * Sets when the corresponding database entry was last changed.
+ *
+ * @param changed When the corresponding database entry was last changed.
+ */
+ public void setChanged(Date changed) {
+ this.changed = changed;
+ }
+
+ /**
+ * Configuration of what information to display about a song.
+ */
+ public static class Visibility {
+ private int captionCutoff;
+ private boolean isTrackNumberVisible;
+ private boolean isArtistVisible;
+ private boolean isAlbumVisible;
+ private boolean isGenreVisible;
+ private boolean isYearVisible;
+ private boolean isBitRateVisible;
+ private boolean isDurationVisible;
+ private boolean isFormatVisible;
+ private boolean isFileSizeVisible;
+
+ public Visibility() {}
+
+ public Visibility(int captionCutoff, boolean trackNumberVisible, boolean artistVisible, boolean albumVisible,
+ boolean genreVisible, boolean yearVisible, boolean bitRateVisible,
+ boolean durationVisible, boolean formatVisible, boolean fileSizeVisible) {
+ this.captionCutoff = captionCutoff;
+ isTrackNumberVisible = trackNumberVisible;
+ isArtistVisible = artistVisible;
+ isAlbumVisible = albumVisible;
+ isGenreVisible = genreVisible;
+ isYearVisible = yearVisible;
+ isBitRateVisible = bitRateVisible;
+ isDurationVisible = durationVisible;
+ isFormatVisible = formatVisible;
+ isFileSizeVisible = fileSizeVisible;
+ }
+
+ public int getCaptionCutoff() {
+ return captionCutoff;
+ }
+
+ public void setCaptionCutoff(int captionCutoff) {
+ this.captionCutoff = captionCutoff;
+ }
+
+ public boolean isTrackNumberVisible() {
+ return isTrackNumberVisible;
+ }
+
+ public void setTrackNumberVisible(boolean trackNumberVisible) {
+ isTrackNumberVisible = trackNumberVisible;
+ }
+
+ public boolean isArtistVisible() {
+ return isArtistVisible;
+ }
+
+ public void setArtistVisible(boolean artistVisible) {
+ isArtistVisible = artistVisible;
+ }
+
+ public boolean isAlbumVisible() {
+ return isAlbumVisible;
+ }
+
+ public void setAlbumVisible(boolean albumVisible) {
+ isAlbumVisible = albumVisible;
+ }
+
+ public boolean isGenreVisible() {
+ return isGenreVisible;
+ }
+
+ public void setGenreVisible(boolean genreVisible) {
+ isGenreVisible = genreVisible;
+ }
+
+ public boolean isYearVisible() {
+ return isYearVisible;
+ }
+
+ public void setYearVisible(boolean yearVisible) {
+ isYearVisible = yearVisible;
+ }
+
+ public boolean isBitRateVisible() {
+ return isBitRateVisible;
+ }
+
+ public void setBitRateVisible(boolean bitRateVisible) {
+ isBitRateVisible = bitRateVisible;
+ }
+
+ public boolean isDurationVisible() {
+ return isDurationVisible;
+ }
+
+ public void setDurationVisible(boolean durationVisible) {
+ isDurationVisible = durationVisible;
+ }
+
+ public boolean isFormatVisible() {
+ return isFormatVisible;
+ }
+
+ public void setFormatVisible(boolean formatVisible) {
+ isFormatVisible = formatVisible;
+ }
+
+ public boolean isFileSizeVisible() {
+ return isFileSizeVisible;
+ }
+
+ public void setFileSizeVisible(boolean fileSizeVisible) {
+ isFileSizeVisible = fileSizeVisible;
+ }
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Version.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Version.java
new file mode 100644
index 00000000..c4d42a99
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/Version.java
@@ -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.domain;
+
+/**
+ * Represents the version number of Subsonic.
+ *
+ * @author Sindre Mehus
+ * @version $Revision: 1.3 $ $Date: 2006/01/20 21:25:16 $
+ */
+public class Version implements Comparable<Version> {
+ private int major;
+ private int minor;
+ private int beta;
+ private int bugfix;
+
+ /**
+ * Creates a new version instance by parsing the given string.
+ * @param version A string of the format "1.27", "1.27.2" or "1.27.beta3".
+ */
+ public Version(String version) {
+ String[] s = version.split("\\.");
+ major = Integer.valueOf(s[0]);
+ minor = Integer.valueOf(s[1]);
+
+ if (s.length > 2) {
+ if (s[2].contains("beta")) {
+ beta = Integer.valueOf(s[2].replace("beta", ""));
+ } else {
+ bugfix = Integer.valueOf(s[2]);
+ }
+ }
+ }
+
+ public int getMajor() {
+ return major;
+ }
+
+ public int getMinor() {
+ return minor;
+ }
+
+ /**
+ * Return whether this object is equal to another.
+ * @param o Object to compare to.
+ * @return Whether this object is equals to another.
+ */
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final Version version = (Version) o;
+
+ if (beta != version.beta) return false;
+ if (bugfix != version.bugfix) return false;
+ if (major != version.major) return false;
+ return minor == version.minor;
+ }
+
+ /**
+ * Returns a hash code for this object.
+ * @return A hash code for this object.
+ */
+ public int hashCode() {
+ int result;
+ result = major;
+ result = 29 * result + minor;
+ result = 29 * result + beta;
+ result = 29 * result + bugfix;
+ return result;
+ }
+
+ /**
+ * Returns a string representation of the form "1.27", "1.27.2" or "1.27.beta3".
+ * @return A string representation of the form "1.27", "1.27.2" or "1.27.beta3".
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append(major).append('.').append(minor);
+ if (beta != 0) {
+ buf.append(".beta").append(beta);
+ } else if (bugfix != 0) {
+ buf.append('.').append(bugfix);
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Compares this object with the specified object for order.
+ * @param version The object to compare to.
+ * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or
+ * greater than the specified object.
+ */
+ public int compareTo(Version version) {
+ if (major < version.major) {
+ return -1;
+ } else if (major > version.major) {
+ return 1;
+ }
+
+ if (minor < version.minor) {
+ return -1;
+ } else if (minor > version.minor) {
+ return 1;
+ }
+
+ if (bugfix < version.bugfix) {
+ return -1;
+ } else if (bugfix > version.bugfix) {
+ return 1;
+ }
+
+ int thisBeta = beta == 0 ? Integer.MAX_VALUE : beta;
+ int otherBeta = version.beta == 0 ? Integer.MAX_VALUE : version.beta;
+
+ if (thisBeta < otherBeta) {
+ return -1;
+ } else if (thisBeta > otherBeta) {
+ return 1;
+ }
+
+ return 0;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/VideoTranscodingSettings.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/VideoTranscodingSettings.java
new file mode 100644
index 00000000..18661ba4
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/VideoTranscodingSettings.java
@@ -0,0 +1,50 @@
+/*
+ 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.domain;
+
+/**
+ * Parameters used when transcoding videos.
+ *
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public class VideoTranscodingSettings {
+
+ private final int width;
+ private final int height;
+ private final int timeOffset;
+
+ public VideoTranscodingSettings(int width, int height, int timeOffset) {
+ this.width = width;
+ this.height = height;
+ this.timeOffset = timeOffset;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public int getTimeOffset() {
+ return timeOffset;
+ }
+}
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/WeUPnPRouter.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/WeUPnPRouter.java
new file mode 100644
index 00000000..e36701e8
--- /dev/null
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/domain/WeUPnPRouter.java
@@ -0,0 +1,56 @@
+/*
+ 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.domain;
+
+import org.wetorrent.upnp.GatewayDevice;
+import org.wetorrent.upnp.GatewayDiscover;
+
+import java.net.InetAddress;
+
+/**
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public class WeUPnPRouter implements Router {
+ private final GatewayDevice device;
+
+ private WeUPnPRouter(GatewayDevice device) {
+ this.device = device;
+ }
+
+ public static WeUPnPRouter findRouter() throws Exception {
+ GatewayDiscover discover = new GatewayDiscover();
+ discover.discover();
+ GatewayDevice device = discover.getValidGateway();
+ if (device == null) {
+ return null;
+ }
+
+ return new WeUPnPRouter(device);
+ }
+
+ public void addPortMapping(int externalPort, int internalPort, int leaseDuration) throws Exception {
+ String localIp = InetAddress.getLocalHost().getHostAddress();
+ device.addPortMapping(externalPort, internalPort, localIp, "TCP", "Subsonic");
+ }
+
+ public void deletePortMapping(int externalPort, int internalPort) throws Exception {
+ device.deletePortMapping(externalPort, "TCP");
+ }
+}