Subsonic-->Libresonic regex
This commit is contained in:
+279
@@ -0,0 +1,279 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This provides static methods to convert comma delimited text into a
|
||||
* JSONArray, and to covert a JSONArray into comma delimited text. Comma
|
||||
* delimited text is a very popular format for data interchange. It is
|
||||
* understood by most database, spreadsheet, and organizer programs.
|
||||
* <p>
|
||||
* Each row of text represents a row in a table or a data record. Each row
|
||||
* ends with a NEWLINE character. Each row contains one or more values.
|
||||
* Values are separated by commas. A value can contain any character except
|
||||
* for comma, unless is is wrapped in single quotes or double quotes.
|
||||
* <p>
|
||||
* The first row usually contains the names of the columns.
|
||||
* <p>
|
||||
* A comma delimited list can be converted into a JSONArray of JSONObjects.
|
||||
* The names for the elements in the JSONObjects can be taken from the names
|
||||
* in the first row.
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class CDL {
|
||||
|
||||
/**
|
||||
* Get the next value. The value can be wrapped in quotes. The value can
|
||||
* be empty.
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return The value string, or null if empty.
|
||||
* @throws JSONException if the quoted string is badly formed.
|
||||
*/
|
||||
private static String getValue(JSONTokener x) throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
StringBuffer sb;
|
||||
do {
|
||||
c = x.next();
|
||||
} while (c == ' ' || c == '\t');
|
||||
switch (c) {
|
||||
case 0:
|
||||
return null;
|
||||
case '"':
|
||||
case '\'':
|
||||
q = c;
|
||||
sb = new StringBuffer();
|
||||
for (;;) {
|
||||
c = x.next();
|
||||
if (c == q) {
|
||||
break;
|
||||
}
|
||||
if (c == 0 || c == '\n' || c == '\r') {
|
||||
throw x.syntaxError("Missing close quote '" + q + "'.");
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
case ',':
|
||||
x.back();
|
||||
return "";
|
||||
default:
|
||||
x.back();
|
||||
return x.nextTo(',');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of strings from a row of comma delimited values.
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return A JSONArray of strings.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException {
|
||||
JSONArray ja = new JSONArray();
|
||||
for (;;) {
|
||||
String value = getValue(x);
|
||||
char c = x.next();
|
||||
if (value == null ||
|
||||
(ja.length() == 0 && value.length() == 0 && c != ',')) {
|
||||
return null;
|
||||
}
|
||||
ja.put(value);
|
||||
for (;;) {
|
||||
if (c == ',') {
|
||||
break;
|
||||
}
|
||||
if (c != ' ') {
|
||||
if (c == '\n' || c == '\r' || c == 0) {
|
||||
return ja;
|
||||
}
|
||||
throw x.syntaxError("Bad character '" + c + "' (" +
|
||||
(int)c + ").");
|
||||
}
|
||||
c = x.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONObject from a row of comma delimited text, using a
|
||||
* parallel JSONArray of strings to provides the names of the elements.
|
||||
* @param names A JSONArray of names. This is commonly obtained from the
|
||||
* first row of a comma delimited text file using the rowToJSONArray
|
||||
* method.
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return A JSONObject combining the names and values.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x)
|
||||
throws JSONException {
|
||||
JSONArray ja = rowToJSONArray(x);
|
||||
return ja != null ? ja.toJSONObject(names) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a comma delimited text row from a JSONArray. Values containing
|
||||
* the comma character will be quoted. Troublesome characters may be
|
||||
* removed.
|
||||
* @param ja A JSONArray of strings.
|
||||
* @return A string ending in NEWLINE.
|
||||
*/
|
||||
public static String rowToString(JSONArray ja) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < ja.length(); i += 1) {
|
||||
if (i > 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
Object object = ja.opt(i);
|
||||
if (object != null) {
|
||||
String string = object.toString();
|
||||
if (string.length() > 0 && (string.indexOf(',') >= 0 ||
|
||||
string.indexOf('\n') >= 0 || string.indexOf('\r') >= 0 ||
|
||||
string.indexOf(0) >= 0 || string.charAt(0) == '"')) {
|
||||
sb.append('"');
|
||||
int length = string.length();
|
||||
for (int j = 0; j < length; j += 1) {
|
||||
char c = string.charAt(j);
|
||||
if (c >= ' ' && c != '"') {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
sb.append('"');
|
||||
} else {
|
||||
sb.append(string);
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append('\n');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string,
|
||||
* using the first row as a source of names.
|
||||
* @param string The comma delimited text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(String string) throws JSONException {
|
||||
return toJSONArray(new JSONTokener(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string,
|
||||
* using the first row as a source of names.
|
||||
* @param x The JSONTokener containing the comma delimited text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(JSONTokener x) throws JSONException {
|
||||
return toJSONArray(rowToJSONArray(x), x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string
|
||||
* using a supplied JSONArray as the source of element names.
|
||||
* @param names A JSONArray of strings.
|
||||
* @param string The comma delimited text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(JSONArray names, String string)
|
||||
throws JSONException {
|
||||
return toJSONArray(names, new JSONTokener(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string
|
||||
* using a supplied JSONArray as the source of element names.
|
||||
* @param names A JSONArray of strings.
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(JSONArray names, JSONTokener x)
|
||||
throws JSONException {
|
||||
if (names == null || names.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
JSONArray ja = new JSONArray();
|
||||
for (;;) {
|
||||
JSONObject jo = rowToJSONObject(names, x);
|
||||
if (jo == null) {
|
||||
break;
|
||||
}
|
||||
ja.put(jo);
|
||||
}
|
||||
if (ja.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return ja;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Produce a comma delimited text from a JSONArray of JSONObjects. The
|
||||
* first row will be a list of names obtained by inspecting the first
|
||||
* JSONObject.
|
||||
* @param ja A JSONArray of JSONObjects.
|
||||
* @return A comma delimited text.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONArray ja) throws JSONException {
|
||||
JSONObject jo = ja.optJSONObject(0);
|
||||
if (jo != null) {
|
||||
JSONArray names = jo.names();
|
||||
if (names != null) {
|
||||
return rowToString(names) + toString(names, ja);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a comma delimited text from a JSONArray of JSONObjects using
|
||||
* a provided list of names. The list of names is not included in the
|
||||
* output.
|
||||
* @param names A JSONArray of strings.
|
||||
* @param ja A JSONArray of JSONObjects.
|
||||
* @return A comma delimited text.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONArray names, JSONArray ja)
|
||||
throws JSONException {
|
||||
if (names == null || names.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < ja.length(); i += 1) {
|
||||
JSONObject jo = ja.optJSONObject(i);
|
||||
if (jo != null) {
|
||||
sb.append(rowToString(jo.toJSONArray(names)));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a web browser cookie specification to a JSONObject and back.
|
||||
* JSON and Cookies are both notations for name/value pairs.
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class Cookie {
|
||||
|
||||
/**
|
||||
* Produce a copy of a string in which the characters '+', '%', '=', ';'
|
||||
* and control characters are replaced with "%hh". This is a gentle form
|
||||
* of URL encoding, attempting to cause as little distortion to the
|
||||
* string as possible. The characters '=' and ';' are meta characters in
|
||||
* cookies. By convention, they are escaped using the URL-encoding. This is
|
||||
* only a convention, not a standard. Often, cookies are expected to have
|
||||
* encoded values. We encode '=' and ';' because we must. We encode '%' and
|
||||
* '+' because they are meta characters in URL encoding.
|
||||
* @param string The source string.
|
||||
* @return The escaped result.
|
||||
*/
|
||||
public static String escape(String string) {
|
||||
char c;
|
||||
String s = string.trim();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int length = s.length();
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
c = s.charAt(i);
|
||||
if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
|
||||
sb.append('%');
|
||||
sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16));
|
||||
sb.append(Character.forDigit((char)(c & 0x0f), 16));
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a cookie specification string into a JSONObject. The string
|
||||
* will contain a name value pair separated by '='. The name and the value
|
||||
* will be unescaped, possibly converting '+' and '%' sequences. The
|
||||
* cookie properties may follow, separated by ';', also represented as
|
||||
* name=value (except the secure property, which does not have a value).
|
||||
* The name will be stored under the key "name", and the value will be
|
||||
* stored under the key "value". This method does not do checking or
|
||||
* validation of the parameters. It only converts the cookie string into
|
||||
* a JSONObject.
|
||||
* @param string The cookie specification string.
|
||||
* @return A JSONObject containing "name", "value", and possibly other
|
||||
* members.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
String name;
|
||||
JSONObject jo = new JSONObject();
|
||||
Object value;
|
||||
JSONTokener x = new JSONTokener(string);
|
||||
jo.put("name", x.nextTo('='));
|
||||
x.next('=');
|
||||
jo.put("value", x.nextTo(';'));
|
||||
x.next();
|
||||
while (x.more()) {
|
||||
name = unescape(x.nextTo("=;"));
|
||||
if (x.next() != '=') {
|
||||
if (name.equals("secure")) {
|
||||
value = Boolean.TRUE;
|
||||
} else {
|
||||
throw x.syntaxError("Missing '=' in cookie parameter.");
|
||||
}
|
||||
} else {
|
||||
value = unescape(x.nextTo(';'));
|
||||
x.next();
|
||||
}
|
||||
jo.put(name, value);
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a JSONObject into a cookie specification string. The JSONObject
|
||||
* must contain "name" and "value" members.
|
||||
* If the JSONObject contains "expires", "domain", "path", or "secure"
|
||||
* members, they will be appended to the cookie specification string.
|
||||
* All other members are ignored.
|
||||
* @param jo A JSONObject
|
||||
* @return A cookie specification string
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
sb.append(escape(jo.getString("name")));
|
||||
sb.append("=");
|
||||
sb.append(escape(jo.getString("value")));
|
||||
if (jo.has("expires")) {
|
||||
sb.append(";expires=");
|
||||
sb.append(jo.getString("expires"));
|
||||
}
|
||||
if (jo.has("domain")) {
|
||||
sb.append(";domain=");
|
||||
sb.append(escape(jo.getString("domain")));
|
||||
}
|
||||
if (jo.has("path")) {
|
||||
sb.append(";path=");
|
||||
sb.append(escape(jo.getString("path")));
|
||||
}
|
||||
if (jo.optBoolean("secure")) {
|
||||
sb.append(";secure");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert <code>%</code><i>hh</i> sequences to single characters, and
|
||||
* convert plus to space.
|
||||
* @param string A string that may contain
|
||||
* <code>+</code> <small>(plus)</small> and
|
||||
* <code>%</code><i>hh</i> sequences.
|
||||
* @return The unescaped string.
|
||||
*/
|
||||
public static String unescape(String string) {
|
||||
int length = string.length();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < length; ++i) {
|
||||
char c = string.charAt(i);
|
||||
if (c == '+') {
|
||||
c = ' ';
|
||||
} else if (c == '%' && i + 2 < length) {
|
||||
int d = JSONTokener.dehexchar(string.charAt(i + 1));
|
||||
int e = JSONTokener.dehexchar(string.charAt(i + 2));
|
||||
if (d >= 0 && e >= 0) {
|
||||
c = (char)(d * 16 + e);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Convert a web browser cookie list string to a JSONObject and back.
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class CookieList {
|
||||
|
||||
/**
|
||||
* Convert a cookie list into a JSONObject. A cookie list is a sequence
|
||||
* of name/value pairs. The names are separated from the values by '='.
|
||||
* The pairs are separated by ';'. The names and the values
|
||||
* will be unescaped, possibly converting '+' and '%' sequences.
|
||||
*
|
||||
* To add a cookie to a cooklist,
|
||||
* cookielistJSONObject.put(cookieJSONObject.getString("name"),
|
||||
* cookieJSONObject.getString("value"));
|
||||
* @param string A cookie list string
|
||||
* @return A JSONObject
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
JSONObject jo = new JSONObject();
|
||||
JSONTokener x = new JSONTokener(string);
|
||||
while (x.more()) {
|
||||
String name = Cookie.unescape(x.nextTo('='));
|
||||
x.next('=');
|
||||
jo.put(name, Cookie.unescape(x.nextTo(';')));
|
||||
x.next();
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a JSONObject into a cookie list. A cookie list is a sequence
|
||||
* of name/value pairs. The names are separated from the values by '='.
|
||||
* The pairs are separated by ';'. The characters '%', '+', '=', and ';'
|
||||
* in the names and values are replaced by "%hh".
|
||||
* @param jo A JSONObject
|
||||
* @return A cookie list string
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
boolean b = false;
|
||||
Iterator keys = jo.keys();
|
||||
String string;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (keys.hasNext()) {
|
||||
string = keys.next().toString();
|
||||
if (!jo.isNull(string)) {
|
||||
if (b) {
|
||||
sb.append(';');
|
||||
}
|
||||
sb.append(Cookie.escape(string));
|
||||
sb.append("=");
|
||||
sb.append(Cookie.escape(jo.getString(string)));
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Convert an HTTP header to a JSONObject and back.
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class HTTP {
|
||||
|
||||
/** Carriage return/line feed. */
|
||||
public static final String CRLF = "\r\n";
|
||||
|
||||
/**
|
||||
* Convert an HTTP header string into a JSONObject. It can be a request
|
||||
* header or a response header. A request header will contain
|
||||
* <pre>{
|
||||
* Method: "POST" (for example),
|
||||
* "Request-URI": "/" (for example),
|
||||
* "HTTP-Version": "HTTP/1.1" (for example)
|
||||
* }</pre>
|
||||
* A response header will contain
|
||||
* <pre>{
|
||||
* "HTTP-Version": "HTTP/1.1" (for example),
|
||||
* "Status-Code": "200" (for example),
|
||||
* "Reason-Phrase": "OK" (for example)
|
||||
* }</pre>
|
||||
* In addition, the other parameters in the header will be captured, using
|
||||
* the HTTP field names as JSON names, so that <pre>
|
||||
* Date: Sun, 26 May 2002 18:06:04 GMT
|
||||
* Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
|
||||
* Cache-Control: no-cache</pre>
|
||||
* become
|
||||
* <pre>{...
|
||||
* Date: "Sun, 26 May 2002 18:06:04 GMT",
|
||||
* Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
|
||||
* "Cache-Control": "no-cache",
|
||||
* ...}</pre>
|
||||
* It does no further checking or conversion. It does not parse dates.
|
||||
* It does not do '%' transforms on URLs.
|
||||
* @param string An HTTP header string.
|
||||
* @return A JSONObject containing the elements and attributes
|
||||
* of the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
JSONObject jo = new JSONObject();
|
||||
HTTPTokener x = new HTTPTokener(string);
|
||||
String token;
|
||||
|
||||
token = x.nextToken();
|
||||
if (token.toUpperCase().startsWith("HTTP")) {
|
||||
|
||||
// Response
|
||||
|
||||
jo.put("HTTP-Version", token);
|
||||
jo.put("Status-Code", x.nextToken());
|
||||
jo.put("Reason-Phrase", x.nextTo('\0'));
|
||||
x.next();
|
||||
|
||||
} else {
|
||||
|
||||
// Request
|
||||
|
||||
jo.put("Method", token);
|
||||
jo.put("Request-URI", x.nextToken());
|
||||
jo.put("HTTP-Version", x.nextToken());
|
||||
}
|
||||
|
||||
// Fields
|
||||
|
||||
while (x.more()) {
|
||||
String name = x.nextTo(':');
|
||||
x.next(':');
|
||||
jo.put(name, x.nextTo('\0'));
|
||||
x.next();
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a JSONObject into an HTTP header. A request header must contain
|
||||
* <pre>{
|
||||
* Method: "POST" (for example),
|
||||
* "Request-URI": "/" (for example),
|
||||
* "HTTP-Version": "HTTP/1.1" (for example)
|
||||
* }</pre>
|
||||
* A response header must contain
|
||||
* <pre>{
|
||||
* "HTTP-Version": "HTTP/1.1" (for example),
|
||||
* "Status-Code": "200" (for example),
|
||||
* "Reason-Phrase": "OK" (for example)
|
||||
* }</pre>
|
||||
* Any other members of the JSONObject will be output as HTTP fields.
|
||||
* The result will end with two CRLF pairs.
|
||||
* @param jo A JSONObject
|
||||
* @return An HTTP header string.
|
||||
* @throws JSONException if the object does not contain enough
|
||||
* information.
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
Iterator keys = jo.keys();
|
||||
String string;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
if (jo.has("Status-Code") && jo.has("Reason-Phrase")) {
|
||||
sb.append(jo.getString("HTTP-Version"));
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("Status-Code"));
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("Reason-Phrase"));
|
||||
} else if (jo.has("Method") && jo.has("Request-URI")) {
|
||||
sb.append(jo.getString("Method"));
|
||||
sb.append(' ');
|
||||
sb.append('"');
|
||||
sb.append(jo.getString("Request-URI"));
|
||||
sb.append('"');
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("HTTP-Version"));
|
||||
} else {
|
||||
throw new JSONException("Not enough material for an HTTP header.");
|
||||
}
|
||||
sb.append(CRLF);
|
||||
while (keys.hasNext()) {
|
||||
string = keys.next().toString();
|
||||
if (!"HTTP-Version".equals(string) && !"Status-Code".equals(string) &&
|
||||
!"Reason-Phrase".equals(string) && !"Method".equals(string) &&
|
||||
!"Request-URI".equals(string) && !jo.isNull(string)) {
|
||||
sb.append(string);
|
||||
sb.append(": ");
|
||||
sb.append(jo.getString(string));
|
||||
sb.append(CRLF);
|
||||
}
|
||||
}
|
||||
sb.append(CRLF);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The HTTPTokener extends the JSONTokener to provide additional methods
|
||||
* for the parsing of HTTP headers.
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class HTTPTokener extends JSONTokener {
|
||||
|
||||
/**
|
||||
* Construct an HTTPTokener from a string.
|
||||
* @param string A source string.
|
||||
*/
|
||||
public HTTPTokener(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next token or string. This is used in parsing HTTP headers.
|
||||
* @throws JSONException
|
||||
* @return A String.
|
||||
*/
|
||||
public String nextToken() throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
if (c == '"' || c == '\'') {
|
||||
q = c;
|
||||
for (;;) {
|
||||
c = next();
|
||||
if (c < ' ') {
|
||||
throw syntaxError("Unterminated string.");
|
||||
}
|
||||
if (c == q) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
if (c == 0 || Character.isWhitespace(c)) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
c = next();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,920 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A JSONArray is an ordered sequence of values. Its external text form is a
|
||||
* string wrapped in square brackets with commas separating the values. The
|
||||
* internal form is an object having <code>get</code> and <code>opt</code>
|
||||
* methods for accessing the values by index, and <code>put</code> methods for
|
||||
* adding or replacing values. The values can be any of these types:
|
||||
* <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
|
||||
* <code>Number</code>, <code>String</code>, or the
|
||||
* <code>JSONObject.NULL object</code>.
|
||||
* <p>
|
||||
* The constructor can convert a JSON text into a Java object. The
|
||||
* <code>toString</code> method converts to JSON text.
|
||||
* <p>
|
||||
* A <code>get</code> method returns a value if one can be found, and throws an
|
||||
* exception if one cannot be found. An <code>opt</code> method returns a
|
||||
* default value instead of throwing an exception, and so is useful for
|
||||
* obtaining optional values.
|
||||
* <p>
|
||||
* The generic <code>get()</code> and <code>opt()</code> methods return an
|
||||
* object which you can cast or query for type. There are also typed
|
||||
* <code>get</code> and <code>opt</code> methods that do type checking and type
|
||||
* coercion for you.
|
||||
* <p>
|
||||
* The texts produced by the <code>toString</code> methods strictly conform to
|
||||
* JSON syntax rules. The constructors are more forgiving in the texts they will
|
||||
* accept:
|
||||
* <ul>
|
||||
* <li>An extra <code>,</code> <small>(comma)</small> may appear just
|
||||
* before the closing bracket.</li>
|
||||
* <li>The <code>null</code> value will be inserted when there
|
||||
* is <code>,</code> <small>(comma)</small> elision.</li>
|
||||
* <li>Strings may be quoted with <code>'</code> <small>(single
|
||||
* quote)</small>.</li>
|
||||
* <li>Strings do not need to be quoted at all if they do not begin with a quote
|
||||
* or single quote, and if they do not contain leading or trailing spaces,
|
||||
* and if they do not contain any of these characters:
|
||||
* <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
|
||||
* and if they are not the reserved words <code>true</code>,
|
||||
* <code>false</code>, or <code>null</code>.</li>
|
||||
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
|
||||
* well as by <code>,</code> <small>(comma)</small>.</li>
|
||||
* </ul>
|
||||
|
||||
* @author JSON.org
|
||||
* @version 2011-12-19
|
||||
*/
|
||||
public class JSONArray {
|
||||
|
||||
|
||||
/**
|
||||
* The arrayList where the JSONArray's properties are kept.
|
||||
*/
|
||||
private final ArrayList myArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* Construct an empty JSONArray.
|
||||
*/
|
||||
public JSONArray() {
|
||||
this.myArrayList = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a JSONTokener.
|
||||
* @param x A JSONTokener
|
||||
* @throws JSONException If there is a syntax error.
|
||||
*/
|
||||
public JSONArray(JSONTokener x) throws JSONException {
|
||||
this();
|
||||
if (x.nextClean() != '[') {
|
||||
throw x.syntaxError("A JSONArray text must start with '['");
|
||||
}
|
||||
if (x.nextClean() != ']') {
|
||||
x.back();
|
||||
for (;;) {
|
||||
if (x.nextClean() == ',') {
|
||||
x.back();
|
||||
this.myArrayList.add(JSONObject.NULL);
|
||||
} else {
|
||||
x.back();
|
||||
this.myArrayList.add(x.nextValue());
|
||||
}
|
||||
switch (x.nextClean()) {
|
||||
case ';':
|
||||
case ',':
|
||||
if (x.nextClean() == ']') {
|
||||
return;
|
||||
}
|
||||
x.back();
|
||||
break;
|
||||
case ']':
|
||||
return;
|
||||
default:
|
||||
throw x.syntaxError("Expected a ',' or ']'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a source JSON text.
|
||||
* @param source A string that begins with
|
||||
* <code>[</code> <small>(left bracket)</small>
|
||||
* and ends with <code>]</code> <small>(right bracket)</small>.
|
||||
* @throws JSONException If there is a syntax error.
|
||||
*/
|
||||
public JSONArray(String source) throws JSONException {
|
||||
this(new JSONTokener(source));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a Collection.
|
||||
* @param collection A Collection.
|
||||
*/
|
||||
public JSONArray(Collection collection) {
|
||||
this.myArrayList = new ArrayList();
|
||||
if (collection != null) {
|
||||
Iterator iter = collection.iterator();
|
||||
while (iter.hasNext()) {
|
||||
this.myArrayList.add(JSONObject.wrap(iter.next()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from an array
|
||||
* @throws JSONException If not an array.
|
||||
*/
|
||||
public JSONArray(Object array) throws JSONException {
|
||||
this();
|
||||
if (array.getClass().isArray()) {
|
||||
int length = Array.getLength(array);
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
this.put(JSONObject.wrap(Array.get(array, i)));
|
||||
}
|
||||
} else {
|
||||
throw new JSONException(
|
||||
"JSONArray initial value should be a string or collection or array.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the object value associated with an index.
|
||||
* @param index
|
||||
* The index must be between 0 and length() - 1.
|
||||
* @return An object value.
|
||||
* @throws JSONException If there is no value for the index.
|
||||
*/
|
||||
public Object get(int index) throws JSONException {
|
||||
Object object = this.opt(index);
|
||||
if (object == null) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the boolean value associated with an index.
|
||||
* The string values "true" and "false" are converted to boolean.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The truth.
|
||||
* @throws JSONException If there is no value for the index or if the
|
||||
* value is not convertible to boolean.
|
||||
*/
|
||||
public boolean getBoolean(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object.equals(Boolean.FALSE) ||
|
||||
(object instanceof String &&
|
||||
((String)object).equalsIgnoreCase("false"))) {
|
||||
return false;
|
||||
} else if (object.equals(Boolean.TRUE) ||
|
||||
(object instanceof String &&
|
||||
((String)object).equalsIgnoreCase("true"))) {
|
||||
return true;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a boolean.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the double value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value cannot
|
||||
* be converted to a number.
|
||||
*/
|
||||
public double getDouble(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number
|
||||
? ((Number)object).doubleValue()
|
||||
: Double.parseDouble((String)object);
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("JSONArray[" + index +
|
||||
"] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the int value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value is not a number.
|
||||
*/
|
||||
public int getInt(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number
|
||||
? ((Number)object).intValue()
|
||||
: Integer.parseInt((String)object);
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("JSONArray[" + index +
|
||||
"] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the JSONArray associated with an index.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A JSONArray value.
|
||||
* @throws JSONException If there is no value for the index. or if the
|
||||
* value is not a JSONArray
|
||||
*/
|
||||
public JSONArray getJSONArray(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof JSONArray) {
|
||||
return (JSONArray)object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index +
|
||||
"] is not a JSONArray.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the JSONObject associated with an index.
|
||||
* @param index subscript
|
||||
* @return A JSONObject value.
|
||||
* @throws JSONException If there is no value for the index or if the
|
||||
* value is not a JSONObject
|
||||
*/
|
||||
public JSONObject getJSONObject(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof JSONObject) {
|
||||
return (JSONObject)object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index +
|
||||
"] is not a JSONObject.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the long value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value cannot
|
||||
* be converted to a number.
|
||||
*/
|
||||
public long getLong(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number
|
||||
? ((Number)object).longValue()
|
||||
: Long.parseLong((String)object);
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("JSONArray[" + index +
|
||||
"] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the string associated with an index.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A string value.
|
||||
* @throws JSONException If there is no string value for the index.
|
||||
*/
|
||||
public String getString(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof String) {
|
||||
return (String)object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] not a string.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the value is null.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return true if the value at the index is null, or if there is no value.
|
||||
*/
|
||||
public boolean isNull(int index) {
|
||||
return JSONObject.NULL.equals(this.opt(index));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a string from the contents of this JSONArray. The
|
||||
* <code>separator</code> string is inserted between each element.
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
* @param separator A string that will be inserted between the elements.
|
||||
* @return a string.
|
||||
* @throws JSONException If the array contains an invalid number.
|
||||
*/
|
||||
public String join(String separator) throws JSONException {
|
||||
int len = this.length();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
for (int i = 0; i < len; i += 1) {
|
||||
if (i > 0) {
|
||||
sb.append(separator);
|
||||
}
|
||||
sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the number of elements in the JSONArray, included nulls.
|
||||
*
|
||||
* @return The length (or size).
|
||||
*/
|
||||
public int length() {
|
||||
return this.myArrayList.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional object value associated with an index.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return An object value, or null if there is no
|
||||
* object at that index.
|
||||
*/
|
||||
public Object opt(int index) {
|
||||
return (index < 0 || index >= this.length())
|
||||
? null
|
||||
: this.myArrayList.get(index);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional boolean value associated with an index.
|
||||
* It returns false if there is no value at that index,
|
||||
* or if the value is not Boolean.TRUE or the String "true".
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The truth.
|
||||
*/
|
||||
public boolean optBoolean(int index) {
|
||||
return this.optBoolean(index, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional boolean value associated with an index.
|
||||
* It returns the defaultValue if there is no value at that index or if
|
||||
* it is not a Boolean or the String "true" or "false" (case insensitive).
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue A boolean default.
|
||||
* @return The truth.
|
||||
*/
|
||||
public boolean optBoolean(int index, boolean defaultValue) {
|
||||
try {
|
||||
return this.getBoolean(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional double value associated with an index.
|
||||
* NaN is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public double optDouble(int index) {
|
||||
return this.optDouble(index, Double.NaN);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional double value associated with an index.
|
||||
* The defaultValue is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index subscript
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public double optDouble(int index, double defaultValue) {
|
||||
try {
|
||||
return this.getDouble(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional int value associated with an index.
|
||||
* Zero is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public int optInt(int index) {
|
||||
return this.optInt(index, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional int value associated with an index.
|
||||
* The defaultValue is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public int optInt(int index, int defaultValue) {
|
||||
try {
|
||||
return this.getInt(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional JSONArray associated with an index.
|
||||
* @param index subscript
|
||||
* @return A JSONArray value, or null if the index has no value,
|
||||
* or if the value is not a JSONArray.
|
||||
*/
|
||||
public JSONArray optJSONArray(int index) {
|
||||
Object o = this.opt(index);
|
||||
return o instanceof JSONArray ? (JSONArray)o : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional JSONObject associated with an index.
|
||||
* Null is returned if the key is not found, or null if the index has
|
||||
* no value, or if the value is not a JSONObject.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A JSONObject value.
|
||||
*/
|
||||
public JSONObject optJSONObject(int index) {
|
||||
Object o = this.opt(index);
|
||||
return o instanceof JSONObject ? (JSONObject)o : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional long value associated with an index.
|
||||
* Zero is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public long optLong(int index) {
|
||||
return this.optLong(index, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional long value associated with an index.
|
||||
* The defaultValue is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public long optLong(int index, long defaultValue) {
|
||||
try {
|
||||
return this.getLong(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional string value associated with an index. It returns an
|
||||
* empty string if there is no value at that index. If the value
|
||||
* is not a string and is not null, then it is coverted to a string.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A String value.
|
||||
*/
|
||||
public String optString(int index) {
|
||||
return this.optString(index, "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the optional string associated with an index.
|
||||
* The defaultValue is returned if the key is not found.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return A String value.
|
||||
*/
|
||||
public String optString(int index, String defaultValue) {
|
||||
Object object = this.opt(index);
|
||||
return JSONObject.NULL.equals(object)
|
||||
? defaultValue
|
||||
: object.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append a boolean value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A boolean value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(boolean value) {
|
||||
this.put(value ? Boolean.TRUE : Boolean.FALSE);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a
|
||||
* JSONArray which is produced from a Collection.
|
||||
* @param value A Collection value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Collection value) {
|
||||
this.put(new JSONArray(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append a double value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A double value.
|
||||
* @throws JSONException if the value is not finite.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(double value) throws JSONException {
|
||||
Double d = new Double(value);
|
||||
JSONObject.testValidity(d);
|
||||
this.put(d);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append an int value. This increases the array's length by one.
|
||||
*
|
||||
* @param value An int value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(int value) {
|
||||
this.put(new Integer(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append an long value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A long value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(long value) {
|
||||
this.put(new Long(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a
|
||||
* JSONObject which is produced from a Map.
|
||||
* @param value A Map value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Map value) {
|
||||
this.put(new JSONObject(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append an object value. This increases the array's length by one.
|
||||
* @param value An object value. The value should be a
|
||||
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
|
||||
* JSONObject.NULL object.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Object value) {
|
||||
this.myArrayList.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Put or replace a boolean value in the JSONArray. If the index is greater
|
||||
* than the length of the JSONArray, then null elements will be added as
|
||||
* necessary to pad it out.
|
||||
* @param index The subscript.
|
||||
* @param value A boolean value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, boolean value) throws JSONException {
|
||||
this.put(index, value ? Boolean.TRUE : Boolean.FALSE);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a
|
||||
* JSONArray which is produced from a Collection.
|
||||
* @param index The subscript.
|
||||
* @param value A Collection value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the value is
|
||||
* not finite.
|
||||
*/
|
||||
public JSONArray put(int index, Collection value) throws JSONException {
|
||||
this.put(index, new JSONArray(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Put or replace a double value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad
|
||||
* it out.
|
||||
* @param index The subscript.
|
||||
* @param value A double value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the value is
|
||||
* not finite.
|
||||
*/
|
||||
public JSONArray put(int index, double value) throws JSONException {
|
||||
this.put(index, new Double(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Put or replace an int value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad
|
||||
* it out.
|
||||
* @param index The subscript.
|
||||
* @param value An int value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, int value) throws JSONException {
|
||||
this.put(index, new Integer(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Put or replace a long value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad
|
||||
* it out.
|
||||
* @param index The subscript.
|
||||
* @param value A long value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, long value) throws JSONException {
|
||||
this.put(index, new Long(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a
|
||||
* JSONObject that is produced from a Map.
|
||||
* @param index The subscript.
|
||||
* @param value The Map value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the the value is
|
||||
* an invalid number.
|
||||
*/
|
||||
public JSONArray put(int index, Map value) throws JSONException {
|
||||
this.put(index, new JSONObject(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Put or replace an object value in the JSONArray. If the index is greater
|
||||
* than the length of the JSONArray, then null elements will be added as
|
||||
* necessary to pad it out.
|
||||
* @param index The subscript.
|
||||
* @param value The value to put into the array. The value should be a
|
||||
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
|
||||
* JSONObject.NULL object.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the the value is
|
||||
* an invalid number.
|
||||
*/
|
||||
public JSONArray put(int index, Object value) throws JSONException {
|
||||
JSONObject.testValidity(value);
|
||||
if (index < 0) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
if (index < this.length()) {
|
||||
this.myArrayList.set(index, value);
|
||||
} else {
|
||||
while (index != this.length()) {
|
||||
this.put(JSONObject.NULL);
|
||||
}
|
||||
this.put(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove an index and close the hole.
|
||||
* @param index The index of the element to be removed.
|
||||
* @return The value that was associated with the index,
|
||||
* or null if there was no value.
|
||||
*/
|
||||
public Object remove(int index) {
|
||||
Object o = this.opt(index);
|
||||
this.myArrayList.remove(index);
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Produce a JSONObject by combining a JSONArray of names with the values
|
||||
* of this JSONArray.
|
||||
* @param names A JSONArray containing a list of key strings. These will be
|
||||
* paired with the values.
|
||||
* @return A JSONObject, or null if there are no names or if this JSONArray
|
||||
* has no values.
|
||||
* @throws JSONException If any of the names are null.
|
||||
*/
|
||||
public JSONObject toJSONObject(JSONArray names) throws JSONException {
|
||||
if (names == null || names.length() == 0 || this.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
JSONObject jo = new JSONObject();
|
||||
for (int i = 0; i < names.length(); i += 1) {
|
||||
jo.put(names.getString(i), this.opt(i));
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a JSON text of this JSONArray. For compactness, no
|
||||
* unnecessary whitespace is added. If it is not possible to produce a
|
||||
* syntactically correct JSON text then null will be returned instead. This
|
||||
* could occur if the array contains an invalid number.
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @return a printable, displayable, transmittable
|
||||
* representation of the array.
|
||||
*/
|
||||
public String toString() {
|
||||
try {
|
||||
return '[' + this.join(",") + ']';
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a prettyprinted JSON text of this JSONArray.
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
* @param indentFactor The number of spaces to add to each level of
|
||||
* indentation.
|
||||
* @return a printable, displayable, transmittable
|
||||
* representation of the object, beginning
|
||||
* with <code>[</code> <small>(left bracket)</small> and ending
|
||||
* with <code>]</code> <small>(right bracket)</small>.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public String toString(int indentFactor) throws JSONException {
|
||||
return this.toString(indentFactor, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a prettyprinted JSON text of this JSONArray.
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
* @param indentFactor The number of spaces to add to each level of
|
||||
* indentation.
|
||||
* @param indent The indention of the top level.
|
||||
* @return a printable, displayable, transmittable
|
||||
* representation of the array.
|
||||
* @throws JSONException
|
||||
*/
|
||||
String toString(int indentFactor, int indent) throws JSONException {
|
||||
int len = this.length();
|
||||
if (len == 0) {
|
||||
return "[]";
|
||||
}
|
||||
int i;
|
||||
StringBuffer sb = new StringBuffer("[");
|
||||
if (len == 1) {
|
||||
sb.append(JSONObject.valueToString(this.myArrayList.get(0),
|
||||
indentFactor, indent));
|
||||
} else {
|
||||
int newindent = indent + indentFactor;
|
||||
sb.append('\n');
|
||||
for (i = 0; i < len; i += 1) {
|
||||
if (i > 0) {
|
||||
sb.append(",\n");
|
||||
}
|
||||
for (int j = 0; j < newindent; j += 1) {
|
||||
sb.append(' ');
|
||||
}
|
||||
sb.append(JSONObject.valueToString(this.myArrayList.get(i),
|
||||
indentFactor, newindent));
|
||||
}
|
||||
sb.append('\n');
|
||||
for (i = 0; i < indent; i += 1) {
|
||||
sb.append(' ');
|
||||
}
|
||||
}
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write the contents of the JSONArray as JSON text to a writer.
|
||||
* For compactness, no whitespace is added.
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @return The writer.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public Writer write(Writer writer) throws JSONException {
|
||||
try {
|
||||
boolean b = false;
|
||||
int len = this.length();
|
||||
|
||||
writer.write('[');
|
||||
|
||||
for (int i = 0; i < len; i += 1) {
|
||||
if (b) {
|
||||
writer.write(',');
|
||||
}
|
||||
Object v = this.myArrayList.get(i);
|
||||
if (v instanceof JSONObject) {
|
||||
((JSONObject)v).write(writer);
|
||||
} else if (v instanceof JSONArray) {
|
||||
((JSONArray)v).write(writer);
|
||||
} else {
|
||||
writer.write(JSONObject.valueToString(v));
|
||||
}
|
||||
b = true;
|
||||
}
|
||||
writer.write(']');
|
||||
return writer;
|
||||
} catch (IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.json;
|
||||
|
||||
/**
|
||||
* The JSONException is thrown by the JSON.org classes when things are amiss.
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class JSONException extends Exception {
|
||||
private static final long serialVersionUID = 0;
|
||||
private Throwable cause;
|
||||
|
||||
/**
|
||||
* Constructs a JSONException with an explanatory message.
|
||||
* @param message Detail about the reason for the exception.
|
||||
*/
|
||||
public JSONException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JSONException(Throwable cause) {
|
||||
super(cause.getMessage());
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public Throwable getCause() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
+465
@@ -0,0 +1,465 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2008 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
/**
|
||||
* This provides static methods to convert an XML text into a JSONArray or
|
||||
* JSONObject, and to covert a JSONArray or JSONObject into an XML text using
|
||||
* the JsonML transform.
|
||||
* @author JSON.org
|
||||
* @version 2011-11-24
|
||||
*/
|
||||
public class JSONML {
|
||||
|
||||
/**
|
||||
* Parse XML values and store them in a JSONArray.
|
||||
* @param x The XMLTokener containing the source string.
|
||||
* @param arrayForm true if array form, false if object form.
|
||||
* @param ja The JSONArray that is containing the current tag or null
|
||||
* if we are at the outermost level.
|
||||
* @return A JSONArray if the value is the outermost tag, otherwise null.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private static Object parse(
|
||||
XMLTokener x,
|
||||
boolean arrayForm,
|
||||
JSONArray ja
|
||||
) throws JSONException {
|
||||
String attribute;
|
||||
char c;
|
||||
String closeTag = null;
|
||||
int i;
|
||||
JSONArray newja = null;
|
||||
JSONObject newjo = null;
|
||||
Object token;
|
||||
String tagName = null;
|
||||
|
||||
// Test for and skip past these forms:
|
||||
// <!-- ... -->
|
||||
// <![ ... ]]>
|
||||
// <! ... >
|
||||
// <? ... ?>
|
||||
|
||||
while (true) {
|
||||
if (!x.more()) {
|
||||
throw x.syntaxError("Bad XML");
|
||||
}
|
||||
token = x.nextContent();
|
||||
if (token == XML.LT) {
|
||||
token = x.nextToken();
|
||||
if (token instanceof Character) {
|
||||
if (token == XML.SLASH) {
|
||||
|
||||
// Close tag </
|
||||
|
||||
token = x.nextToken();
|
||||
if (!(token instanceof String)) {
|
||||
throw new JSONException(
|
||||
"Expected a closing name instead of '" +
|
||||
token + "'.");
|
||||
}
|
||||
if (x.nextToken() != XML.GT) {
|
||||
throw x.syntaxError("Misshaped close tag");
|
||||
}
|
||||
return token;
|
||||
} else if (token == XML.BANG) {
|
||||
|
||||
// <!
|
||||
|
||||
c = x.next();
|
||||
if (c == '-') {
|
||||
if (x.next() == '-') {
|
||||
x.skipPast("-->");
|
||||
}
|
||||
x.back();
|
||||
} else if (c == '[') {
|
||||
token = x.nextToken();
|
||||
if (token.equals("CDATA") && x.next() == '[') {
|
||||
if (ja != null) {
|
||||
ja.put(x.nextCDATA());
|
||||
}
|
||||
} else {
|
||||
throw x.syntaxError("Expected 'CDATA['");
|
||||
}
|
||||
} else {
|
||||
i = 1;
|
||||
do {
|
||||
token = x.nextMeta();
|
||||
if (token == null) {
|
||||
throw x.syntaxError("Missing '>' after '<!'.");
|
||||
} else if (token == XML.LT) {
|
||||
i += 1;
|
||||
} else if (token == XML.GT) {
|
||||
i -= 1;
|
||||
}
|
||||
} while (i > 0);
|
||||
}
|
||||
} else if (token == XML.QUEST) {
|
||||
|
||||
// <?
|
||||
|
||||
x.skipPast("?>");
|
||||
} else {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
|
||||
// Open tag <
|
||||
|
||||
} else {
|
||||
if (!(token instanceof String)) {
|
||||
throw x.syntaxError("Bad tagName '" + token + "'.");
|
||||
}
|
||||
tagName = (String)token;
|
||||
newja = new JSONArray();
|
||||
newjo = new JSONObject();
|
||||
if (arrayForm) {
|
||||
newja.put(tagName);
|
||||
if (ja != null) {
|
||||
ja.put(newja);
|
||||
}
|
||||
} else {
|
||||
newjo.put("tagName", tagName);
|
||||
if (ja != null) {
|
||||
ja.put(newjo);
|
||||
}
|
||||
}
|
||||
token = null;
|
||||
for (;;) {
|
||||
if (token == null) {
|
||||
token = x.nextToken();
|
||||
}
|
||||
if (token == null) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
if (!(token instanceof String)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// attribute = value
|
||||
|
||||
attribute = (String)token;
|
||||
if (!arrayForm && (attribute == "tagName" || attribute == "childNode")) {
|
||||
throw x.syntaxError("Reserved attribute.");
|
||||
}
|
||||
token = x.nextToken();
|
||||
if (token == XML.EQ) {
|
||||
token = x.nextToken();
|
||||
if (!(token instanceof String)) {
|
||||
throw x.syntaxError("Missing value");
|
||||
}
|
||||
newjo.accumulate(attribute, XML.stringToValue((String)token));
|
||||
token = null;
|
||||
} else {
|
||||
newjo.accumulate(attribute, "");
|
||||
}
|
||||
}
|
||||
if (arrayForm && newjo.length() > 0) {
|
||||
newja.put(newjo);
|
||||
}
|
||||
|
||||
// Empty tag <.../>
|
||||
|
||||
if (token == XML.SLASH) {
|
||||
if (x.nextToken() != XML.GT) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
if (ja == null) {
|
||||
if (arrayForm) {
|
||||
return newja;
|
||||
} else {
|
||||
return newjo;
|
||||
}
|
||||
}
|
||||
|
||||
// Content, between <...> and </...>
|
||||
|
||||
} else {
|
||||
if (token != XML.GT) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
closeTag = (String)parse(x, arrayForm, newja);
|
||||
if (closeTag != null) {
|
||||
if (!closeTag.equals(tagName)) {
|
||||
throw x.syntaxError("Mismatched '" + tagName +
|
||||
"' and '" + closeTag + "'");
|
||||
}
|
||||
tagName = null;
|
||||
if (!arrayForm && newja.length() > 0) {
|
||||
newjo.put("childNodes", newja);
|
||||
}
|
||||
if (ja == null) {
|
||||
if (arrayForm) {
|
||||
return newja;
|
||||
} else {
|
||||
return newjo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ja != null) {
|
||||
ja.put(token instanceof String
|
||||
? XML.stringToValue((String)token)
|
||||
: token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONArray using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONArray in which the first element is the tag name. If the tag has
|
||||
* attributes, then the second element will be JSONObject containing the
|
||||
* name/value pairs. If the tag contains children, then strings and
|
||||
* JSONArrays will represent the child tags.
|
||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
* @param string The source string.
|
||||
* @return A JSONArray containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(String string) throws JSONException {
|
||||
return toJSONArray(new XMLTokener(string));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONArray using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONArray in which the first element is the tag name. If the tag has
|
||||
* attributes, then the second element will be JSONObject containing the
|
||||
* name/value pairs. If the tag contains children, then strings and
|
||||
* JSONArrays will represent the child content and tags.
|
||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
* @param x An XMLTokener.
|
||||
* @return A JSONArray containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
|
||||
return (JSONArray)parse(x, true, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONObject using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONObject with a "tagName" property. If the tag has attributes, then
|
||||
* the attributes will be in the JSONObject as properties. If the tag
|
||||
* contains children, the object will have a "childNodes" property which
|
||||
* will be an array of strings and JsonML JSONObjects.
|
||||
|
||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
* @param x An XMLTokener of the XML source text.
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
|
||||
return (JSONObject)parse(x, false, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONObject using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONObject with a "tagName" property. If the tag has attributes, then
|
||||
* the attributes will be in the JSONObject as properties. If the tag
|
||||
* contains children, the object will have a "childNodes" property which
|
||||
* will be an array of strings and JsonML JSONObjects.
|
||||
|
||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
* @param string The XML source text.
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
return toJSONObject(new XMLTokener(string));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reverse the JSONML transformation, making an XML text from a JSONArray.
|
||||
* @param ja A JSONArray.
|
||||
* @return An XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONArray ja) throws JSONException {
|
||||
int i;
|
||||
JSONObject jo;
|
||||
String key;
|
||||
Iterator keys;
|
||||
int length;
|
||||
Object object;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
String tagName;
|
||||
String value;
|
||||
|
||||
// Emit <tagName
|
||||
|
||||
tagName = ja.getString(0);
|
||||
XML.noSpace(tagName);
|
||||
tagName = XML.escape(tagName);
|
||||
sb.append('<');
|
||||
sb.append(tagName);
|
||||
|
||||
object = ja.opt(1);
|
||||
if (object instanceof JSONObject) {
|
||||
i = 2;
|
||||
jo = (JSONObject)object;
|
||||
|
||||
// Emit the attributes
|
||||
|
||||
keys = jo.keys();
|
||||
while (keys.hasNext()) {
|
||||
key = keys.next().toString();
|
||||
XML.noSpace(key);
|
||||
value = jo.optString(key);
|
||||
if (value != null) {
|
||||
sb.append(' ');
|
||||
sb.append(XML.escape(key));
|
||||
sb.append('=');
|
||||
sb.append('"');
|
||||
sb.append(XML.escape(value));
|
||||
sb.append('"');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i = 1;
|
||||
}
|
||||
|
||||
//Emit content in body
|
||||
|
||||
length = ja.length();
|
||||
if (i >= length) {
|
||||
sb.append('/');
|
||||
sb.append('>');
|
||||
} else {
|
||||
sb.append('>');
|
||||
do {
|
||||
object = ja.get(i);
|
||||
i += 1;
|
||||
if (object != null) {
|
||||
if (object instanceof String) {
|
||||
sb.append(XML.escape(object.toString()));
|
||||
} else if (object instanceof JSONObject) {
|
||||
sb.append(toString((JSONObject)object));
|
||||
} else if (object instanceof JSONArray) {
|
||||
sb.append(toString((JSONArray)object));
|
||||
}
|
||||
}
|
||||
} while (i < length);
|
||||
sb.append('<');
|
||||
sb.append('/');
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the JSONML transformation, making an XML text from a JSONObject.
|
||||
* The JSONObject must contain a "tagName" property. If it has children,
|
||||
* then it must have a "childNodes" property containing an array of objects.
|
||||
* The other properties are attributes with string values.
|
||||
* @param jo A JSONObject.
|
||||
* @return An XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int i;
|
||||
JSONArray ja;
|
||||
String key;
|
||||
Iterator keys;
|
||||
int length;
|
||||
Object object;
|
||||
String tagName;
|
||||
String value;
|
||||
|
||||
//Emit <tagName
|
||||
|
||||
tagName = jo.optString("tagName");
|
||||
if (tagName == null) {
|
||||
return XML.escape(jo.toString());
|
||||
}
|
||||
XML.noSpace(tagName);
|
||||
tagName = XML.escape(tagName);
|
||||
sb.append('<');
|
||||
sb.append(tagName);
|
||||
|
||||
//Emit the attributes
|
||||
|
||||
keys = jo.keys();
|
||||
while (keys.hasNext()) {
|
||||
key = keys.next().toString();
|
||||
if (!"tagName".equals(key) && !"childNodes".equals(key)) {
|
||||
XML.noSpace(key);
|
||||
value = jo.optString(key);
|
||||
if (value != null) {
|
||||
sb.append(' ');
|
||||
sb.append(XML.escape(key));
|
||||
sb.append('=');
|
||||
sb.append('"');
|
||||
sb.append(XML.escape(value));
|
||||
sb.append('"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Emit content in body
|
||||
|
||||
ja = jo.optJSONArray("childNodes");
|
||||
if (ja == null) {
|
||||
sb.append('/');
|
||||
sb.append('>');
|
||||
} else {
|
||||
sb.append('>');
|
||||
length = ja.length();
|
||||
for (i = 0; i < length; i += 1) {
|
||||
object = ja.get(i);
|
||||
if (object != null) {
|
||||
if (object instanceof String) {
|
||||
sb.append(XML.escape(object.toString()));
|
||||
} else if (object instanceof JSONObject) {
|
||||
sb.append(toString((JSONObject)object));
|
||||
} else if (object instanceof JSONArray) {
|
||||
sb.append(toString((JSONArray)object));
|
||||
} else {
|
||||
sb.append(object.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append('<');
|
||||
sb.append('/');
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
package org.json;
|
||||
/**
|
||||
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
|
||||
* method so that a class can change the behavior of
|
||||
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
|
||||
* and <code>JSONWriter.value(</code>Object<code>)</code>. The
|
||||
* <code>toJSONString</code> method will be used instead of the default behavior
|
||||
* of using the Object's <code>toString()</code> method and quoting the result.
|
||||
*/
|
||||
public interface JSONString {
|
||||
/**
|
||||
* The <code>toJSONString</code> method allows a class to produce its own JSON
|
||||
* serialization.
|
||||
*
|
||||
* @return A strictly syntactically correct JSON text.
|
||||
*/
|
||||
public String toJSONString();
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2006 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* JSONStringer provides a quick and convenient way of producing JSON text.
|
||||
* The texts produced strictly conform to JSON syntax rules. No whitespace is
|
||||
* added, so the results are ready for transmission or storage. Each instance of
|
||||
* JSONStringer can produce one JSON text.
|
||||
* <p>
|
||||
* A JSONStringer instance provides a <code>value</code> method for appending
|
||||
* values to the
|
||||
* text, and a <code>key</code>
|
||||
* method for adding keys before values in objects. There are <code>array</code>
|
||||
* and <code>endArray</code> methods that make and bound array values, and
|
||||
* <code>object</code> and <code>endObject</code> methods which make and bound
|
||||
* object values. All of these methods return the JSONWriter instance,
|
||||
* permitting cascade style. For example, <pre>
|
||||
* myString = new JSONStringer()
|
||||
* .object()
|
||||
* .key("JSON")
|
||||
* .value("Hello, World!")
|
||||
* .endObject()
|
||||
* .toString();</pre> which produces the string <pre>
|
||||
* {"JSON":"Hello, World!"}</pre>
|
||||
* <p>
|
||||
* The first method called must be <code>array</code> or <code>object</code>.
|
||||
* There are no methods for adding commas or colons. JSONStringer adds them for
|
||||
* you. Objects and arrays can be nested up to 20 levels deep.
|
||||
* <p>
|
||||
* This can sometimes be easier than using a JSONObject to build a string.
|
||||
* @author JSON.org
|
||||
* @version 2008-09-18
|
||||
*/
|
||||
public class JSONStringer extends JSONWriter {
|
||||
/**
|
||||
* Make a fresh JSONStringer. It can be used to build one JSON text.
|
||||
*/
|
||||
public JSONStringer() {
|
||||
super(new StringWriter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JSON text. This method is used to obtain the product of the
|
||||
* JSONStringer instance. It will return <code>null</code> if there was a
|
||||
* problem in the construction of the JSON text (such as the calls to
|
||||
* <code>array</code> were not properly balanced with calls to
|
||||
* <code>endArray</code>).
|
||||
* @return The JSON text.
|
||||
*/
|
||||
public String toString() {
|
||||
return this.mode == 'd' ? this.writer.toString() : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,446 @@
|
||||
package org.json;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A JSONTokener takes a source string and extracts characters and tokens from
|
||||
* it. It is used by the JSONObject and JSONArray constructors to parse
|
||||
* JSON source strings.
|
||||
* @author JSON.org
|
||||
* @version 2011-11-24
|
||||
*/
|
||||
public class JSONTokener {
|
||||
|
||||
private int character;
|
||||
private boolean eof;
|
||||
private int index;
|
||||
private int line;
|
||||
private char previous;
|
||||
private final Reader reader;
|
||||
private boolean usePrevious;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a Reader.
|
||||
*
|
||||
* @param reader A reader.
|
||||
*/
|
||||
public JSONTokener(Reader reader) {
|
||||
this.reader = reader.markSupported()
|
||||
? reader
|
||||
: new BufferedReader(reader);
|
||||
this.eof = false;
|
||||
this.usePrevious = false;
|
||||
this.previous = 0;
|
||||
this.index = 0;
|
||||
this.character = 1;
|
||||
this.line = 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from an InputStream.
|
||||
*/
|
||||
public JSONTokener(InputStream inputStream) throws JSONException {
|
||||
this(new InputStreamReader(inputStream));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a string.
|
||||
*
|
||||
* @param s A source string.
|
||||
*/
|
||||
public JSONTokener(String s) {
|
||||
this(new StringReader(s));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Back up one character. This provides a sort of lookahead capability,
|
||||
* so that you can test for a digit or letter before attempting to parse
|
||||
* the next number or identifier.
|
||||
*/
|
||||
public void back() throws JSONException {
|
||||
if (this.usePrevious || this.index <= 0) {
|
||||
throw new JSONException("Stepping back two steps is not supported");
|
||||
}
|
||||
this.index -= 1;
|
||||
this.character -= 1;
|
||||
this.usePrevious = true;
|
||||
this.eof = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the hex value of a character (base16).
|
||||
* @param c A character between '0' and '9' or between 'A' and 'F' or
|
||||
* between 'a' and 'f'.
|
||||
* @return An int between 0 and 15, or -1 if c was not a hex digit.
|
||||
*/
|
||||
public static int dehexchar(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return c - ('A' - 10);
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return c - ('a' - 10);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean end() {
|
||||
return this.eof && !this.usePrevious;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the source string still contains characters that next()
|
||||
* can consume.
|
||||
* @return true if not yet at the end of the source.
|
||||
*/
|
||||
public boolean more() throws JSONException {
|
||||
this.next();
|
||||
if (this.end()) {
|
||||
return false;
|
||||
}
|
||||
this.back();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next character in the source string.
|
||||
*
|
||||
* @return The next character, or 0 if past the end of the source string.
|
||||
*/
|
||||
public char next() throws JSONException {
|
||||
int c;
|
||||
if (this.usePrevious) {
|
||||
this.usePrevious = false;
|
||||
c = this.previous;
|
||||
} else {
|
||||
try {
|
||||
c = this.reader.read();
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
|
||||
if (c <= 0) { // End of stream
|
||||
this.eof = true;
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
this.index += 1;
|
||||
if (this.previous == '\r') {
|
||||
this.line += 1;
|
||||
this.character = c == '\n' ? 0 : 1;
|
||||
} else if (c == '\n') {
|
||||
this.line += 1;
|
||||
this.character = 0;
|
||||
} else {
|
||||
this.character += 1;
|
||||
}
|
||||
this.previous = (char) c;
|
||||
return this.previous;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Consume the next character, and check that it matches a specified
|
||||
* character.
|
||||
* @param c The character to match.
|
||||
* @return The character.
|
||||
* @throws JSONException if the character does not match.
|
||||
*/
|
||||
public char next(char c) throws JSONException {
|
||||
char n = this.next();
|
||||
if (n != c) {
|
||||
throw this.syntaxError("Expected '" + c + "' and instead saw '" +
|
||||
n + "'");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next n characters.
|
||||
*
|
||||
* @param n The number of characters to take.
|
||||
* @return A string of n characters.
|
||||
* @throws JSONException
|
||||
* Substring bounds error if there are not
|
||||
* n characters remaining in the source string.
|
||||
*/
|
||||
public String next(int n) throws JSONException {
|
||||
if (n == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
char[] chars = new char[n];
|
||||
int pos = 0;
|
||||
|
||||
while (pos < n) {
|
||||
chars[pos] = this.next();
|
||||
if (this.end()) {
|
||||
throw this.syntaxError("Substring bounds error");
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next char in the string, skipping whitespace.
|
||||
* @throws JSONException
|
||||
* @return A character, or 0 if there are no more characters.
|
||||
*/
|
||||
public char nextClean() throws JSONException {
|
||||
for (;;) {
|
||||
char c = this.next();
|
||||
if (c == 0 || c > ' ') {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the characters up to the next close quote character.
|
||||
* Backslash processing is done. The formal JSON format does not
|
||||
* allow strings in single quotes, but an implementation is allowed to
|
||||
* accept them.
|
||||
* @param quote The quoting character, either
|
||||
* <code>"</code> <small>(double quote)</small> or
|
||||
* <code>'</code> <small>(single quote)</small>.
|
||||
* @return A String.
|
||||
* @throws JSONException Unterminated string.
|
||||
*/
|
||||
public String nextString(char quote) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '\n':
|
||||
case '\r':
|
||||
throw this.syntaxError("Unterminated string");
|
||||
case '\\':
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
break;
|
||||
case 'u':
|
||||
sb.append((char)Integer.parseInt(this.next(4), 16));
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
case '/':
|
||||
sb.append(c);
|
||||
break;
|
||||
default:
|
||||
throw this.syntaxError("Illegal escape.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (c == quote) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the text up but not including the specified character or the
|
||||
* end of line, whichever comes first.
|
||||
* @param delimiter A delimiter character.
|
||||
* @return A string.
|
||||
*/
|
||||
public String nextTo(char delimiter) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
char c = this.next();
|
||||
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the text up but not including one of the specified delimiter
|
||||
* characters or the end of line, whichever comes first.
|
||||
* @param delimiters A set of delimiter characters.
|
||||
* @return A string, trimmed.
|
||||
*/
|
||||
public String nextTo(String delimiters) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
c = this.next();
|
||||
if (delimiters.indexOf(c) >= 0 || c == 0 ||
|
||||
c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next value. The value can be a Boolean, Double, Integer,
|
||||
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
|
||||
* @throws JSONException If syntax error.
|
||||
*
|
||||
* @return An object.
|
||||
*/
|
||||
public Object nextValue() throws JSONException {
|
||||
char c = this.nextClean();
|
||||
String string;
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\'':
|
||||
return this.nextString(c);
|
||||
case '{':
|
||||
this.back();
|
||||
return new JSONObject(this);
|
||||
case '[':
|
||||
this.back();
|
||||
return new JSONArray(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle unquoted text. This could be the values true, false, or
|
||||
* null, or it can be a number. An implementation (such as this one)
|
||||
* is allowed to also accept non-standard forms.
|
||||
*
|
||||
* Accumulate characters until we reach the end of the text or a
|
||||
* formatting character.
|
||||
*/
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||
sb.append(c);
|
||||
c = this.next();
|
||||
}
|
||||
this.back();
|
||||
|
||||
string = sb.toString().trim();
|
||||
if ("".equals(string)) {
|
||||
throw this.syntaxError("Missing value");
|
||||
}
|
||||
return JSONObject.stringToValue(string);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Skip characters until the next character is the requested character.
|
||||
* If the requested character is not found, no characters are skipped.
|
||||
* @param to A character to skip to.
|
||||
* @return The requested character, or zero if the requested character
|
||||
* is not found.
|
||||
*/
|
||||
public char skipTo(char to) throws JSONException {
|
||||
char c;
|
||||
try {
|
||||
int startIndex = this.index;
|
||||
int startCharacter = this.character;
|
||||
int startLine = this.line;
|
||||
this.reader.mark(Integer.MAX_VALUE);
|
||||
do {
|
||||
c = this.next();
|
||||
if (c == 0) {
|
||||
this.reader.reset();
|
||||
this.index = startIndex;
|
||||
this.character = startCharacter;
|
||||
this.line = startLine;
|
||||
return c;
|
||||
}
|
||||
} while (c != to);
|
||||
} catch (IOException exc) {
|
||||
throw new JSONException(exc);
|
||||
}
|
||||
|
||||
this.back();
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a JSONException to signal a syntax error.
|
||||
*
|
||||
* @param message The error message.
|
||||
* @return A JSONException object, suitable for throwing
|
||||
*/
|
||||
public JSONException syntaxError(String message) {
|
||||
return new JSONException(message + this.toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a printable string of this JSONTokener.
|
||||
*
|
||||
* @return " at {index} [character {character} line {line}]"
|
||||
*/
|
||||
public String toString() {
|
||||
return " at " + this.index + " [character " + this.character + " line " +
|
||||
this.line + "]";
|
||||
}
|
||||
}
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
package org.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/*
|
||||
Copyright (c) 2006 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* JSONWriter provides a quick and convenient way of producing JSON text.
|
||||
* The texts produced strictly conform to JSON syntax rules. No whitespace is
|
||||
* added, so the results are ready for transmission or storage. Each instance of
|
||||
* JSONWriter can produce one JSON text.
|
||||
* <p>
|
||||
* A JSONWriter instance provides a <code>value</code> method for appending
|
||||
* values to the
|
||||
* text, and a <code>key</code>
|
||||
* method for adding keys before values in objects. There are <code>array</code>
|
||||
* and <code>endArray</code> methods that make and bound array values, and
|
||||
* <code>object</code> and <code>endObject</code> methods which make and bound
|
||||
* object values. All of these methods return the JSONWriter instance,
|
||||
* permitting a cascade style. For example, <pre>
|
||||
* new JSONWriter(myWriter)
|
||||
* .object()
|
||||
* .key("JSON")
|
||||
* .value("Hello, World!")
|
||||
* .endObject();</pre> which writes <pre>
|
||||
* {"JSON":"Hello, World!"}</pre>
|
||||
* <p>
|
||||
* The first method called must be <code>array</code> or <code>object</code>.
|
||||
* There are no methods for adding commas or colons. JSONWriter adds them for
|
||||
* you. Objects and arrays can be nested up to 20 levels deep.
|
||||
* <p>
|
||||
* This can sometimes be easier than using a JSONObject to build a string.
|
||||
* @author JSON.org
|
||||
* @version 2011-11-24
|
||||
*/
|
||||
public class JSONWriter {
|
||||
private static final int maxdepth = 200;
|
||||
|
||||
/**
|
||||
* The comma flag determines if a comma should be output before the next
|
||||
* value.
|
||||
*/
|
||||
private boolean comma;
|
||||
|
||||
/**
|
||||
* The current mode. Values:
|
||||
* 'a' (array),
|
||||
* 'd' (done),
|
||||
* 'i' (initial),
|
||||
* 'k' (key),
|
||||
* 'o' (object).
|
||||
*/
|
||||
protected char mode;
|
||||
|
||||
/**
|
||||
* The object/array stack.
|
||||
*/
|
||||
private final JSONObject stack[];
|
||||
|
||||
/**
|
||||
* The stack top index. A value of 0 indicates that the stack is empty.
|
||||
*/
|
||||
private int top;
|
||||
|
||||
/**
|
||||
* The writer that will receive the output.
|
||||
*/
|
||||
protected Writer writer;
|
||||
|
||||
/**
|
||||
* Make a fresh JSONWriter. It can be used to build one JSON text.
|
||||
*/
|
||||
public JSONWriter(Writer w) {
|
||||
this.comma = false;
|
||||
this.mode = 'i';
|
||||
this.stack = new JSONObject[maxdepth];
|
||||
this.top = 0;
|
||||
this.writer = w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a value.
|
||||
* @param string A string value.
|
||||
* @return this
|
||||
* @throws JSONException If the value is out of sequence.
|
||||
*/
|
||||
private JSONWriter append(String string) throws JSONException {
|
||||
if (string == null) {
|
||||
throw new JSONException("Null pointer");
|
||||
}
|
||||
if (this.mode == 'o' || this.mode == 'a') {
|
||||
try {
|
||||
if (this.comma && this.mode == 'a') {
|
||||
this.writer.write(',');
|
||||
}
|
||||
this.writer.write(string);
|
||||
} catch (IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
if (this.mode == 'o') {
|
||||
this.mode = 'k';
|
||||
}
|
||||
this.comma = true;
|
||||
return this;
|
||||
}
|
||||
throw new JSONException("Value out of sequence.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin appending a new array. All values until the balancing
|
||||
* <code>endArray</code> will be appended to this array. The
|
||||
* <code>endArray</code> method must be called to mark the array's end.
|
||||
* @return this
|
||||
* @throws JSONException If the nesting is too deep, or if the object is
|
||||
* started in the wrong place (for example as a key or after the end of the
|
||||
* outermost array or object).
|
||||
*/
|
||||
public JSONWriter array() throws JSONException {
|
||||
if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
|
||||
this.push(null);
|
||||
this.append("[");
|
||||
this.comma = false;
|
||||
return this;
|
||||
}
|
||||
throw new JSONException("Misplaced array.");
|
||||
}
|
||||
|
||||
/**
|
||||
* End something.
|
||||
* @param mode Mode
|
||||
* @param c Closing character
|
||||
* @return this
|
||||
* @throws JSONException If unbalanced.
|
||||
*/
|
||||
private JSONWriter end(char mode, char c) throws JSONException {
|
||||
if (this.mode != mode) {
|
||||
throw new JSONException(mode == 'a'
|
||||
? "Misplaced endArray."
|
||||
: "Misplaced endObject.");
|
||||
}
|
||||
this.pop(mode);
|
||||
try {
|
||||
this.writer.write(c);
|
||||
} catch (IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
this.comma = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* End an array. This method most be called to balance calls to
|
||||
* <code>array</code>.
|
||||
* @return this
|
||||
* @throws JSONException If incorrectly nested.
|
||||
*/
|
||||
public JSONWriter endArray() throws JSONException {
|
||||
return this.end('a', ']');
|
||||
}
|
||||
|
||||
/**
|
||||
* End an object. This method most be called to balance calls to
|
||||
* <code>object</code>.
|
||||
* @return this
|
||||
* @throws JSONException If incorrectly nested.
|
||||
*/
|
||||
public JSONWriter endObject() throws JSONException {
|
||||
return this.end('k', '}');
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a key. The key will be associated with the next value. In an
|
||||
* object, every value must be preceded by a key.
|
||||
* @param string A key string.
|
||||
* @return this
|
||||
* @throws JSONException If the key is out of place. For example, keys
|
||||
* do not belong in arrays or if the key is null.
|
||||
*/
|
||||
public JSONWriter key(String string) throws JSONException {
|
||||
if (string == null) {
|
||||
throw new JSONException("Null key.");
|
||||
}
|
||||
if (this.mode == 'k') {
|
||||
try {
|
||||
this.stack[this.top - 1].putOnce(string, Boolean.TRUE);
|
||||
if (this.comma) {
|
||||
this.writer.write(',');
|
||||
}
|
||||
this.writer.write(JSONObject.quote(string));
|
||||
this.writer.write(':');
|
||||
this.comma = false;
|
||||
this.mode = 'o';
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
}
|
||||
throw new JSONException("Misplaced key.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begin appending a new object. All keys and values until the balancing
|
||||
* <code>endObject</code> will be appended to this object. The
|
||||
* <code>endObject</code> method must be called to mark the object's end.
|
||||
* @return this
|
||||
* @throws JSONException If the nesting is too deep, or if the object is
|
||||
* started in the wrong place (for example as a key or after the end of the
|
||||
* outermost array or object).
|
||||
*/
|
||||
public JSONWriter object() throws JSONException {
|
||||
if (this.mode == 'i') {
|
||||
this.mode = 'o';
|
||||
}
|
||||
if (this.mode == 'o' || this.mode == 'a') {
|
||||
this.append("{");
|
||||
this.push(new JSONObject());
|
||||
this.comma = false;
|
||||
return this;
|
||||
}
|
||||
throw new JSONException("Misplaced object.");
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pop an array or object scope.
|
||||
* @param c The scope to close.
|
||||
* @throws JSONException If nesting is wrong.
|
||||
*/
|
||||
private void pop(char c) throws JSONException {
|
||||
if (this.top <= 0) {
|
||||
throw new JSONException("Nesting error.");
|
||||
}
|
||||
char m = this.stack[this.top - 1] == null ? 'a' : 'k';
|
||||
if (m != c) {
|
||||
throw new JSONException("Nesting error.");
|
||||
}
|
||||
this.top -= 1;
|
||||
this.mode = this.top == 0
|
||||
? 'd'
|
||||
: this.stack[this.top - 1] == null
|
||||
? 'a'
|
||||
: 'k';
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an array or object scope.
|
||||
* @param c The scope to open.
|
||||
* @throws JSONException If nesting is too deep.
|
||||
*/
|
||||
private void push(JSONObject jo) throws JSONException {
|
||||
if (this.top >= maxdepth) {
|
||||
throw new JSONException("Nesting too deep.");
|
||||
}
|
||||
this.stack[this.top] = jo;
|
||||
this.mode = jo == null ? 'a' : 'k';
|
||||
this.top += 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append either the value <code>true</code> or the value
|
||||
* <code>false</code>.
|
||||
* @param b A boolean.
|
||||
* @return this
|
||||
* @throws JSONException
|
||||
*/
|
||||
public JSONWriter value(boolean b) throws JSONException {
|
||||
return this.append(b ? "true" : "false");
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a double value.
|
||||
* @param d A double.
|
||||
* @return this
|
||||
* @throws JSONException If the number is not finite.
|
||||
*/
|
||||
public JSONWriter value(double d) throws JSONException {
|
||||
return this.value(new Double(d));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a long value.
|
||||
* @param l A long.
|
||||
* @return this
|
||||
* @throws JSONException
|
||||
*/
|
||||
public JSONWriter value(long l) throws JSONException {
|
||||
return this.append(Long.toString(l));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append an object value.
|
||||
* @param object The object to append. It can be null, or a Boolean, Number,
|
||||
* String, JSONObject, or JSONArray, or an object that implements JSONString.
|
||||
* @return this
|
||||
* @throws JSONException If the value is out of sequence.
|
||||
*/
|
||||
public JSONWriter value(Object object) throws JSONException {
|
||||
return this.append(JSONObject.valueToString(object));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,508 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
/**
|
||||
* This provides static methods to convert an XML text into a JSONObject,
|
||||
* and to covert a JSONObject into an XML text.
|
||||
* @author JSON.org
|
||||
* @version 2011-02-11
|
||||
*/
|
||||
public class XML {
|
||||
|
||||
/** The Character '&'. */
|
||||
public static final Character AMP = new Character('&');
|
||||
|
||||
/** The Character '''. */
|
||||
public static final Character APOS = new Character('\'');
|
||||
|
||||
/** The Character '!'. */
|
||||
public static final Character BANG = new Character('!');
|
||||
|
||||
/** The Character '='. */
|
||||
public static final Character EQ = new Character('=');
|
||||
|
||||
/** The Character '>'. */
|
||||
public static final Character GT = new Character('>');
|
||||
|
||||
/** The Character '<'. */
|
||||
public static final Character LT = new Character('<');
|
||||
|
||||
/** The Character '?'. */
|
||||
public static final Character QUEST = new Character('?');
|
||||
|
||||
/** The Character '"'. */
|
||||
public static final Character QUOT = new Character('"');
|
||||
|
||||
/** The Character '/'. */
|
||||
public static final Character SLASH = new Character('/');
|
||||
|
||||
/**
|
||||
* Replace special characters with XML escapes:
|
||||
* <pre>
|
||||
* & <small>(ampersand)</small> is replaced by &amp;
|
||||
* < <small>(less than)</small> is replaced by &lt;
|
||||
* > <small>(greater than)</small> is replaced by &gt;
|
||||
* " <small>(double quote)</small> is replaced by &quot;
|
||||
* </pre>
|
||||
* @param string The string to be escaped.
|
||||
* @return The escaped string.
|
||||
*/
|
||||
public static String escape(String string) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0, length = string.length(); i < length; i++) {
|
||||
char c = string.charAt(i);
|
||||
switch (c) {
|
||||
case '&':
|
||||
sb.append("&");
|
||||
break;
|
||||
case '<':
|
||||
sb.append("<");
|
||||
break;
|
||||
case '>':
|
||||
sb.append(">");
|
||||
break;
|
||||
case '"':
|
||||
sb.append(""");
|
||||
break;
|
||||
case '\'':
|
||||
sb.append("'");
|
||||
break;
|
||||
default:
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception if the string contains whitespace.
|
||||
* Whitespace is not allowed in tagNames and attributes.
|
||||
* @param string
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static void noSpace(String string) throws JSONException {
|
||||
int i, length = string.length();
|
||||
if (length == 0) {
|
||||
throw new JSONException("Empty string.");
|
||||
}
|
||||
for (i = 0; i < length; i += 1) {
|
||||
if (Character.isWhitespace(string.charAt(i))) {
|
||||
throw new JSONException("'" + string +
|
||||
"' contains a space character.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the content following the named tag, attaching it to the context.
|
||||
* @param x The XMLTokener containing the source string.
|
||||
* @param context The JSONObject that will include the new material.
|
||||
* @param name The tag name.
|
||||
* @return true if the close tag is processed.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private static boolean parse(XMLTokener x, JSONObject context,
|
||||
String name) throws JSONException {
|
||||
char c;
|
||||
int i;
|
||||
JSONObject jsonobject = null;
|
||||
String string;
|
||||
String tagName;
|
||||
Object token;
|
||||
|
||||
// Test for and skip past these forms:
|
||||
// <!-- ... -->
|
||||
// <! ... >
|
||||
// <![ ... ]]>
|
||||
// <? ... ?>
|
||||
// Report errors for these forms:
|
||||
// <>
|
||||
// <=
|
||||
// <<
|
||||
|
||||
token = x.nextToken();
|
||||
|
||||
// <!
|
||||
|
||||
if (token == BANG) {
|
||||
c = x.next();
|
||||
if (c == '-') {
|
||||
if (x.next() == '-') {
|
||||
x.skipPast("-->");
|
||||
return false;
|
||||
}
|
||||
x.back();
|
||||
} else if (c == '[') {
|
||||
token = x.nextToken();
|
||||
if ("CDATA".equals(token)) {
|
||||
if (x.next() == '[') {
|
||||
string = x.nextCDATA();
|
||||
if (string.length() > 0) {
|
||||
context.accumulate("content", string);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
throw x.syntaxError("Expected 'CDATA['");
|
||||
}
|
||||
i = 1;
|
||||
do {
|
||||
token = x.nextMeta();
|
||||
if (token == null) {
|
||||
throw x.syntaxError("Missing '>' after '<!'.");
|
||||
} else if (token == LT) {
|
||||
i += 1;
|
||||
} else if (token == GT) {
|
||||
i -= 1;
|
||||
}
|
||||
} while (i > 0);
|
||||
return false;
|
||||
} else if (token == QUEST) {
|
||||
|
||||
// <?
|
||||
|
||||
x.skipPast("?>");
|
||||
return false;
|
||||
} else if (token == SLASH) {
|
||||
|
||||
// Close tag </
|
||||
|
||||
token = x.nextToken();
|
||||
if (name == null) {
|
||||
throw x.syntaxError("Mismatched close tag " + token);
|
||||
}
|
||||
if (!token.equals(name)) {
|
||||
throw x.syntaxError("Mismatched " + name + " and " + token);
|
||||
}
|
||||
if (x.nextToken() != GT) {
|
||||
throw x.syntaxError("Misshaped close tag");
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (token instanceof Character) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
|
||||
// Open tag <
|
||||
|
||||
} else {
|
||||
tagName = (String)token;
|
||||
token = null;
|
||||
jsonobject = new JSONObject();
|
||||
for (;;) {
|
||||
if (token == null) {
|
||||
token = x.nextToken();
|
||||
}
|
||||
|
||||
// attribute = value
|
||||
|
||||
if (token instanceof String) {
|
||||
string = (String)token;
|
||||
token = x.nextToken();
|
||||
if (token == EQ) {
|
||||
token = x.nextToken();
|
||||
if (!(token instanceof String)) {
|
||||
throw x.syntaxError("Missing value");
|
||||
}
|
||||
jsonobject.accumulate(string,
|
||||
XML.stringToValue((String)token));
|
||||
token = null;
|
||||
} else {
|
||||
jsonobject.accumulate(string, "");
|
||||
}
|
||||
|
||||
// Empty tag <.../>
|
||||
|
||||
} else if (token == SLASH) {
|
||||
if (x.nextToken() != GT) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
if (jsonobject.length() > 0) {
|
||||
context.accumulate(tagName, jsonobject);
|
||||
} else {
|
||||
context.accumulate(tagName, "");
|
||||
}
|
||||
return false;
|
||||
|
||||
// Content, between <...> and </...>
|
||||
|
||||
} else if (token == GT) {
|
||||
for (;;) {
|
||||
token = x.nextContent();
|
||||
if (token == null) {
|
||||
if (tagName != null) {
|
||||
throw x.syntaxError("Unclosed tag " + tagName);
|
||||
}
|
||||
return false;
|
||||
} else if (token instanceof String) {
|
||||
string = (String)token;
|
||||
if (string.length() > 0) {
|
||||
jsonobject.accumulate("content",
|
||||
XML.stringToValue(string));
|
||||
}
|
||||
|
||||
// Nested element
|
||||
|
||||
} else if (token == LT) {
|
||||
if (parse(x, jsonobject, tagName)) {
|
||||
if (jsonobject.length() == 0) {
|
||||
context.accumulate(tagName, "");
|
||||
} else if (jsonobject.length() == 1 &&
|
||||
jsonobject.opt("content") != null) {
|
||||
context.accumulate(tagName,
|
||||
jsonobject.opt("content"));
|
||||
} else {
|
||||
context.accumulate(tagName, jsonobject);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to convert a string into a number, boolean, or null. If the string
|
||||
* can't be converted, return the string. This is much less ambitious than
|
||||
* JSONObject.stringToValue, especially because it does not attempt to
|
||||
* convert plus forms, octal forms, hex forms, or E forms lacking decimal
|
||||
* points.
|
||||
* @param string A String.
|
||||
* @return A simple JSON value.
|
||||
*/
|
||||
public static Object stringToValue(String string) {
|
||||
if ("".equals(string)) {
|
||||
return string;
|
||||
}
|
||||
if ("true".equalsIgnoreCase(string)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if ("false".equalsIgnoreCase(string)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
if ("null".equalsIgnoreCase(string)) {
|
||||
return JSONObject.NULL;
|
||||
}
|
||||
if ("0".equals(string)) {
|
||||
return new Integer(0);
|
||||
}
|
||||
|
||||
// If it might be a number, try converting it. If that doesn't work,
|
||||
// return the string.
|
||||
|
||||
try {
|
||||
char initial = string.charAt(0);
|
||||
boolean negative = false;
|
||||
if (initial == '-') {
|
||||
initial = string.charAt(1);
|
||||
negative = true;
|
||||
}
|
||||
if (initial == '0' && string.charAt(negative ? 2 : 1) == '0') {
|
||||
return string;
|
||||
}
|
||||
if ((initial >= '0' && initial <= '9')) {
|
||||
if (string.indexOf('.') >= 0) {
|
||||
return Double.valueOf(string);
|
||||
} else if (string.indexOf('e') < 0 && string.indexOf('E') < 0) {
|
||||
Long myLong = new Long(string);
|
||||
if (myLong.longValue() == myLong.intValue()) {
|
||||
return new Integer(myLong.intValue());
|
||||
} else {
|
||||
return myLong;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONObject. Some information may be lost in this transformation
|
||||
* because JSON is a data format and XML is a document format. XML uses
|
||||
* elements, attributes, and content text, while JSON uses unordered
|
||||
* collections of name/value pairs and arrays of values. JSON does not
|
||||
* does not like to distinguish between elements and attributes.
|
||||
* Sequences of similar elements are represented as JSONArrays. Content
|
||||
* text may be placed in a "content" member. Comments, prologs, DTDs, and
|
||||
* <code><[ [ ]]></code> are ignored.
|
||||
* @param string The source string.
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
JSONObject jo = new JSONObject();
|
||||
XMLTokener x = new XMLTokener(string);
|
||||
while (x.more() && x.skipPast("<")) {
|
||||
parse(x, jo, null);
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a JSONObject into a well-formed, element-normal XML string.
|
||||
* @param object A JSONObject.
|
||||
* @return A string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(Object object) throws JSONException {
|
||||
return toString(object, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a JSONObject into a well-formed, element-normal XML string.
|
||||
* @param object A JSONObject.
|
||||
* @param tagName The optional name of the enclosing tag.
|
||||
* @return A string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(Object object, String tagName)
|
||||
throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int i;
|
||||
JSONArray ja;
|
||||
JSONObject jo;
|
||||
String key;
|
||||
Iterator keys;
|
||||
int length;
|
||||
String string;
|
||||
Object value;
|
||||
if (object instanceof JSONObject) {
|
||||
|
||||
// Emit <tagName>
|
||||
|
||||
if (tagName != null) {
|
||||
sb.append('<');
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
}
|
||||
|
||||
// Loop thru the keys.
|
||||
|
||||
jo = (JSONObject)object;
|
||||
keys = jo.keys();
|
||||
while (keys.hasNext()) {
|
||||
key = keys.next().toString();
|
||||
value = jo.opt(key);
|
||||
if (value == null) {
|
||||
value = "";
|
||||
}
|
||||
if (value instanceof String) {
|
||||
string = (String)value;
|
||||
} else {
|
||||
string = null;
|
||||
}
|
||||
|
||||
// Emit content in body
|
||||
|
||||
if ("content".equals(key)) {
|
||||
if (value instanceof JSONArray) {
|
||||
ja = (JSONArray)value;
|
||||
length = ja.length();
|
||||
for (i = 0; i < length; i += 1) {
|
||||
if (i > 0) {
|
||||
sb.append('\n');
|
||||
}
|
||||
sb.append(escape(ja.get(i).toString()));
|
||||
}
|
||||
} else {
|
||||
sb.append(escape(value.toString()));
|
||||
}
|
||||
|
||||
// Emit an array of similar keys
|
||||
|
||||
} else if (value instanceof JSONArray) {
|
||||
ja = (JSONArray)value;
|
||||
length = ja.length();
|
||||
for (i = 0; i < length; i += 1) {
|
||||
value = ja.get(i);
|
||||
if (value instanceof JSONArray) {
|
||||
sb.append('<');
|
||||
sb.append(key);
|
||||
sb.append('>');
|
||||
sb.append(toString(value));
|
||||
sb.append("</");
|
||||
sb.append(key);
|
||||
sb.append('>');
|
||||
} else {
|
||||
sb.append(toString(value, key));
|
||||
}
|
||||
}
|
||||
} else if ("".equals(value)) {
|
||||
sb.append('<');
|
||||
sb.append(key);
|
||||
sb.append("/>");
|
||||
|
||||
// Emit a new tag <k>
|
||||
|
||||
} else {
|
||||
sb.append(toString(value, key));
|
||||
}
|
||||
}
|
||||
if (tagName != null) {
|
||||
|
||||
// Emit the </tagname> close tag
|
||||
|
||||
sb.append("</");
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
}
|
||||
return sb.toString();
|
||||
|
||||
// XML does not have good support for arrays. If an array appears in a place
|
||||
// where XML is lacking, synthesize an <array> element.
|
||||
|
||||
} else {
|
||||
if (object.getClass().isArray()) {
|
||||
object = new JSONArray(object);
|
||||
}
|
||||
if (object instanceof JSONArray) {
|
||||
ja = (JSONArray)object;
|
||||
length = ja.length();
|
||||
for (i = 0; i < length; i += 1) {
|
||||
sb.append(toString(ja.opt(i), tagName == null ? "array" : tagName));
|
||||
}
|
||||
return sb.toString();
|
||||
} else {
|
||||
string = (object == null) ? "null" : escape(object.toString());
|
||||
return (tagName == null) ? "\"" + string + "\"" :
|
||||
(string.length() == 0) ? "<" + tagName + "/>" :
|
||||
"<" + tagName + ">" + string + "</" + tagName + ">";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The XMLTokener extends the JSONTokener to provide additional methods
|
||||
* for the parsing of XML texts.
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class XMLTokener extends JSONTokener {
|
||||
|
||||
|
||||
/** The table of entity values. It initially contains Character values for
|
||||
* amp, apos, gt, lt, quot.
|
||||
*/
|
||||
public static final java.util.HashMap entity;
|
||||
|
||||
static {
|
||||
entity = new java.util.HashMap(8);
|
||||
entity.put("amp", XML.AMP);
|
||||
entity.put("apos", XML.APOS);
|
||||
entity.put("gt", XML.GT);
|
||||
entity.put("lt", XML.LT);
|
||||
entity.put("quot", XML.QUOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an XMLTokener from a string.
|
||||
* @param s A source string.
|
||||
*/
|
||||
public XMLTokener(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text in the CDATA block.
|
||||
* @return The string up to the <code>]]></code>.
|
||||
* @throws JSONException If the <code>]]></code> is not found.
|
||||
*/
|
||||
public String nextCDATA() throws JSONException {
|
||||
char c;
|
||||
int i;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
c = next();
|
||||
if (end()) {
|
||||
throw syntaxError("Unclosed CDATA");
|
||||
}
|
||||
sb.append(c);
|
||||
i = sb.length() - 3;
|
||||
if (i >= 0 && sb.charAt(i) == ']' &&
|
||||
sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') {
|
||||
sb.setLength(i);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next XML outer token, trimming whitespace. There are two kinds
|
||||
* of tokens: the '<' character which begins a markup tag, and the content
|
||||
* text between markup tags.
|
||||
*
|
||||
* @return A string, or a '<' Character, or null if there is no more
|
||||
* source text.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public Object nextContent() throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb;
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
if (c == 0) {
|
||||
return null;
|
||||
}
|
||||
if (c == '<') {
|
||||
return XML.LT;
|
||||
}
|
||||
sb = new StringBuffer();
|
||||
for (;;) {
|
||||
if (c == '<' || c == 0) {
|
||||
back();
|
||||
return sb.toString().trim();
|
||||
}
|
||||
if (c == '&') {
|
||||
sb.append(nextEntity(c));
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
c = next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the next entity. These entities are translated to Characters:
|
||||
* <code>& ' > < "</code>.
|
||||
* @param ampersand An ampersand character.
|
||||
* @return A Character or an entity String if the entity is not recognized.
|
||||
* @throws JSONException If missing ';' in XML entity.
|
||||
*/
|
||||
public Object nextEntity(char ampersand) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
char c = next();
|
||||
if (Character.isLetterOrDigit(c) || c == '#') {
|
||||
sb.append(Character.toLowerCase(c));
|
||||
} else if (c == ';') {
|
||||
break;
|
||||
} else {
|
||||
throw syntaxError("Missing ';' in XML entity: &" + sb);
|
||||
}
|
||||
}
|
||||
String string = sb.toString();
|
||||
Object object = entity.get(string);
|
||||
return object != null ? object : ampersand + string + ";";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next XML meta token. This is used for skipping over <!...>
|
||||
* and <?...?> structures.
|
||||
* @return Syntax characters (<code>< > / = ! ?</code>) are returned as
|
||||
* Character, and strings and names are returned as Boolean. We don't care
|
||||
* what the values actually are.
|
||||
* @throws JSONException If a string is not properly closed or if the XML
|
||||
* is badly structured.
|
||||
*/
|
||||
public Object nextMeta() throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
switch (c) {
|
||||
case 0:
|
||||
throw syntaxError("Misshaped meta tag");
|
||||
case '<':
|
||||
return XML.LT;
|
||||
case '>':
|
||||
return XML.GT;
|
||||
case '/':
|
||||
return XML.SLASH;
|
||||
case '=':
|
||||
return XML.EQ;
|
||||
case '!':
|
||||
return XML.BANG;
|
||||
case '?':
|
||||
return XML.QUEST;
|
||||
case '"':
|
||||
case '\'':
|
||||
q = c;
|
||||
for (;;) {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
throw syntaxError("Unterminated string");
|
||||
}
|
||||
if (c == q) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
default:
|
||||
for (;;) {
|
||||
c = next();
|
||||
if (Character.isWhitespace(c)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '<':
|
||||
case '>':
|
||||
case '/':
|
||||
case '=':
|
||||
case '!':
|
||||
case '?':
|
||||
case '"':
|
||||
case '\'':
|
||||
back();
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next XML Token. These tokens are found inside of angle
|
||||
* brackets. It may be one of these characters: <code>/ > = ! ?</code> or it
|
||||
* may be a string wrapped in single quotes or double quotes, or it may be a
|
||||
* name.
|
||||
* @return a String or a Character.
|
||||
* @throws JSONException If the XML is not well formed.
|
||||
*/
|
||||
public Object nextToken() throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
StringBuffer sb;
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
switch (c) {
|
||||
case 0:
|
||||
throw syntaxError("Misshaped element");
|
||||
case '<':
|
||||
throw syntaxError("Misplaced '<'");
|
||||
case '>':
|
||||
return XML.GT;
|
||||
case '/':
|
||||
return XML.SLASH;
|
||||
case '=':
|
||||
return XML.EQ;
|
||||
case '!':
|
||||
return XML.BANG;
|
||||
case '?':
|
||||
return XML.QUEST;
|
||||
|
||||
// Quoted string
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
q = c;
|
||||
sb = new StringBuffer();
|
||||
for (;;) {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
throw syntaxError("Unterminated string");
|
||||
}
|
||||
if (c == q) {
|
||||
return sb.toString();
|
||||
}
|
||||
if (c == '&') {
|
||||
sb.append(nextEntity(c));
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
||||
// Name
|
||||
|
||||
sb = new StringBuffer();
|
||||
for (;;) {
|
||||
sb.append(c);
|
||||
c = next();
|
||||
if (Character.isWhitespace(c)) {
|
||||
return sb.toString();
|
||||
}
|
||||
switch (c) {
|
||||
case 0:
|
||||
return sb.toString();
|
||||
case '>':
|
||||
case '/':
|
||||
case '=':
|
||||
case '!':
|
||||
case '?':
|
||||
case '[':
|
||||
case ']':
|
||||
back();
|
||||
return sb.toString();
|
||||
case '<':
|
||||
case '"':
|
||||
case '\'':
|
||||
throw syntaxError("Bad character in a name");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Skip characters until past the requested string.
|
||||
* If it is not found, we are left at the end of the source with a result of false.
|
||||
* @param to A string to skip past.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public boolean skipPast(String to) throws JSONException {
|
||||
boolean b;
|
||||
char c;
|
||||
int i;
|
||||
int j;
|
||||
int offset = 0;
|
||||
int length = to.length();
|
||||
char[] circle = new char[length];
|
||||
|
||||
/*
|
||||
* First fill the circle buffer with as many characters as are in the
|
||||
* to string. If we reach an early end, bail.
|
||||
*/
|
||||
|
||||
for (i = 0; i < length; i += 1) {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
return false;
|
||||
}
|
||||
circle[i] = c;
|
||||
}
|
||||
/*
|
||||
* We will loop, possibly for all of the remaining characters.
|
||||
*/
|
||||
for (;;) {
|
||||
j = offset;
|
||||
b = true;
|
||||
/*
|
||||
* Compare the circle buffer with the to string.
|
||||
*/
|
||||
for (i = 0; i < length; i += 1) {
|
||||
if (circle[j] != to.charAt(i)) {
|
||||
b = false;
|
||||
break;
|
||||
}
|
||||
j += 1;
|
||||
if (j >= length) {
|
||||
j -= length;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we exit the loop with b intact, then victory is ours.
|
||||
*/
|
||||
if (b) {
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* Get the next character. If there isn't one, then defeat is ours.
|
||||
*/
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Shove the character in the circle buffer and advance the
|
||||
* circle offset. The offset is mod n.
|
||||
*/
|
||||
circle[offset] = c;
|
||||
offset += 1;
|
||||
if (offset >= length) {
|
||||
offset -= length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player;
|
||||
|
||||
import org.libresonic.player.domain.Version;
|
||||
import org.libresonic.player.service.*;
|
||||
import org.libresonic.player.util.*;
|
||||
import org.apache.commons.lang.exception.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Logger implementation which logs to SUBSONIC_HOME/libresonic.log.
|
||||
* <br/>
|
||||
* Note: Third party logging libraries (such as log4j and Commons logging) are intentionally not
|
||||
* used. These libraries causes a lot of headache when deploying to some application servers
|
||||
* (for instance Jetty and JBoss).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
* @version $Revision: 1.1 $ $Date: 2005/05/09 19:58:26 $
|
||||
*/
|
||||
public class Logger {
|
||||
|
||||
private String category;
|
||||
|
||||
private static List<Entry> entries = Collections.synchronizedList(new BoundedList<Entry>(50));
|
||||
private static PrintWriter writer;
|
||||
private static Boolean debugEnabled;
|
||||
|
||||
/**
|
||||
* Creates a logger for the given class.
|
||||
* @param clazz The class.
|
||||
* @return A logger for the class.
|
||||
*/
|
||||
public static Logger getLogger(Class clazz) {
|
||||
return new Logger(clazz.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a logger for the given namee.
|
||||
* @param name The name.
|
||||
* @return A logger for the name.
|
||||
*/
|
||||
public static Logger getLogger(String name) {
|
||||
return new Logger(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last few log entries.
|
||||
* @return The last few log entries.
|
||||
*/
|
||||
public static Entry[] getLatestLogEntries() {
|
||||
return entries.toArray(new Entry[entries.size()]);
|
||||
}
|
||||
|
||||
private Logger(String name) {
|
||||
int lastDot = name.lastIndexOf('.');
|
||||
if (lastDot == -1) {
|
||||
category = name;
|
||||
} else {
|
||||
category = name.substring(lastDot + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a debug message.
|
||||
* @param message The log message.
|
||||
*/
|
||||
public void debug(Object message) {
|
||||
debug(message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a debug message.
|
||||
* @param message The message.
|
||||
* @param error The optional exception.
|
||||
*/
|
||||
public void debug(Object message, Throwable error) {
|
||||
if (isDebugEnabled()) {
|
||||
add(Level.DEBUG, message, error);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDebugEnabled() {
|
||||
if (debugEnabled == null) {
|
||||
VersionService versionService = ServiceLocator.getVersionService();
|
||||
if (versionService == null) {
|
||||
return true; // versionService not yet available.
|
||||
}
|
||||
Version localVersion = versionService.getLocalVersion();
|
||||
debugEnabled = localVersion == null || localVersion.getBeta() != 0;
|
||||
}
|
||||
return debugEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an info message.
|
||||
* @param message The message.
|
||||
*/
|
||||
public void info(Object message) {
|
||||
info(message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an info message.
|
||||
* @param message The message.
|
||||
* @param error The optional exception.
|
||||
*/
|
||||
public void info(Object message, Throwable error) {
|
||||
add(Level.INFO, message, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a warning message.
|
||||
* @param message The message.
|
||||
*/
|
||||
public void warn(Object message) {
|
||||
warn(message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a warning message.
|
||||
* @param message The message.
|
||||
* @param error The optional exception.
|
||||
*/
|
||||
public void warn(Object message, Throwable error) {
|
||||
add(Level.WARN, message, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an error message.
|
||||
* @param message The message.
|
||||
*/
|
||||
public void error(Object message) {
|
||||
error(message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an error message.
|
||||
* @param message The message.
|
||||
* @param error The optional exception.
|
||||
*/
|
||||
public void error(Object message, Throwable error) {
|
||||
add(Level.ERROR, message, error);
|
||||
}
|
||||
|
||||
private void add(Level level, Object message, Throwable error) {
|
||||
Entry entry = new Entry(category, level, message, error);
|
||||
try {
|
||||
getPrintWriter().println(entry);
|
||||
} catch (IOException x) {
|
||||
System.err.println("Failed to write to libresonic.log. " + x);
|
||||
}
|
||||
entries.add(entry);
|
||||
}
|
||||
|
||||
private static synchronized PrintWriter getPrintWriter() throws IOException {
|
||||
if (writer == null) {
|
||||
writer = new PrintWriter(new FileWriter(getLogFile(), false), true);
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
public static File getLogFile() {
|
||||
File libresonicHome = SettingsService.getLibresonicHome();
|
||||
return new File(libresonicHome, "libresonic.log");
|
||||
}
|
||||
|
||||
/**
|
||||
* Log level.
|
||||
*/
|
||||
public enum Level {
|
||||
DEBUG, INFO, WARN, ERROR
|
||||
}
|
||||
|
||||
/**
|
||||
* Log entry.
|
||||
*/
|
||||
public static class Entry {
|
||||
private String category;
|
||||
private Date date;
|
||||
private Level level;
|
||||
private Object message;
|
||||
private Throwable error;
|
||||
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
|
||||
|
||||
public Entry(String category, Level level, Object message, Throwable error) {
|
||||
this.date = new Date();
|
||||
this.category = category;
|
||||
this.level = level;
|
||||
this.message = message;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public Level getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public Object getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Throwable getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append('[').append(DATE_FORMAT.format(date)).append("] ");
|
||||
builder.append(level).append(' ');
|
||||
builder.append(category).append(" - ");
|
||||
builder.append(message);
|
||||
|
||||
if (error != null) {
|
||||
builder.append('\n').append(ExceptionUtils.getFullStackTrace(error));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of Libresonic.
|
||||
*
|
||||
* Libresonic 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.
|
||||
*
|
||||
* Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 (C) Sindre Mehus
|
||||
*/
|
||||
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.libresonic.player.domain.ArtistBio;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ArtistInfo {
|
||||
|
||||
private final List<SimilarArtist> similarArtists;
|
||||
private final ArtistBio artistBio;
|
||||
private final List<TopSong> topSongs;
|
||||
|
||||
public ArtistInfo(List<SimilarArtist> similarArtists, ArtistBio artistBio, List<TopSong> topSongs) {
|
||||
this.similarArtists = similarArtists;
|
||||
this.artistBio = artistBio;
|
||||
this.topSongs = topSongs;
|
||||
}
|
||||
|
||||
public List<SimilarArtist> getSimilarArtists() {
|
||||
return similarArtists;
|
||||
}
|
||||
|
||||
public ArtistBio getArtistBio() {
|
||||
return artistBio;
|
||||
}
|
||||
|
||||
public List<TopSong> getTopSongs() {
|
||||
return topSongs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.util.BoundedList;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.directwebremoting.WebContext;
|
||||
import org.directwebremoting.WebContextFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for the chatting.
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ChatService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(ChatService.class);
|
||||
private static final String CACHE_KEY = "1";
|
||||
private static final int MAX_MESSAGES = 10;
|
||||
private static final long TTL_MILLIS = 3L * 24L * 60L * 60L * 1000L; // 3 days.
|
||||
|
||||
private final LinkedList<Message> messages = new BoundedList<Message>(MAX_MESSAGES);
|
||||
private SecurityService securityService;
|
||||
|
||||
private long revision = System.identityHashCode(this);
|
||||
|
||||
/**
|
||||
* Invoked by Spring.
|
||||
*/
|
||||
public void init() {
|
||||
// Delete old messages every hour.
|
||||
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
removeOldMessages();
|
||||
}
|
||||
};
|
||||
executor.scheduleWithFixedDelay(runnable, 0L, 3600L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private synchronized void removeOldMessages() {
|
||||
long now = System.currentTimeMillis();
|
||||
for (Iterator<Message> iterator = messages.iterator(); iterator.hasNext();) {
|
||||
Message message = iterator.next();
|
||||
if (now - message.getDate().getTime() > TTL_MILLIS) {
|
||||
iterator.remove();
|
||||
revision++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void addMessage(String message) {
|
||||
WebContext webContext = WebContextFactory.get();
|
||||
doAddMessage(message, webContext.getHttpServletRequest());
|
||||
}
|
||||
|
||||
public synchronized void doAddMessage(String message, HttpServletRequest request) {
|
||||
|
||||
String user = securityService.getCurrentUsername(request);
|
||||
message = StringUtils.trimToNull(message);
|
||||
if (message != null && user != null) {
|
||||
messages.addFirst(new Message(message, user, new Date()));
|
||||
revision++;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void clearMessages() {
|
||||
messages.clear();
|
||||
revision++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all messages, but only if the given revision is different from the
|
||||
* current revision.
|
||||
*/
|
||||
public synchronized Messages getMessages(long revision) {
|
||||
if (this.revision != revision) {
|
||||
return new Messages(new ArrayList<Message>(messages), this.revision);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public static class Messages implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -752602719879818165L;
|
||||
private final List<Message> messages;
|
||||
private final long revision;
|
||||
|
||||
public Messages(List<Message> messages, long revision) {
|
||||
this.messages = messages;
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
public List<Message> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Message implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -1907101191518133712L;
|
||||
private final String content;
|
||||
private final String username;
|
||||
private final Date date;
|
||||
|
||||
public Message(String content, String username, Date date) {
|
||||
this.content = content;
|
||||
this.username = username;
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
/**
|
||||
* Contains info about cover art images for an album.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class CoverArtInfo {
|
||||
|
||||
private final String imagePreviewUrl;
|
||||
private final String imageDownloadUrl;
|
||||
|
||||
public CoverArtInfo(String imagePreviewUrl, String imageDownloadUrl) {
|
||||
this.imagePreviewUrl = imagePreviewUrl;
|
||||
this.imageDownloadUrl = imageDownloadUrl;
|
||||
}
|
||||
|
||||
public String getImagePreviewUrl() {
|
||||
return imagePreviewUrl;
|
||||
}
|
||||
|
||||
public String getImageDownloadUrl() {
|
||||
return imageDownloadUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for changing cover art images.
|
||||
* <p/>
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class CoverArtService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(CoverArtService.class);
|
||||
|
||||
private SecurityService securityService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
/**
|
||||
* Downloads and saves the cover art at the given URL.
|
||||
*
|
||||
* @param albumId ID of the album in question.
|
||||
* @param url The image URL.
|
||||
* @return The error string if something goes wrong, <code>null</code> otherwise.
|
||||
*/
|
||||
public String setCoverArtImage(int albumId, String url) {
|
||||
try {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(albumId);
|
||||
saveCoverArt(mediaFile.getPath(), url);
|
||||
return null;
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to save cover art for album " + albumId, x);
|
||||
return x.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCoverArt(String path, String url) throws Exception {
|
||||
InputStream input = null;
|
||||
OutputStream output = null;
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
|
||||
try {
|
||||
HttpConnectionParams.setConnectionTimeout(client.getParams(), 20 * 1000); // 20 seconds
|
||||
HttpConnectionParams.setSoTimeout(client.getParams(), 20 * 1000); // 20 seconds
|
||||
HttpGet method = new HttpGet(url);
|
||||
|
||||
HttpResponse response = client.execute(method);
|
||||
input = response.getEntity().getContent();
|
||||
|
||||
// Attempt to resolve proper suffix.
|
||||
String suffix = "jpg";
|
||||
if (url.toLowerCase().endsWith(".gif")) {
|
||||
suffix = "gif";
|
||||
} else if (url.toLowerCase().endsWith(".png")) {
|
||||
suffix = "png";
|
||||
}
|
||||
|
||||
// Check permissions.
|
||||
File newCoverFile = new File(path, "cover." + suffix);
|
||||
if (!securityService.isWriteAllowed(newCoverFile)) {
|
||||
throw new Exception("Permission denied: " + StringUtil.toHtml(newCoverFile.getPath()));
|
||||
}
|
||||
|
||||
// If file exists, create a backup.
|
||||
backup(newCoverFile, new File(path, "cover." + suffix + ".backup"));
|
||||
|
||||
// Write file.
|
||||
output = new FileOutputStream(newCoverFile);
|
||||
IOUtils.copy(input, output);
|
||||
|
||||
MediaFile dir = mediaFileService.getMediaFile(path);
|
||||
|
||||
// Refresh database.
|
||||
mediaFileService.refreshMediaFile(dir);
|
||||
dir = mediaFileService.getMediaFile(dir.getId());
|
||||
|
||||
// Rename existing cover files if new cover file is not the preferred.
|
||||
try {
|
||||
while (true) {
|
||||
File coverFile = mediaFileService.getCoverArt(dir);
|
||||
if (coverFile != null && !isMediaFile(coverFile) && !newCoverFile.equals(coverFile)) {
|
||||
if (!coverFile.renameTo(new File(coverFile.getCanonicalPath() + ".old"))) {
|
||||
LOG.warn("Unable to rename old image file " + coverFile);
|
||||
break;
|
||||
}
|
||||
LOG.info("Renamed old image file " + coverFile);
|
||||
|
||||
// Must refresh again.
|
||||
mediaFileService.refreshMediaFile(dir);
|
||||
dir = mediaFileService.getMediaFile(dir.getId());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to rename existing cover file.", x);
|
||||
}
|
||||
|
||||
} finally {
|
||||
IOUtils.closeQuietly(input);
|
||||
IOUtils.closeQuietly(output);
|
||||
client.getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMediaFile(File file) {
|
||||
return !mediaFileService.filterMediaFiles(new File[]{file}).isEmpty();
|
||||
}
|
||||
|
||||
private void backup(File newCoverFile, File backup) {
|
||||
if (newCoverFile.exists()) {
|
||||
if (backup.exists()) {
|
||||
backup.delete();
|
||||
}
|
||||
if (newCoverFile.renameTo(backup)) {
|
||||
LOG.info("Backed up old image file to " + backup);
|
||||
} else {
|
||||
LOG.warn("Failed to create image file backup " + backup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
/**
|
||||
* Contains lyrics info for a song.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class LyricsInfo {
|
||||
|
||||
private final String lyrics;
|
||||
private final String artist;
|
||||
private final String title;
|
||||
private boolean tryLater;
|
||||
|
||||
public LyricsInfo() {
|
||||
this(null, null, null);
|
||||
}
|
||||
|
||||
public LyricsInfo(String lyrics, String artist, String title) {
|
||||
this.lyrics = lyrics;
|
||||
this.artist = artist;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getLyrics() {
|
||||
return lyrics;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTryLater(boolean tryLater) {
|
||||
this.tryLater = tryLater;
|
||||
}
|
||||
|
||||
public boolean isTryLater() {
|
||||
return tryLater;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.SocketException;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.ResponseHandler;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.BasicResponseHandler;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.jdom.Document;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.Namespace;
|
||||
import org.jdom.input.SAXBuilder;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for retrieving song lyrics from chartlyrics.com.
|
||||
* <p/>
|
||||
* See http://www.chartlyrics.com/api.aspx for details.
|
||||
* <p/>
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class LyricsService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(LyricsService.class);
|
||||
|
||||
/**
|
||||
* Returns lyrics for the given song and artist.
|
||||
*
|
||||
* @param artist The artist.
|
||||
* @param song The song.
|
||||
* @return The lyrics, never <code>null</code> .
|
||||
*/
|
||||
public LyricsInfo getLyrics(String artist, String song) {
|
||||
LyricsInfo lyrics = new LyricsInfo();
|
||||
try {
|
||||
|
||||
artist = StringUtil.urlEncode(artist);
|
||||
song = StringUtil.urlEncode(song);
|
||||
|
||||
String url = "http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect?artist=" + artist + "&song=" + song;
|
||||
String xml = executeGetRequest(url);
|
||||
lyrics = parseSearchResult(xml);
|
||||
|
||||
} catch (SocketException x) {
|
||||
lyrics.setTryLater(true);
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to get lyrics for song '" + song + "'.", x);
|
||||
}
|
||||
return lyrics;
|
||||
}
|
||||
|
||||
private LyricsInfo parseSearchResult(String xml) throws Exception {
|
||||
SAXBuilder builder = new SAXBuilder();
|
||||
Document document = builder.build(new StringReader(xml));
|
||||
|
||||
Element root = document.getRootElement();
|
||||
Namespace ns = root.getNamespace();
|
||||
|
||||
String lyric = StringUtils.trimToNull(root.getChildText("Lyric", ns));
|
||||
String song = root.getChildText("LyricSong", ns);
|
||||
String artist = root.getChildText("LyricArtist", ns);
|
||||
|
||||
return new LyricsInfo(lyric, artist, song);
|
||||
}
|
||||
|
||||
private String executeGetRequest(String url) throws IOException {
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
HttpConnectionParams.setConnectionTimeout(client.getParams(), 15000);
|
||||
HttpConnectionParams.setSoTimeout(client.getParams(), 15000);
|
||||
HttpGet method = new HttpGet(url);
|
||||
try {
|
||||
|
||||
ResponseHandler<String> responseHandler = new BasicResponseHandler();
|
||||
return client.execute(method, responseHandler);
|
||||
|
||||
} finally {
|
||||
client.getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.directwebremoting.WebContextFactory;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.domain.ArtistBio;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.LastFmService;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.NetworkService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Provides miscellaneous AJAX-enabled services.
|
||||
* <p/>
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MultiService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MultiService.class);
|
||||
|
||||
private NetworkService networkService;
|
||||
private MediaFileService mediaFileService;
|
||||
private LastFmService lastFmService;
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
/**
|
||||
* Returns status for port forwarding and URL redirection.
|
||||
*/
|
||||
public NetworkStatus getNetworkStatus() {
|
||||
NetworkService.Status portForwardingStatus = networkService.getPortForwardingStatus();
|
||||
NetworkService.Status urlRedirectionStatus = networkService.getURLRedirecionStatus();
|
||||
return new NetworkStatus(portForwardingStatus.getText(),
|
||||
portForwardingStatus.getDate(),
|
||||
urlRedirectionStatus.getText(),
|
||||
urlRedirectionStatus.getDate());
|
||||
}
|
||||
|
||||
public ArtistInfo getArtistInfo(int mediaFileId, int maxSimilarArtists, int maxTopSongs) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(mediaFileId);
|
||||
List<SimilarArtist> similarArtists = getSimilarArtists(mediaFileId, maxSimilarArtists);
|
||||
ArtistBio artistBio = lastFmService.getArtistBio(mediaFile);
|
||||
List<TopSong> topSongs = getTopSongs(mediaFile, maxTopSongs);
|
||||
|
||||
return new ArtistInfo(similarArtists, artistBio, topSongs);
|
||||
}
|
||||
|
||||
private List<TopSong> getTopSongs(MediaFile mediaFile, int limit) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
|
||||
List<TopSong> result = new ArrayList<TopSong>();
|
||||
List<MediaFile> files = lastFmService.getTopSongs(mediaFile, limit, musicFolders);
|
||||
mediaFileService.populateStarredDate(files, username);
|
||||
for (MediaFile file : files) {
|
||||
result.add(new TopSong(file.getId(), file.getTitle(), file.getArtist(), file.getAlbumName(),
|
||||
file.getDurationString(), file.getStarredDate() != null));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<SimilarArtist> getSimilarArtists(int mediaFileId, int limit) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
|
||||
MediaFile artist = mediaFileService.getMediaFile(mediaFileId);
|
||||
List<MediaFile> similarArtists = lastFmService.getSimilarArtists(artist, limit, false, musicFolders);
|
||||
SimilarArtist[] result = new SimilarArtist[similarArtists.size()];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
MediaFile similarArtist = similarArtists.get(i);
|
||||
result[i] = new SimilarArtist(similarArtist.getId(), similarArtist.getName());
|
||||
}
|
||||
return Arrays.asList(result);
|
||||
}
|
||||
|
||||
public void setShowSideBar(boolean show) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
userSettings.setShowSideBar(show);
|
||||
userSettings.setChanged(new Date());
|
||||
settingsService.updateUserSettings(userSettings);
|
||||
}
|
||||
|
||||
public void setNetworkService(NetworkService networkService) {
|
||||
this.networkService = networkService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setLastFmService(LastFmService lastFmService) {
|
||||
this.lastFmService = lastFmService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class NetworkStatus {
|
||||
private final String portForwardingStatusText;
|
||||
private final Date portForwardingStatusDate;
|
||||
private final String urlRedirectionStatusText;
|
||||
private final Date urlRedirectionStatusDate;
|
||||
|
||||
public NetworkStatus(String portForwardingStatusText, Date portForwardingStatusDate,
|
||||
String urlRedirectionStatusText, Date urlRedirectionStatusDate) {
|
||||
this.portForwardingStatusText = portForwardingStatusText;
|
||||
this.portForwardingStatusDate = portForwardingStatusDate;
|
||||
this.urlRedirectionStatusText = urlRedirectionStatusText;
|
||||
this.urlRedirectionStatusDate = urlRedirectionStatusDate;
|
||||
}
|
||||
|
||||
public String getPortForwardingStatusText() {
|
||||
return portForwardingStatusText;
|
||||
}
|
||||
|
||||
public Date getPortForwardingStatusDate() {
|
||||
return portForwardingStatusDate;
|
||||
}
|
||||
|
||||
public String getUrlRedirectionStatusText() {
|
||||
return urlRedirectionStatusText;
|
||||
}
|
||||
|
||||
public Date getUrlRedirectionStatusDate() {
|
||||
return urlRedirectionStatusDate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
/**
|
||||
* Details about what a user is currently listening to.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class NowPlayingInfo {
|
||||
|
||||
private final String playerId;
|
||||
private final String username;
|
||||
private final String artist;
|
||||
private final String title;
|
||||
private final String tooltip;
|
||||
private final String streamUrl;
|
||||
private final String albumUrl;
|
||||
private final String lyricsUrl;
|
||||
private final String coverArtUrl;
|
||||
private final String avatarUrl;
|
||||
private final int minutesAgo;
|
||||
|
||||
public NowPlayingInfo(String playerId, String user, String artist, String title, String tooltip, String streamUrl, String albumUrl,
|
||||
String lyricsUrl, String coverArtUrl, String avatarUrl, int minutesAgo) {
|
||||
this.playerId = playerId;
|
||||
this.username = user;
|
||||
this.artist = artist;
|
||||
this.title = title;
|
||||
this.tooltip = tooltip;
|
||||
this.streamUrl = streamUrl;
|
||||
this.albumUrl = albumUrl;
|
||||
this.lyricsUrl = lyricsUrl;
|
||||
this.coverArtUrl = coverArtUrl;
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.minutesAgo = minutesAgo;
|
||||
}
|
||||
|
||||
public String getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getTooltip() {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
public String getStreamUrl() {
|
||||
return streamUrl;
|
||||
}
|
||||
|
||||
public String getAlbumUrl() {
|
||||
return albumUrl;
|
||||
}
|
||||
|
||||
public String getLyricsUrl() {
|
||||
return lyricsUrl;
|
||||
}
|
||||
|
||||
public String getCoverArtUrl() {
|
||||
return coverArtUrl;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
public int getMinutesAgo() {
|
||||
return minutesAgo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.directwebremoting.WebContext;
|
||||
import org.directwebremoting.WebContextFactory;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.domain.AvatarScheme;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.PlayStatus;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.MediaScannerService;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.service.StatusService;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for retrieving the currently playing file and directory.
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class NowPlayingService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(NowPlayingService.class);
|
||||
|
||||
private PlayerService playerService;
|
||||
private StatusService statusService;
|
||||
private SettingsService settingsService;
|
||||
private MediaScannerService mediaScannerService;
|
||||
|
||||
/**
|
||||
* Returns details about what the current player is playing.
|
||||
*
|
||||
* @return Details about what the current player is playing, or <code>null</code> if not playing anything.
|
||||
*/
|
||||
public NowPlayingInfo getNowPlayingForCurrentPlayer() throws Exception {
|
||||
WebContext webContext = WebContextFactory.get();
|
||||
Player player = playerService.getPlayer(webContext.getHttpServletRequest(), webContext.getHttpServletResponse());
|
||||
|
||||
for (NowPlayingInfo info : getNowPlaying()) {
|
||||
if (player.getId().equals(info.getPlayerId())) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns details about what all users are currently playing.
|
||||
*
|
||||
* @return Details about what all users are currently playing.
|
||||
*/
|
||||
public List<NowPlayingInfo> getNowPlaying() throws Exception {
|
||||
try {
|
||||
return convert(statusService.getPlayStatuses());
|
||||
} catch (Throwable x) {
|
||||
LOG.error("Unexpected error in getNowPlaying: " + x, x);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns media folder scanning status.
|
||||
*/
|
||||
public ScanInfo getScanningStatus() {
|
||||
return new ScanInfo(mediaScannerService.isScanning(), mediaScannerService.getScanCount());
|
||||
}
|
||||
|
||||
private List<NowPlayingInfo> convert(List<PlayStatus> playStatuses) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String url = request.getRequestURL().toString();
|
||||
List<NowPlayingInfo> result = new ArrayList<NowPlayingInfo>();
|
||||
for (PlayStatus status : playStatuses) {
|
||||
|
||||
Player player = status.getPlayer();
|
||||
MediaFile mediaFile = status.getMediaFile();
|
||||
String username = player.getUsername();
|
||||
if (username == null) {
|
||||
continue;
|
||||
}
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
if (!userSettings.isNowPlayingAllowed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String artist = mediaFile.getArtist();
|
||||
String title = mediaFile.getTitle();
|
||||
String streamUrl = url.replaceFirst("/dwr/.*", "/stream?player=" + player.getId() + "&id=" + mediaFile.getId());
|
||||
String albumUrl = url.replaceFirst("/dwr/.*", "/main.view?id=" + mediaFile.getId());
|
||||
String lyricsUrl = null;
|
||||
if (!mediaFile.isVideo()) {
|
||||
lyricsUrl = url.replaceFirst("/dwr/.*", "/lyrics.view?artistUtf8Hex=" + StringUtil.utf8HexEncode(artist) +
|
||||
"&songUtf8Hex=" + StringUtil.utf8HexEncode(title));
|
||||
}
|
||||
String coverArtUrl = url.replaceFirst("/dwr/.*", "/coverArt.view?size=60&id=" + mediaFile.getId());
|
||||
|
||||
String avatarUrl = null;
|
||||
if (userSettings.getAvatarScheme() == AvatarScheme.SYSTEM) {
|
||||
avatarUrl = url.replaceFirst("/dwr/.*", "/avatar.view?id=" + userSettings.getSystemAvatarId());
|
||||
} else if (userSettings.getAvatarScheme() == AvatarScheme.CUSTOM && settingsService.getCustomAvatar(username) != null) {
|
||||
avatarUrl = url.replaceFirst("/dwr/.*", "/avatar.view?usernameUtf8Hex=" + StringUtil.utf8HexEncode(username));
|
||||
}
|
||||
|
||||
// Rewrite URLs in case we're behind a proxy.
|
||||
if (settingsService.isRewriteUrlEnabled()) {
|
||||
String referer = request.getHeader("referer");
|
||||
streamUrl = StringUtil.rewriteUrl(streamUrl, referer);
|
||||
albumUrl = StringUtil.rewriteUrl(albumUrl, referer);
|
||||
lyricsUrl = StringUtil.rewriteUrl(lyricsUrl, referer);
|
||||
coverArtUrl = StringUtil.rewriteUrl(coverArtUrl, referer);
|
||||
avatarUrl = StringUtil.rewriteUrl(avatarUrl, referer);
|
||||
}
|
||||
|
||||
String tooltip = StringUtil.toHtml(artist) + " – " + StringUtil.toHtml(title);
|
||||
|
||||
if (StringUtils.isNotBlank(player.getName())) {
|
||||
username += "@" + player.getName();
|
||||
}
|
||||
artist = StringUtil.toHtml(StringUtils.abbreviate(artist, 25));
|
||||
title = StringUtil.toHtml(StringUtils.abbreviate(title, 25));
|
||||
username = StringUtil.toHtml(StringUtils.abbreviate(username, 25));
|
||||
|
||||
long minutesAgo = status.getMinutesAgo();
|
||||
|
||||
if (minutesAgo < 60) {
|
||||
result.add(new NowPlayingInfo(player.getId(),username, artist, title, tooltip, streamUrl, albumUrl, lyricsUrl,
|
||||
coverArtUrl, avatarUrl, (int) minutesAgo));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setStatusService(StatusService statusService) {
|
||||
this.statusService = statusService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setMediaScannerService(MediaScannerService mediaScannerService) {
|
||||
this.mediaScannerService = mediaScannerService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
/**
|
||||
* The playlist of a player.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlayQueueInfo {
|
||||
|
||||
private final List<Entry> entries;
|
||||
private final boolean stopEnabled;
|
||||
private final boolean repeatEnabled;
|
||||
private final boolean sendM3U;
|
||||
private final float gain;
|
||||
private int startPlayerAt = -1;
|
||||
private long startPlayerAtPosition; // millis
|
||||
|
||||
public PlayQueueInfo(List<Entry> entries, boolean stopEnabled, boolean repeatEnabled, boolean sendM3U, float gain) {
|
||||
this.entries = entries;
|
||||
this.stopEnabled = stopEnabled;
|
||||
this.repeatEnabled = repeatEnabled;
|
||||
this.sendM3U = sendM3U;
|
||||
this.gain = gain;
|
||||
}
|
||||
|
||||
public List<Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public String getDurationAsString() {
|
||||
int durationSeconds = 0;
|
||||
for (Entry entry : entries) {
|
||||
if (entry.getDuration() != null) {
|
||||
durationSeconds += entry.getDuration();
|
||||
}
|
||||
}
|
||||
return StringUtil.formatDuration(durationSeconds);
|
||||
}
|
||||
|
||||
public boolean isStopEnabled() {
|
||||
return stopEnabled;
|
||||
}
|
||||
|
||||
public boolean isSendM3U() {
|
||||
return sendM3U;
|
||||
}
|
||||
|
||||
public boolean isRepeatEnabled() {
|
||||
return repeatEnabled;
|
||||
}
|
||||
|
||||
public float getGain() {
|
||||
return gain;
|
||||
}
|
||||
|
||||
public int getStartPlayerAt() {
|
||||
return startPlayerAt;
|
||||
}
|
||||
|
||||
public PlayQueueInfo setStartPlayerAt(int startPlayerAt) {
|
||||
this.startPlayerAt = startPlayerAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getStartPlayerAtPosition() {
|
||||
return startPlayerAtPosition;
|
||||
}
|
||||
|
||||
public PlayQueueInfo setStartPlayerAtPosition(long startPlayerAtPosition) {
|
||||
this.startPlayerAtPosition = startPlayerAtPosition;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class Entry {
|
||||
private final int id;
|
||||
private final Integer trackNumber;
|
||||
private final String title;
|
||||
private final String artist;
|
||||
private final String album;
|
||||
private final String genre;
|
||||
private final Integer year;
|
||||
private final String bitRate;
|
||||
private final Integer duration;
|
||||
private final String durationAsString;
|
||||
private final String format;
|
||||
private final String contentType;
|
||||
private final String fileSize;
|
||||
private final boolean starred;
|
||||
private final String albumUrl;
|
||||
private final String streamUrl;
|
||||
private final String remoteStreamUrl;
|
||||
private final String coverArtUrl;
|
||||
private final String remoteCoverArtUrl;
|
||||
|
||||
public Entry(int id, Integer trackNumber, String title, String artist, String album, String genre, Integer year,
|
||||
String bitRate, Integer duration, String durationAsString, String format, String contentType, String fileSize,
|
||||
boolean starred, String albumUrl, String streamUrl, String remoteStreamUrl, String coverArtUrl, String remoteCoverArtUrl) {
|
||||
this.id = id;
|
||||
this.trackNumber = trackNumber;
|
||||
this.title = title;
|
||||
this.artist = artist;
|
||||
this.album = album;
|
||||
this.genre = genre;
|
||||
this.year = year;
|
||||
this.bitRate = bitRate;
|
||||
this.duration = duration;
|
||||
this.durationAsString = durationAsString;
|
||||
this.format = format;
|
||||
this.contentType = contentType;
|
||||
this.fileSize = fileSize;
|
||||
this.starred = starred;
|
||||
this.albumUrl = albumUrl;
|
||||
this.streamUrl = streamUrl;
|
||||
this.remoteStreamUrl = remoteStreamUrl;
|
||||
this.coverArtUrl = coverArtUrl;
|
||||
this.remoteCoverArtUrl = remoteCoverArtUrl;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Integer getTrackNumber() {
|
||||
return trackNumber;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public String getGenre() {
|
||||
return genre;
|
||||
}
|
||||
|
||||
public Integer getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
public String getBitRate() {
|
||||
return bitRate;
|
||||
}
|
||||
|
||||
public String getDurationAsString() {
|
||||
return durationAsString;
|
||||
}
|
||||
|
||||
public Integer getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public String getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public boolean isStarred() {
|
||||
return starred;
|
||||
}
|
||||
|
||||
public String getAlbumUrl() {
|
||||
return albumUrl;
|
||||
}
|
||||
|
||||
public String getStreamUrl() {
|
||||
return streamUrl;
|
||||
}
|
||||
|
||||
public String getRemoteStreamUrl() {
|
||||
return remoteStreamUrl;
|
||||
}
|
||||
|
||||
public String getCoverArtUrl() {
|
||||
return coverArtUrl;
|
||||
}
|
||||
|
||||
public String getRemoteCoverArtUrl() {
|
||||
return remoteCoverArtUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,737 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.directwebremoting.WebContextFactory;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.libresonic.player.dao.MediaFileDao;
|
||||
import org.libresonic.player.dao.PlayQueueDao;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.PlayQueue;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.PodcastEpisode;
|
||||
import org.libresonic.player.domain.PodcastStatus;
|
||||
import org.libresonic.player.domain.SavedPlayQueue;
|
||||
import org.libresonic.player.domain.UrlRedirectType;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.JukeboxService;
|
||||
import org.libresonic.player.service.LastFmService;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.PlaylistService;
|
||||
import org.libresonic.player.service.PodcastService;
|
||||
import org.libresonic.player.service.RatingService;
|
||||
import org.libresonic.player.service.SearchService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.service.TranscodingService;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for manipulating the play queue of a player.
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public class PlayQueueService {
|
||||
|
||||
private PlayerService playerService;
|
||||
private JukeboxService jukeboxService;
|
||||
private TranscodingService transcodingService;
|
||||
private SettingsService settingsService;
|
||||
private MediaFileService mediaFileService;
|
||||
private LastFmService lastFmService;
|
||||
private SecurityService securityService;
|
||||
private SearchService searchService;
|
||||
private RatingService ratingService;
|
||||
private PodcastService podcastService;
|
||||
private org.libresonic.player.service.PlaylistService playlistService;
|
||||
private MediaFileDao mediaFileDao;
|
||||
private PlayQueueDao playQueueDao;
|
||||
|
||||
/**
|
||||
* Returns the play queue for the player of the current user.
|
||||
*
|
||||
* @return The play queue.
|
||||
*/
|
||||
public PlayQueueInfo getPlayQueue() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo start() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
return doStart(request, response);
|
||||
}
|
||||
|
||||
public PlayQueueInfo doStart(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().setStatus(PlayQueue.Status.PLAYING);
|
||||
return convert(request, player, true);
|
||||
}
|
||||
|
||||
public PlayQueueInfo stop() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
return doStop(request, response);
|
||||
}
|
||||
|
||||
public PlayQueueInfo doStop(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().setStatus(PlayQueue.Status.STOPPED);
|
||||
return convert(request, player, true);
|
||||
}
|
||||
|
||||
public PlayQueueInfo skip(int index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
return doSkip(request, response, index, 0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo doSkip(HttpServletRequest request, HttpServletResponse response, int index, int offset) throws Exception {
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().setIndex(index);
|
||||
boolean serverSidePlaylist = !player.isExternalWithPlaylist();
|
||||
return convert(request, player, serverSidePlaylist, offset);
|
||||
}
|
||||
|
||||
public void savePlayQueue(int currentSongIndex, long positionMillis) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
List<Integer> ids = MediaFile.toIdList(playQueue.getFiles());
|
||||
|
||||
Integer currentId = currentSongIndex == -1 ? null : playQueue.getFile(currentSongIndex).getId();
|
||||
SavedPlayQueue savedPlayQueue = new SavedPlayQueue(null, username, ids, currentId, positionMillis, new Date(), "Libresonic");
|
||||
playQueueDao.savePlayQueue(savedPlayQueue);
|
||||
}
|
||||
|
||||
public PlayQueueInfo loadPlayQueue() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
SavedPlayQueue savedPlayQueue = playQueueDao.getPlayQueue(username);
|
||||
|
||||
if (savedPlayQueue == null) {
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
playQueue.clear();
|
||||
for (Integer mediaFileId : savedPlayQueue.getMediaFileIds()) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(mediaFileId);
|
||||
if (mediaFile != null) {
|
||||
playQueue.addFiles(true, mediaFile);
|
||||
}
|
||||
}
|
||||
PlayQueueInfo result = convert(request, player, false);
|
||||
|
||||
Integer currentId = savedPlayQueue.getCurrentMediaFileId();
|
||||
int currentIndex = -1;
|
||||
long positionMillis = savedPlayQueue.getPositionMillis() == null ? 0L : savedPlayQueue.getPositionMillis();
|
||||
if (currentId != null) {
|
||||
MediaFile current = mediaFileService.getMediaFile(currentId);
|
||||
currentIndex = playQueue.getFiles().indexOf(current);
|
||||
if (currentIndex != -1) {
|
||||
result.setStartPlayerAt(currentIndex);
|
||||
result.setStartPlayerAtPosition(positionMillis);
|
||||
}
|
||||
}
|
||||
|
||||
boolean serverSidePlaylist = !player.isExternalWithPlaylist();
|
||||
if (serverSidePlaylist && currentIndex != -1) {
|
||||
doSkip(request, response, currentIndex, (int) (positionMillis / 1000L));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public PlayQueueInfo play(int id) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
MediaFile file = mediaFileService.getMediaFile(id);
|
||||
|
||||
if (file.isFile()) {
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean queueFollowingSongs = settingsService.getUserSettings(username).isQueueFollowingSongs();
|
||||
List<MediaFile> songs;
|
||||
if (queueFollowingSongs) {
|
||||
MediaFile dir = mediaFileService.getParentOf(file);
|
||||
songs = mediaFileService.getChildrenOf(dir, true, false, true);
|
||||
if (!songs.isEmpty()) {
|
||||
int index = songs.indexOf(file);
|
||||
songs = songs.subList(index, songs.size());
|
||||
}
|
||||
} else {
|
||||
songs = Arrays.asList(file);
|
||||
}
|
||||
return doPlay(request, player, songs).setStartPlayerAt(0);
|
||||
} else {
|
||||
List<MediaFile> songs = mediaFileService.getDescendantsOf(file, true);
|
||||
return doPlay(request, player, songs).setStartPlayerAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index Start playing at this index, or play whole playlist if {@code null}.
|
||||
*/
|
||||
public PlayQueueInfo playPlaylist(int id, Integer index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean queueFollowingSongs = settingsService.getUserSettings(username).isQueueFollowingSongs();
|
||||
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
if (!files.isEmpty() && index != null) {
|
||||
if (queueFollowingSongs) {
|
||||
files = files.subList(index, files.size());
|
||||
} else {
|
||||
files = Arrays.asList(files.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove non-present files
|
||||
Iterator<MediaFile> iterator = files.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
MediaFile file = iterator.next();
|
||||
if (!file.isPresent()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index Start playing at this index, or play all top songs if {@code null}.
|
||||
*/
|
||||
public PlayQueueInfo playTopSong(int id, Integer index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean queueFollowingSongs = settingsService.getUserSettings(username).isQueueFollowingSongs();
|
||||
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
List<MediaFile> files = lastFmService.getTopSongs(mediaFileService.getMediaFile(id), 50, musicFolders);
|
||||
if (!files.isEmpty() && index != null) {
|
||||
if (queueFollowingSongs) {
|
||||
files = files.subList(index, files.size());
|
||||
} else {
|
||||
files = Arrays.asList(files.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playPodcastChannel(int id) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
List<PodcastEpisode> episodes = podcastService.getEpisodes(id);
|
||||
List<MediaFile> files = new ArrayList<MediaFile>();
|
||||
for (PodcastEpisode episode : episodes) {
|
||||
if (episode.getStatus() == PodcastStatus.COMPLETED) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(episode.getMediaFileId());
|
||||
if (mediaFile != null && mediaFile.isPresent()) {
|
||||
files.add(mediaFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playPodcastEpisode(int id) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
PodcastEpisode episode = podcastService.getEpisode(id, false);
|
||||
List<PodcastEpisode> allEpisodes = podcastService.getEpisodes(episode.getChannelId());
|
||||
List<MediaFile> files = new ArrayList<MediaFile>();
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean queueFollowingSongs = settingsService.getUserSettings(username).isQueueFollowingSongs();
|
||||
|
||||
for (PodcastEpisode ep : allEpisodes) {
|
||||
if (ep.getStatus() == PodcastStatus.COMPLETED) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(ep.getMediaFileId());
|
||||
if (mediaFile != null && mediaFile.isPresent() &&
|
||||
(ep.getId().equals(episode.getId()) || queueFollowingSongs && !files.isEmpty())) {
|
||||
files.add(mediaFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playNewestPodcastEpisode(Integer index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
List<PodcastEpisode> episodes = podcastService.getNewestEpisodes(10);
|
||||
List<MediaFile> files = Lists.transform(episodes, new Function<PodcastEpisode, MediaFile>() {
|
||||
@Override
|
||||
public MediaFile apply(PodcastEpisode episode) {
|
||||
return mediaFileService.getMediaFile(episode.getMediaFileId());
|
||||
}
|
||||
});
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean queueFollowingSongs = settingsService.getUserSettings(username).isQueueFollowingSongs();
|
||||
|
||||
if (!files.isEmpty() && index != null) {
|
||||
if (queueFollowingSongs) {
|
||||
files = files.subList(index, files.size());
|
||||
} else {
|
||||
files = Arrays.asList(files.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playStarred() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
List<MediaFile> files = mediaFileDao.getStarredFiles(0, Integer.MAX_VALUE, username, musicFolders);
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, files).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playShuffle(String albumListType, int offset, int count, String genre, String decade) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(securityService.getCurrentUsername(request));
|
||||
|
||||
MusicFolder selectedMusicFolder = settingsService.getSelectedMusicFolder(username);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username,
|
||||
selectedMusicFolder == null ? null : selectedMusicFolder.getId());
|
||||
List<MediaFile> albums;
|
||||
if ("highest".equals(albumListType)) {
|
||||
albums = ratingService.getHighestRatedAlbums(offset, count, musicFolders);
|
||||
} else if ("frequent".equals(albumListType)) {
|
||||
albums = mediaFileService.getMostFrequentlyPlayedAlbums(offset, count, musicFolders);
|
||||
} else if ("recent".equals(albumListType)) {
|
||||
albums = mediaFileService.getMostRecentlyPlayedAlbums(offset, count, musicFolders);
|
||||
} else if ("newest".equals(albumListType)) {
|
||||
albums = mediaFileService.getNewestAlbums(offset, count, musicFolders);
|
||||
} else if ("starred".equals(albumListType)) {
|
||||
albums = mediaFileService.getStarredAlbums(offset, count, username, musicFolders);
|
||||
} else if ("random".equals(albumListType)) {
|
||||
albums = searchService.getRandomAlbums(count, musicFolders);
|
||||
} else if ("alphabetical".equals(albumListType)) {
|
||||
albums = mediaFileService.getAlphabeticalAlbums(offset, count, true, musicFolders);
|
||||
} else if ("decade".equals(albumListType)) {
|
||||
int fromYear = Integer.parseInt(decade);
|
||||
int toYear = fromYear + 9;
|
||||
albums = mediaFileService.getAlbumsByYear(offset, count, fromYear, toYear, musicFolders);
|
||||
} else if ("genre".equals(albumListType)) {
|
||||
albums = mediaFileService.getAlbumsByGenre(offset, count, genre, musicFolders);
|
||||
} else {
|
||||
albums = Collections.emptyList();
|
||||
}
|
||||
|
||||
List<MediaFile> songs = new ArrayList<MediaFile>();
|
||||
for (MediaFile album : albums) {
|
||||
songs.addAll(mediaFileService.getChildrenOf(album, true, false, false));
|
||||
}
|
||||
Collections.shuffle(songs);
|
||||
songs = songs.subList(0, Math.min(40, songs.size()));
|
||||
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
return doPlay(request, player, songs).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
private PlayQueueInfo doPlay(HttpServletRequest request, Player player, List<MediaFile> files) throws Exception {
|
||||
if (player.isWeb()) {
|
||||
mediaFileService.removeVideoFiles(files);
|
||||
}
|
||||
player.getPlayQueue().addFiles(false, files);
|
||||
player.getPlayQueue().setRandomSearchCriteria(null);
|
||||
return convert(request, player, true);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playRandom(int id, int count) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
|
||||
MediaFile file = mediaFileService.getMediaFile(id);
|
||||
List<MediaFile> randomFiles = mediaFileService.getRandomSongsForParent(file, count);
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().addFiles(false, randomFiles);
|
||||
player.getPlayQueue().setRandomSearchCriteria(null);
|
||||
return convert(request, player, true).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo playSimilar(int id, int count) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
MediaFile artist = mediaFileService.getMediaFile(id);
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
List<MediaFile> similarSongs = lastFmService.getSimilarSongs(artist, count, musicFolders);
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().addFiles(false, similarSongs);
|
||||
return convert(request, player, true).setStartPlayerAt(0);
|
||||
}
|
||||
|
||||
public PlayQueueInfo add(int id) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
return doAdd(request, response, new int[]{id}, null);
|
||||
}
|
||||
|
||||
public PlayQueueInfo addAt(int id, int index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
return doAdd(request, response, new int[]{id}, index);
|
||||
}
|
||||
|
||||
public PlayQueueInfo doAdd(HttpServletRequest request, HttpServletResponse response, int[] ids, Integer index) throws Exception {
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
List<MediaFile> files = new ArrayList<MediaFile>(ids.length);
|
||||
for (int id : ids) {
|
||||
MediaFile ancestor = mediaFileService.getMediaFile(id);
|
||||
files.addAll(mediaFileService.getDescendantsOf(ancestor, true));
|
||||
}
|
||||
if (player.isWeb()) {
|
||||
mediaFileService.removeVideoFiles(files);
|
||||
}
|
||||
if (index != null) {
|
||||
player.getPlayQueue().addFilesAt(files, index);
|
||||
} else {
|
||||
player.getPlayQueue().addFiles(true, files);
|
||||
}
|
||||
player.getPlayQueue().setRandomSearchCriteria(null);
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo doSet(HttpServletRequest request, HttpServletResponse response, int[] ids) throws Exception {
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
MediaFile currentFile = playQueue.getCurrentFile();
|
||||
PlayQueue.Status status = playQueue.getStatus();
|
||||
|
||||
playQueue.clear();
|
||||
PlayQueueInfo result = doAdd(request, response, ids, null);
|
||||
|
||||
int index = currentFile == null ? -1 : playQueue.getFiles().indexOf(currentFile);
|
||||
playQueue.setIndex(index);
|
||||
playQueue.setStatus(status);
|
||||
return result;
|
||||
}
|
||||
|
||||
public PlayQueueInfo clear() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
return doClear(request, response);
|
||||
}
|
||||
|
||||
public PlayQueueInfo doClear(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().clear();
|
||||
boolean serverSidePlaylist = !player.isExternalWithPlaylist();
|
||||
return convert(request, player, serverSidePlaylist);
|
||||
}
|
||||
|
||||
public PlayQueueInfo shuffle() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
return doShuffle(request, response);
|
||||
}
|
||||
|
||||
public PlayQueueInfo doShuffle(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().shuffle();
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo remove(int index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
return doRemove(request, response, index);
|
||||
}
|
||||
|
||||
public PlayQueueInfo toggleStar(int index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
|
||||
MediaFile file = player.getPlayQueue().getFile(index);
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean starred = mediaFileDao.getMediaFileStarredDate(file.getId(), username) != null;
|
||||
if (starred) {
|
||||
mediaFileDao.unstarMediaFile(file.getId(), username);
|
||||
} else {
|
||||
mediaFileDao.starMediaFile(file.getId(), username);
|
||||
}
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo doRemove(HttpServletRequest request, HttpServletResponse response, int index) throws Exception {
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().removeFileAt(index);
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo removeMany(int[] indexes) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
for (int i = indexes.length - 1; i >= 0; i--) {
|
||||
player.getPlayQueue().removeFileAt(indexes[i]);
|
||||
}
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo rearrange(int[] indexes) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().rearrange(indexes);
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo up(int index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().moveUp(index);
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo down(int index) throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().moveDown(index);
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo toggleRepeat() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().setRepeatEnabled(!player.getPlayQueue().isRepeatEnabled());
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo undo() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().undo();
|
||||
boolean serverSidePlaylist = !player.isExternalWithPlaylist();
|
||||
return convert(request, player, serverSidePlaylist);
|
||||
}
|
||||
|
||||
public PlayQueueInfo sortByTrack() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().sort(PlayQueue.SortOrder.TRACK);
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo sortByArtist() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().sort(PlayQueue.SortOrder.ARTIST);
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public PlayQueueInfo sortByAlbum() throws Exception {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = getCurrentPlayer(request, response);
|
||||
player.getPlayQueue().sort(PlayQueue.SortOrder.ALBUM);
|
||||
return convert(request, player, false);
|
||||
}
|
||||
|
||||
public void setGain(float gain) {
|
||||
jukeboxService.setGain(gain);
|
||||
}
|
||||
|
||||
private PlayQueueInfo convert(HttpServletRequest request, Player player, boolean serverSidePlaylist) throws Exception {
|
||||
return convert(request, player, serverSidePlaylist, 0);
|
||||
}
|
||||
|
||||
private PlayQueueInfo convert(HttpServletRequest request, Player player, boolean serverSidePlaylist, int offset) throws Exception {
|
||||
String url = request.getRequestURL().toString();
|
||||
|
||||
if (serverSidePlaylist && player.isJukebox()) {
|
||||
jukeboxService.updateJukebox(player, offset);
|
||||
}
|
||||
boolean isCurrentPlayer = player.getIpAddress() != null && player.getIpAddress().equals(request.getRemoteAddr());
|
||||
|
||||
boolean m3uSupported = player.isExternal() || player.isExternalWithPlaylist();
|
||||
serverSidePlaylist = player.isAutoControlEnabled() && m3uSupported && isCurrentPlayer && serverSidePlaylist;
|
||||
Locale locale = RequestContextUtils.getLocale(request);
|
||||
|
||||
List<PlayQueueInfo.Entry> entries = new ArrayList<PlayQueueInfo.Entry>();
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
|
||||
for (MediaFile file : playQueue.getFiles()) {
|
||||
|
||||
String albumUrl = url.replaceFirst("/dwr/.*", "/main.view?id=" + file.getId());
|
||||
String streamUrl = url.replaceFirst("/dwr/.*", "/stream?player=" + player.getId() + "&id=" + file.getId());
|
||||
String coverArtUrl = url.replaceFirst("/dwr/.*", "/coverArt.view?id=" + file.getId());
|
||||
|
||||
// Rewrite URLs in case we're behind a proxy.
|
||||
if (settingsService.isRewriteUrlEnabled()) {
|
||||
String referer = request.getHeader("referer");
|
||||
albumUrl = StringUtil.rewriteUrl(albumUrl, referer);
|
||||
streamUrl = StringUtil.rewriteUrl(streamUrl, referer);
|
||||
}
|
||||
|
||||
String remoteStreamUrl = settingsService.rewriteRemoteUrl(streamUrl);
|
||||
String remoteCoverArtUrl = settingsService.rewriteRemoteUrl(coverArtUrl);
|
||||
|
||||
String format = formatFormat(player, file);
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
boolean starred = mediaFileService.getMediaFileStarredDate(file.getId(), username) != null;
|
||||
entries.add(new PlayQueueInfo.Entry(file.getId(), file.getTrackNumber(), file.getTitle(), file.getArtist(),
|
||||
file.getAlbumName(), file.getGenre(), file.getYear(), formatBitRate(file),
|
||||
file.getDurationSeconds(), file.getDurationString(), format, formatContentType(format),
|
||||
formatFileSize(file.getFileSize(), locale), starred, albumUrl, streamUrl, remoteStreamUrl,
|
||||
coverArtUrl, remoteCoverArtUrl));
|
||||
}
|
||||
boolean isStopEnabled = playQueue.getStatus() == PlayQueue.Status.PLAYING && !player.isExternalWithPlaylist();
|
||||
float gain = jukeboxService.getGain();
|
||||
return new PlayQueueInfo(entries, isStopEnabled, playQueue.isRepeatEnabled(), serverSidePlaylist, gain);
|
||||
}
|
||||
|
||||
private String formatFileSize(Long fileSize, Locale locale) {
|
||||
if (fileSize == null) {
|
||||
return null;
|
||||
}
|
||||
return StringUtil.formatBytes(fileSize, locale);
|
||||
}
|
||||
|
||||
private String formatFormat(Player player, MediaFile file) {
|
||||
return transcodingService.getSuffix(player, file, null);
|
||||
}
|
||||
|
||||
private String formatContentType(String format) {
|
||||
return StringUtil.getMimeType(format);
|
||||
}
|
||||
|
||||
private String formatBitRate(MediaFile mediaFile) {
|
||||
if (mediaFile.getBitRate() == null) {
|
||||
return null;
|
||||
}
|
||||
if (mediaFile.isVariableBitRate()) {
|
||||
return mediaFile.getBitRate() + " Kbps vbr";
|
||||
}
|
||||
return mediaFile.getBitRate() + " Kbps";
|
||||
}
|
||||
|
||||
private Player getCurrentPlayer(HttpServletRequest request, HttpServletResponse response) {
|
||||
return playerService.getPlayer(request, response);
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setLastFmService(LastFmService lastFmService) {
|
||||
this.lastFmService = lastFmService;
|
||||
}
|
||||
|
||||
public void setJukeboxService(JukeboxService jukeboxService) {
|
||||
this.jukeboxService = jukeboxService;
|
||||
}
|
||||
|
||||
public void setTranscodingService(TranscodingService transcodingService) {
|
||||
this.transcodingService = transcodingService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
public void setRatingService(RatingService ratingService) {
|
||||
this.ratingService = ratingService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
|
||||
public void setMediaFileDao(MediaFileDao mediaFileDao) {
|
||||
this.mediaFileDao = mediaFileDao;
|
||||
}
|
||||
|
||||
public void setPlayQueueDao(PlayQueueDao playQueueDao) {
|
||||
this.playQueueDao = playQueueDao;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.Playlist;
|
||||
|
||||
/**
|
||||
* The playlist of a player.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlaylistInfo {
|
||||
|
||||
private final Playlist playlist;
|
||||
private final List<Entry> entries;
|
||||
|
||||
public PlaylistInfo(Playlist playlist, List<Entry> entries) {
|
||||
this.playlist = playlist;
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
public Playlist getPlaylist() {
|
||||
return playlist;
|
||||
}
|
||||
|
||||
public List<Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static class Entry {
|
||||
private final int id;
|
||||
private final String title;
|
||||
private final String artist;
|
||||
private final String album;
|
||||
private final String durationAsString;
|
||||
private final boolean starred;
|
||||
private final boolean present;
|
||||
|
||||
public Entry(int id, String title, String artist, String album, String durationAsString, boolean starred, boolean present) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.artist = artist;
|
||||
this.album = album;
|
||||
this.durationAsString = durationAsString;
|
||||
this.starred = starred;
|
||||
this.present = present;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public String getDurationAsString() {
|
||||
return durationAsString;
|
||||
}
|
||||
|
||||
public boolean isStarred() {
|
||||
return starred;
|
||||
}
|
||||
|
||||
public boolean isPresent() {
|
||||
return present;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import org.libresonic.player.dao.MediaFileDao;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.Playlist;
|
||||
import org.libresonic.player.i18n.LibresonicLocaleResolver;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.directwebremoting.WebContextFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for manipulating playlists.
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlaylistService {
|
||||
|
||||
private MediaFileService mediaFileService;
|
||||
private SecurityService securityService;
|
||||
private org.libresonic.player.service.PlaylistService playlistService;
|
||||
private MediaFileDao mediaFileDao;
|
||||
private SettingsService settingsService;
|
||||
private PlayerService playerService;
|
||||
private LibresonicLocaleResolver localeResolver;
|
||||
|
||||
public List<Playlist> getReadablePlaylists() {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
return playlistService.getReadablePlaylistsForUser(username);
|
||||
}
|
||||
|
||||
public List<Playlist> getWritablePlaylists() {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
return playlistService.getWritablePlaylistsForUser(username);
|
||||
}
|
||||
|
||||
public PlaylistInfo getPlaylist(int id) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
|
||||
Playlist playlist = playlistService.getPlaylist(id);
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
mediaFileService.populateStarredDate(files, username);
|
||||
populateAccess(files, username);
|
||||
return new PlaylistInfo(playlist, createEntries(files));
|
||||
}
|
||||
|
||||
private void populateAccess(List<MediaFile> files, String username) {
|
||||
for (MediaFile file : files) {
|
||||
if (!securityService.isFolderAccessAllowed(file, username)) {
|
||||
file.setPresent(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Playlist> createEmptyPlaylist() {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
Locale locale = localeResolver.resolveLocale(request);
|
||||
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, locale);
|
||||
|
||||
Date now = new Date();
|
||||
Playlist playlist = new Playlist();
|
||||
playlist.setUsername(securityService.getCurrentUsername(request));
|
||||
playlist.setCreated(now);
|
||||
playlist.setChanged(now);
|
||||
playlist.setShared(false);
|
||||
playlist.setName(dateFormat.format(now));
|
||||
|
||||
playlistService.createPlaylist(playlist);
|
||||
return getReadablePlaylists();
|
||||
}
|
||||
|
||||
public int createPlaylistForPlayQueue() {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
HttpServletResponse response = WebContextFactory.get().getHttpServletResponse();
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
Locale locale = localeResolver.resolveLocale(request);
|
||||
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, locale);
|
||||
|
||||
Date now = new Date();
|
||||
Playlist playlist = new Playlist();
|
||||
playlist.setUsername(securityService.getCurrentUsername(request));
|
||||
playlist.setCreated(now);
|
||||
playlist.setChanged(now);
|
||||
playlist.setShared(false);
|
||||
playlist.setName(dateFormat.format(now));
|
||||
|
||||
playlistService.createPlaylist(playlist);
|
||||
playlistService.setFilesInPlaylist(playlist.getId(), player.getPlayQueue().getFiles());
|
||||
|
||||
return playlist.getId();
|
||||
}
|
||||
|
||||
public int createPlaylistForStarredSongs() {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
Locale locale = localeResolver.resolveLocale(request);
|
||||
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, locale);
|
||||
|
||||
Date now = new Date();
|
||||
Playlist playlist = new Playlist();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
playlist.setUsername(username);
|
||||
playlist.setCreated(now);
|
||||
playlist.setChanged(now);
|
||||
playlist.setShared(false);
|
||||
|
||||
ResourceBundle bundle = ResourceBundle.getBundle("org.libresonic.player.i18n.ResourceBundle", locale);
|
||||
playlist.setName(bundle.getString("top.starred") + " " + dateFormat.format(now));
|
||||
|
||||
playlistService.createPlaylist(playlist);
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
List<MediaFile> songs = mediaFileDao.getStarredFiles(0, Integer.MAX_VALUE, username, musicFolders);
|
||||
playlistService.setFilesInPlaylist(playlist.getId(), songs);
|
||||
|
||||
return playlist.getId();
|
||||
}
|
||||
|
||||
public void appendToPlaylist(int playlistId, List<Integer> mediaFileIds) {
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(playlistId, true);
|
||||
for (Integer mediaFileId : mediaFileIds) {
|
||||
MediaFile file = mediaFileService.getMediaFile(mediaFileId);
|
||||
if (file != null) {
|
||||
files.add(file);
|
||||
}
|
||||
}
|
||||
playlistService.setFilesInPlaylist(playlistId, files);
|
||||
}
|
||||
|
||||
private List<PlaylistInfo.Entry> createEntries(List<MediaFile> files) {
|
||||
List<PlaylistInfo.Entry> result = new ArrayList<PlaylistInfo.Entry>();
|
||||
for (MediaFile file : files) {
|
||||
result.add(new PlaylistInfo.Entry(file.getId(), file.getTitle(), file.getArtist(), file.getAlbumName(),
|
||||
file.getDurationString(), file.getStarredDate() != null, file.isPresent()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public PlaylistInfo toggleStar(int id, int index) {
|
||||
HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
MediaFile file = files.get(index);
|
||||
|
||||
boolean starred = mediaFileDao.getMediaFileStarredDate(file.getId(), username) != null;
|
||||
if (starred) {
|
||||
mediaFileDao.unstarMediaFile(file.getId(), username);
|
||||
} else {
|
||||
mediaFileDao.starMediaFile(file.getId(), username);
|
||||
}
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public PlaylistInfo remove(int id, int index) {
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
files.remove(index);
|
||||
playlistService.setFilesInPlaylist(id, files);
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public PlaylistInfo up(int id, int index) {
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
if (index > 0) {
|
||||
MediaFile file = files.remove(index);
|
||||
files.add(index - 1, file);
|
||||
playlistService.setFilesInPlaylist(id, files);
|
||||
}
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public PlaylistInfo rearrange(int id, int[] indexes) {
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
MediaFile[] newFiles = new MediaFile[files.size()];
|
||||
for (int i = 0; i < indexes.length; i++) {
|
||||
newFiles[i] = files.get(indexes[i]);
|
||||
}
|
||||
playlistService.setFilesInPlaylist(id, Arrays.asList(newFiles));
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public PlaylistInfo down(int id, int index) {
|
||||
List<MediaFile> files = playlistService.getFilesInPlaylist(id, true);
|
||||
if (index < files.size() - 1) {
|
||||
MediaFile file = files.remove(index);
|
||||
files.add(index + 1, file);
|
||||
playlistService.setFilesInPlaylist(id, files);
|
||||
}
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public void deletePlaylist(int id) {
|
||||
playlistService.deletePlaylist(id);
|
||||
}
|
||||
|
||||
public PlaylistInfo updatePlaylist(int id, String name, String comment, boolean shared) {
|
||||
Playlist playlist = playlistService.getPlaylist(id);
|
||||
playlist.setName(name);
|
||||
playlist.setComment(comment);
|
||||
playlist.setShared(shared);
|
||||
playlistService.updatePlaylist(playlist);
|
||||
return getPlaylist(id);
|
||||
}
|
||||
|
||||
public void setPlaylistService(org.libresonic.player.service.PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setMediaFileDao(MediaFileDao mediaFileDao) {
|
||||
this.mediaFileDao = mediaFileDao;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setLocaleResolver(LibresonicLocaleResolver localeResolver) {
|
||||
this.localeResolver = localeResolver;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
/**
|
||||
* Media folder scanning status.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ScanInfo {
|
||||
|
||||
private final boolean scanning;
|
||||
private final int count;
|
||||
|
||||
public ScanInfo(boolean scanning, int count) {
|
||||
this.scanning = scanning;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public boolean isScanning() {
|
||||
return scanning;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of Libresonic.
|
||||
*
|
||||
* Libresonic 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.
|
||||
*
|
||||
* Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
/**
|
||||
* Contains info about a similar artist.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SimilarArtist {
|
||||
|
||||
private final int mediaFileId;
|
||||
private final String artistName;
|
||||
|
||||
public SimilarArtist(int mediaFileId, String artistName) {
|
||||
this.mediaFileId = mediaFileId;
|
||||
this.artistName = artistName;
|
||||
}
|
||||
|
||||
public int getMediaFileId() {
|
||||
return mediaFileId;
|
||||
}
|
||||
|
||||
public String getArtistName() {
|
||||
return artistName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.dao.MediaFileDao;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.directwebremoting.WebContext;
|
||||
import org.directwebremoting.WebContextFactory;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for starring.
|
||||
* <p/>
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class StarService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(StarService.class);
|
||||
|
||||
private SecurityService securityService;
|
||||
private MediaFileDao mediaFileDao;
|
||||
|
||||
public void star(int id) {
|
||||
mediaFileDao.starMediaFile(id, getUser());
|
||||
}
|
||||
|
||||
public void unstar(int id) {
|
||||
mediaFileDao.unstarMediaFile(id, getUser());
|
||||
}
|
||||
|
||||
private String getUser() {
|
||||
WebContext webContext = WebContextFactory.get();
|
||||
User user = securityService.getCurrentUser(webContext.getHttpServletRequest());
|
||||
return user.getUsername();
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMediaFileDao(MediaFileDao mediaFileDao) {
|
||||
this.mediaFileDao = mediaFileDao;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.metadata.MetaData;
|
||||
import org.libresonic.player.service.metadata.MetaDataParser;
|
||||
import org.libresonic.player.service.metadata.MetaDataParserFactory;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for editing tags in music files.
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class TagService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(TagService.class);
|
||||
|
||||
private MetaDataParserFactory metaDataParserFactory;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
/**
|
||||
* Updated tags for a given music file.
|
||||
*
|
||||
* @param id The ID of the music file.
|
||||
* @param track The track number.
|
||||
* @param artist The artist name.
|
||||
* @param album The album name.
|
||||
* @param title The song title.
|
||||
* @param year The release year.
|
||||
* @param genre The musical genre.
|
||||
* @return "UPDATED" if the new tags were updated, "SKIPPED" if no update was necessary.
|
||||
* Otherwise the error message is returned.
|
||||
*/
|
||||
public String setTags(int id, String track, String artist, String album, String title, String year, String genre) {
|
||||
|
||||
track = StringUtils.trimToNull(track);
|
||||
artist = StringUtils.trimToNull(artist);
|
||||
album = StringUtils.trimToNull(album);
|
||||
title = StringUtils.trimToNull(title);
|
||||
year = StringUtils.trimToNull(year);
|
||||
genre = StringUtils.trimToNull(genre);
|
||||
|
||||
Integer trackNumber = null;
|
||||
if (track != null) {
|
||||
try {
|
||||
trackNumber = new Integer(track);
|
||||
} catch (NumberFormatException x) {
|
||||
LOG.warn("Illegal track number: " + track, x);
|
||||
}
|
||||
}
|
||||
|
||||
Integer yearNumber = null;
|
||||
if (year != null) {
|
||||
try {
|
||||
yearNumber = new Integer(year);
|
||||
} catch (NumberFormatException x) {
|
||||
LOG.warn("Illegal year: " + year, x);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
MediaFile file = mediaFileService.getMediaFile(id);
|
||||
MetaDataParser parser = metaDataParserFactory.getParser(file.getFile());
|
||||
|
||||
if (!parser.isEditingSupported()) {
|
||||
return "Tag editing of " + FilenameUtils.getExtension(file.getPath()) + " files is not supported.";
|
||||
}
|
||||
|
||||
if (StringUtils.equals(artist, file.getArtist()) &&
|
||||
StringUtils.equals(album, file.getAlbumName()) &&
|
||||
StringUtils.equals(title, file.getTitle()) &&
|
||||
ObjectUtils.equals(yearNumber, file.getYear()) &&
|
||||
StringUtils.equals(genre, file.getGenre()) &&
|
||||
ObjectUtils.equals(trackNumber, file.getTrackNumber())) {
|
||||
return "SKIPPED";
|
||||
}
|
||||
|
||||
MetaData newMetaData = parser.getMetaData(file.getFile());
|
||||
|
||||
// Note: album artist is intentionally set, as it is not user-changeable.
|
||||
newMetaData.setArtist(artist);
|
||||
newMetaData.setAlbumName(album);
|
||||
newMetaData.setTitle(title);
|
||||
newMetaData.setYear(yearNumber);
|
||||
newMetaData.setGenre(genre);
|
||||
newMetaData.setTrackNumber(trackNumber);
|
||||
parser.setMetaData(file, newMetaData);
|
||||
mediaFileService.refreshMediaFile(file);
|
||||
mediaFileService.refreshMediaFile(mediaFileService.getParentOf(file));
|
||||
return "UPDATED";
|
||||
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to update tags for " + id, x);
|
||||
return x.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setMetaDataParserFactory(MetaDataParserFactory metaDataParserFactory) {
|
||||
this.metaDataParserFactory = metaDataParserFactory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of Libresonic.
|
||||
*
|
||||
* Libresonic 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.
|
||||
*
|
||||
* Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2015 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
/**
|
||||
* See {@link ArtistInfo}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class TopSong {
|
||||
|
||||
private final int id;
|
||||
private final String title;
|
||||
private final String artist;
|
||||
private final String album;
|
||||
private final String durationAsString;
|
||||
private final boolean starred;
|
||||
|
||||
public TopSong(int id, String title, String artist, String album, String durationAsString, boolean starred) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.artist = artist;
|
||||
this.album = album;
|
||||
this.durationAsString = durationAsString;
|
||||
this.starred = starred;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public String getDurationAsString() {
|
||||
return durationAsString;
|
||||
}
|
||||
|
||||
public boolean isStarred() {
|
||||
return starred;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
import org.libresonic.player.domain.*;
|
||||
import org.libresonic.player.controller.*;
|
||||
import org.directwebremoting.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/**
|
||||
* Provides AJAX-enabled services for retrieving the status of ongoing transfers.
|
||||
* This class is used by the DWR framework (http://getahead.ltd.uk/dwr/).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class TransferService {
|
||||
|
||||
/**
|
||||
* Returns info about any ongoing upload within the current session.
|
||||
* @return Info about ongoing upload.
|
||||
*/
|
||||
public UploadInfo getUploadInfo() {
|
||||
|
||||
HttpSession session = WebContextFactory.get().getSession();
|
||||
TransferStatus status = (TransferStatus) session.getAttribute(UploadController.UPLOAD_STATUS);
|
||||
|
||||
if (status != null) {
|
||||
return new UploadInfo(status.getBytesTransfered(), status.getBytesTotal());
|
||||
}
|
||||
return new UploadInfo(0L, 0L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.ajax;
|
||||
|
||||
/**
|
||||
* Contains status for a file upload.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class UploadInfo {
|
||||
|
||||
private long bytesUploaded;
|
||||
private long bytesTotal;
|
||||
|
||||
public UploadInfo(long bytesUploaded, long bytesTotal) {
|
||||
this.bytesUploaded = bytesUploaded;
|
||||
this.bytesTotal = bytesTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes uploaded.
|
||||
* @return The number of bytes uploaded.
|
||||
*/
|
||||
public long getBytesUploaded() {
|
||||
return bytesUploaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of bytes.
|
||||
* @return The total number of bytes.
|
||||
*/
|
||||
public long getBytesTotal() {
|
||||
return bytesTotal;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.cache;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import net.sf.ehcache.config.Configuration;
|
||||
import net.sf.ehcache.config.ConfigurationFactory;
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Initializes Ehcache and creates caches.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
* @version $Id$
|
||||
*/
|
||||
public class CacheFactory implements InitializingBean {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(CacheFactory.class);
|
||||
private CacheManager cacheManager;
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Configuration configuration = ConfigurationFactory.parseConfiguration();
|
||||
|
||||
// Override configuration to make sure cache is stored in Libresonic home dir.
|
||||
File cacheDir = new File(SettingsService.getLibresonicHome(), "cache");
|
||||
configuration.getDiskStoreConfiguration().setPath(cacheDir.getPath());
|
||||
|
||||
cacheManager = CacheManager.create(configuration);
|
||||
}
|
||||
|
||||
public Ehcache getCache(String name) {
|
||||
return cacheManager.getCache(name);
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
import org.libresonic.player.controller.AdvancedSettingsController;
|
||||
|
||||
/**
|
||||
* Command used in {@link AdvancedSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class AdvancedSettingsCommand {
|
||||
|
||||
private String downloadLimit;
|
||||
private String uploadLimit;
|
||||
private boolean ldapEnabled;
|
||||
private String ldapUrl;
|
||||
private String ldapSearchFilter;
|
||||
private String ldapManagerDn;
|
||||
private String ldapManagerPassword;
|
||||
private boolean ldapAutoShadowing;
|
||||
private String brand;
|
||||
private boolean isReloadNeeded;
|
||||
private boolean toast;
|
||||
|
||||
public String getDownloadLimit() {
|
||||
return downloadLimit;
|
||||
}
|
||||
|
||||
public void setDownloadLimit(String downloadLimit) {
|
||||
this.downloadLimit = downloadLimit;
|
||||
}
|
||||
|
||||
public String getUploadLimit() {
|
||||
return uploadLimit;
|
||||
}
|
||||
|
||||
public void setUploadLimit(String uploadLimit) {
|
||||
this.uploadLimit = uploadLimit;
|
||||
}
|
||||
|
||||
public boolean isLdapEnabled() {
|
||||
return ldapEnabled;
|
||||
}
|
||||
|
||||
public void setLdapEnabled(boolean ldapEnabled) {
|
||||
this.ldapEnabled = ldapEnabled;
|
||||
}
|
||||
|
||||
public String getLdapUrl() {
|
||||
return ldapUrl;
|
||||
}
|
||||
|
||||
public void setLdapUrl(String ldapUrl) {
|
||||
this.ldapUrl = ldapUrl;
|
||||
}
|
||||
|
||||
public String getLdapSearchFilter() {
|
||||
return ldapSearchFilter;
|
||||
}
|
||||
|
||||
public void setLdapSearchFilter(String ldapSearchFilter) {
|
||||
this.ldapSearchFilter = ldapSearchFilter;
|
||||
}
|
||||
|
||||
public String getLdapManagerDn() {
|
||||
return ldapManagerDn;
|
||||
}
|
||||
|
||||
public void setLdapManagerDn(String ldapManagerDn) {
|
||||
this.ldapManagerDn = ldapManagerDn;
|
||||
}
|
||||
|
||||
public String getLdapManagerPassword() {
|
||||
return ldapManagerPassword;
|
||||
}
|
||||
|
||||
public void setLdapManagerPassword(String ldapManagerPassword) {
|
||||
this.ldapManagerPassword = ldapManagerPassword;
|
||||
}
|
||||
|
||||
public boolean isLdapAutoShadowing() {
|
||||
return ldapAutoShadowing;
|
||||
}
|
||||
|
||||
public void setLdapAutoShadowing(boolean ldapAutoShadowing) {
|
||||
this.ldapAutoShadowing = ldapAutoShadowing;
|
||||
}
|
||||
|
||||
public void setBrand(String brand) {
|
||||
this.brand = brand;
|
||||
}
|
||||
|
||||
public String getBrand() {
|
||||
return brand;
|
||||
}
|
||||
|
||||
public void setReloadNeeded(boolean reloadNeeded) {
|
||||
isReloadNeeded = reloadNeeded;
|
||||
}
|
||||
|
||||
public boolean isReloadNeeded() {
|
||||
return isReloadNeeded;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
/**
|
||||
* Holds the name and description of an enum value.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class EnumHolder {
|
||||
private String name;
|
||||
private String description;
|
||||
|
||||
public EnumHolder(String name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
import org.libresonic.player.controller.GeneralSettingsController;
|
||||
import org.libresonic.player.domain.Theme;
|
||||
|
||||
/**
|
||||
* Command used in {@link GeneralSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class GeneralSettingsCommand {
|
||||
|
||||
private String playlistFolder;
|
||||
private String musicFileTypes;
|
||||
private String videoFileTypes;
|
||||
private String coverArtFileTypes;
|
||||
private String index;
|
||||
private String ignoredArticles;
|
||||
private String shortcuts;
|
||||
private boolean sortAlbumsByYear;
|
||||
private boolean gettingStartedEnabled;
|
||||
private String welcomeTitle;
|
||||
private String welcomeSubtitle;
|
||||
private String welcomeMessage;
|
||||
private String loginMessage;
|
||||
private String localeIndex;
|
||||
private String[] locales;
|
||||
private String themeIndex;
|
||||
private Theme[] themes;
|
||||
private boolean isReloadNeeded;
|
||||
private boolean toast;
|
||||
|
||||
public String getPlaylistFolder() {
|
||||
return playlistFolder;
|
||||
}
|
||||
|
||||
public void setPlaylistFolder(String playlistFolder) {
|
||||
this.playlistFolder = playlistFolder;
|
||||
}
|
||||
|
||||
public String getMusicFileTypes() {
|
||||
return musicFileTypes;
|
||||
}
|
||||
|
||||
public void setMusicFileTypes(String musicFileTypes) {
|
||||
this.musicFileTypes = musicFileTypes;
|
||||
}
|
||||
|
||||
public String getVideoFileTypes() {
|
||||
return videoFileTypes;
|
||||
}
|
||||
|
||||
public void setVideoFileTypes(String videoFileTypes) {
|
||||
this.videoFileTypes = videoFileTypes;
|
||||
}
|
||||
|
||||
public String getCoverArtFileTypes() {
|
||||
return coverArtFileTypes;
|
||||
}
|
||||
|
||||
public void setCoverArtFileTypes(String coverArtFileTypes) {
|
||||
this.coverArtFileTypes = coverArtFileTypes;
|
||||
}
|
||||
|
||||
public String getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public void setIndex(String index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String getIgnoredArticles() {
|
||||
return ignoredArticles;
|
||||
}
|
||||
|
||||
public void setIgnoredArticles(String ignoredArticles) {
|
||||
this.ignoredArticles = ignoredArticles;
|
||||
}
|
||||
|
||||
public String getShortcuts() {
|
||||
return shortcuts;
|
||||
}
|
||||
|
||||
public void setShortcuts(String shortcuts) {
|
||||
this.shortcuts = shortcuts;
|
||||
}
|
||||
|
||||
public String getWelcomeTitle() {
|
||||
return welcomeTitle;
|
||||
}
|
||||
|
||||
public void setWelcomeTitle(String welcomeTitle) {
|
||||
this.welcomeTitle = welcomeTitle;
|
||||
}
|
||||
|
||||
public String getWelcomeSubtitle() {
|
||||
return welcomeSubtitle;
|
||||
}
|
||||
|
||||
public void setWelcomeSubtitle(String welcomeSubtitle) {
|
||||
this.welcomeSubtitle = welcomeSubtitle;
|
||||
}
|
||||
|
||||
public String getWelcomeMessage() {
|
||||
return welcomeMessage;
|
||||
}
|
||||
|
||||
public void setWelcomeMessage(String welcomeMessage) {
|
||||
this.welcomeMessage = welcomeMessage;
|
||||
}
|
||||
|
||||
public String getLoginMessage() {
|
||||
return loginMessage;
|
||||
}
|
||||
|
||||
public void setLoginMessage(String loginMessage) {
|
||||
this.loginMessage = loginMessage;
|
||||
}
|
||||
|
||||
public String getLocaleIndex() {
|
||||
return localeIndex;
|
||||
}
|
||||
|
||||
public void setLocaleIndex(String localeIndex) {
|
||||
this.localeIndex = localeIndex;
|
||||
}
|
||||
|
||||
public String[] getLocales() {
|
||||
return locales;
|
||||
}
|
||||
|
||||
public void setLocales(String[] locales) {
|
||||
this.locales = locales;
|
||||
}
|
||||
|
||||
public String getThemeIndex() {
|
||||
return themeIndex;
|
||||
}
|
||||
|
||||
public void setThemeIndex(String themeIndex) {
|
||||
this.themeIndex = themeIndex;
|
||||
}
|
||||
|
||||
public Theme[] getThemes() {
|
||||
return themes;
|
||||
}
|
||||
|
||||
public void setThemes(Theme[] themes) {
|
||||
this.themes = themes;
|
||||
}
|
||||
|
||||
public boolean isReloadNeeded() {
|
||||
return isReloadNeeded;
|
||||
}
|
||||
|
||||
public void setReloadNeeded(boolean reloadNeeded) {
|
||||
isReloadNeeded = reloadNeeded;
|
||||
}
|
||||
|
||||
public boolean isSortAlbumsByYear() {
|
||||
return sortAlbumsByYear;
|
||||
}
|
||||
|
||||
public void setSortAlbumsByYear(boolean sortAlbumsByYear) {
|
||||
this.sortAlbumsByYear = sortAlbumsByYear;
|
||||
}
|
||||
|
||||
public boolean isGettingStartedEnabled() {
|
||||
return gettingStartedEnabled;
|
||||
}
|
||||
|
||||
public void setGettingStartedEnabled(boolean gettingStartedEnabled) {
|
||||
this.gettingStartedEnabled = gettingStartedEnabled;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.libresonic.player.controller.MusicFolderSettingsController;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* Command used in {@link MusicFolderSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MusicFolderSettingsCommand {
|
||||
|
||||
private String interval;
|
||||
private String hour;
|
||||
private boolean scanning;
|
||||
private boolean fastCache;
|
||||
private boolean organizeByFolderStructure;
|
||||
private List<MusicFolderInfo> musicFolders;
|
||||
private MusicFolderInfo newMusicFolder;
|
||||
private boolean reload;
|
||||
|
||||
public String getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
public void setInterval(String interval) {
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
public String getHour() {
|
||||
return hour;
|
||||
}
|
||||
|
||||
public void setHour(String hour) {
|
||||
this.hour = hour;
|
||||
}
|
||||
|
||||
public boolean isScanning() {
|
||||
return scanning;
|
||||
}
|
||||
|
||||
public void setScanning(boolean scanning) {
|
||||
this.scanning = scanning;
|
||||
}
|
||||
|
||||
public boolean isFastCache() {
|
||||
return fastCache;
|
||||
}
|
||||
|
||||
public List<MusicFolderInfo> getMusicFolders() {
|
||||
return musicFolders;
|
||||
}
|
||||
|
||||
public void setMusicFolders(List<MusicFolderInfo> musicFolders) {
|
||||
this.musicFolders = musicFolders;
|
||||
}
|
||||
|
||||
public void setFastCache(boolean fastCache) {
|
||||
this.fastCache = fastCache;
|
||||
}
|
||||
|
||||
public MusicFolderInfo getNewMusicFolder() {
|
||||
return newMusicFolder;
|
||||
}
|
||||
|
||||
public void setNewMusicFolder(MusicFolderInfo newMusicFolder) {
|
||||
this.newMusicFolder = newMusicFolder;
|
||||
}
|
||||
|
||||
public void setReload(boolean reload) {
|
||||
this.reload = reload;
|
||||
}
|
||||
|
||||
public boolean isReload() {
|
||||
return reload;
|
||||
}
|
||||
|
||||
public boolean isOrganizeByFolderStructure() {
|
||||
return organizeByFolderStructure;
|
||||
}
|
||||
|
||||
public void setOrganizeByFolderStructure(boolean organizeByFolderStructure) {
|
||||
this.organizeByFolderStructure = organizeByFolderStructure;
|
||||
}
|
||||
|
||||
public static class MusicFolderInfo {
|
||||
|
||||
private Integer id;
|
||||
private String path;
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
private boolean delete;
|
||||
private boolean existing;
|
||||
|
||||
public MusicFolderInfo(MusicFolder musicFolder) {
|
||||
id = musicFolder.getId();
|
||||
path = musicFolder.getPath().getPath();
|
||||
name = musicFolder.getName();
|
||||
enabled = musicFolder.isEnabled();
|
||||
existing = musicFolder.getPath().exists() && musicFolder.getPath().isDirectory();
|
||||
}
|
||||
|
||||
public MusicFolderInfo() {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isDelete() {
|
||||
return delete;
|
||||
}
|
||||
|
||||
public void setDelete(boolean delete) {
|
||||
this.delete = delete;
|
||||
}
|
||||
|
||||
public MusicFolder toMusicFolder() {
|
||||
String path = StringUtils.trimToNull(this.path);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
File file = new File(path);
|
||||
String name = StringUtils.trimToNull(this.name);
|
||||
if (name == null) {
|
||||
name = file.getName();
|
||||
}
|
||||
return new MusicFolder(id, new File(path), name, enabled, new Date());
|
||||
}
|
||||
|
||||
public boolean isExisting() {
|
||||
return existing;
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
import org.libresonic.player.domain.LicenseInfo;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
* @version $Id$
|
||||
*/
|
||||
public class NetworkSettingsCommand {
|
||||
|
||||
private boolean portForwardingEnabled;
|
||||
private boolean urlRedirectionEnabled;
|
||||
private String urlRedirectFrom;
|
||||
private String urlRedirectCustomUrl;
|
||||
private String urlRedirectType;
|
||||
private int port;
|
||||
private boolean toast;
|
||||
private LicenseInfo licenseInfo;
|
||||
|
||||
public void setPortForwardingEnabled(boolean portForwardingEnabled) {
|
||||
this.portForwardingEnabled = portForwardingEnabled;
|
||||
}
|
||||
|
||||
public boolean isPortForwardingEnabled() {
|
||||
return portForwardingEnabled;
|
||||
}
|
||||
|
||||
public boolean isUrlRedirectionEnabled() {
|
||||
return urlRedirectionEnabled;
|
||||
}
|
||||
|
||||
public void setUrlRedirectionEnabled(boolean urlRedirectionEnabled) {
|
||||
this.urlRedirectionEnabled = urlRedirectionEnabled;
|
||||
}
|
||||
|
||||
public String getUrlRedirectFrom() {
|
||||
return urlRedirectFrom;
|
||||
}
|
||||
|
||||
public void setUrlRedirectFrom(String urlRedirectFrom) {
|
||||
this.urlRedirectFrom = urlRedirectFrom;
|
||||
}
|
||||
|
||||
public String getUrlRedirectCustomUrl() {
|
||||
return urlRedirectCustomUrl;
|
||||
}
|
||||
|
||||
public void setUrlRedirectCustomUrl(String urlRedirectCustomUrl) {
|
||||
this.urlRedirectCustomUrl = urlRedirectCustomUrl;
|
||||
}
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
|
||||
public void setLicenseInfo(LicenseInfo licenseInfo) {
|
||||
this.licenseInfo = licenseInfo;
|
||||
}
|
||||
|
||||
public LicenseInfo getLicenseInfo() {
|
||||
return licenseInfo;
|
||||
}
|
||||
|
||||
public String getUrlRedirectType() {
|
||||
return urlRedirectType;
|
||||
}
|
||||
|
||||
public void setUrlRedirectType(String urlRedirectType) {
|
||||
this.urlRedirectType = urlRedirectType;
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
import org.libresonic.player.controller.*;
|
||||
|
||||
/**
|
||||
* Command used in {@link PasswordSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PasswordSettingsCommand {
|
||||
private String username;
|
||||
private String password;
|
||||
private String confirmPassword;
|
||||
private boolean ldapAuthenticated;
|
||||
private boolean toast;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getConfirmPassword() {
|
||||
return confirmPassword;
|
||||
}
|
||||
|
||||
public void setConfirmPassword(String confirmPassword) {
|
||||
this.confirmPassword = confirmPassword;
|
||||
}
|
||||
|
||||
public boolean isLdapAuthenticated() {
|
||||
return ldapAuthenticated;
|
||||
}
|
||||
|
||||
public void setLdapAuthenticated(boolean ldapAuthenticated) {
|
||||
this.ldapAuthenticated = ldapAuthenticated;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
}
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.libresonic.player.controller.PersonalSettingsController;
|
||||
import org.libresonic.player.domain.AlbumListType;
|
||||
import org.libresonic.player.domain.Avatar;
|
||||
import org.libresonic.player.domain.Theme;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
|
||||
/**
|
||||
* Command used in {@link PersonalSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PersonalSettingsCommand {
|
||||
private User user;
|
||||
private String localeIndex;
|
||||
private String[] locales;
|
||||
private String themeIndex;
|
||||
private Theme[] themes;
|
||||
private String albumListId;
|
||||
private AlbumListType[] albumLists;
|
||||
private int avatarId;
|
||||
private List<Avatar> avatars;
|
||||
private Avatar customAvatar;
|
||||
private UserSettings.Visibility mainVisibility;
|
||||
private UserSettings.Visibility playlistVisibility;
|
||||
private boolean partyModeEnabled;
|
||||
private boolean showNowPlayingEnabled;
|
||||
private boolean showChatEnabled;
|
||||
private boolean showArtistInfoEnabled;
|
||||
private boolean nowPlayingAllowed;
|
||||
private boolean autoHidePlayQueue;
|
||||
private boolean finalVersionNotificationEnabled;
|
||||
private boolean betaVersionNotificationEnabled;
|
||||
private boolean songNotificationEnabled;
|
||||
private boolean queueFollowingSongs;
|
||||
private boolean lastFmEnabled;
|
||||
private String lastFmUsername;
|
||||
private String lastFmPassword;
|
||||
private boolean isReloadNeeded;
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getLocaleIndex() {
|
||||
return localeIndex;
|
||||
}
|
||||
|
||||
public void setLocaleIndex(String localeIndex) {
|
||||
this.localeIndex = localeIndex;
|
||||
}
|
||||
|
||||
public String[] getLocales() {
|
||||
return locales;
|
||||
}
|
||||
|
||||
public void setLocales(String[] locales) {
|
||||
this.locales = locales;
|
||||
}
|
||||
|
||||
public String getThemeIndex() {
|
||||
return themeIndex;
|
||||
}
|
||||
|
||||
public void setThemeIndex(String themeIndex) {
|
||||
this.themeIndex = themeIndex;
|
||||
}
|
||||
|
||||
public Theme[] getThemes() {
|
||||
return themes;
|
||||
}
|
||||
|
||||
public void setThemes(Theme[] themes) {
|
||||
this.themes = themes;
|
||||
}
|
||||
|
||||
public String getAlbumListId() {
|
||||
return albumListId;
|
||||
}
|
||||
|
||||
public void setAlbumListId(String albumListId) {
|
||||
this.albumListId = albumListId;
|
||||
}
|
||||
|
||||
public AlbumListType[] getAlbumLists() {
|
||||
return albumLists;
|
||||
}
|
||||
|
||||
public void setAlbumLists(AlbumListType[] albumLists) {
|
||||
this.albumLists = albumLists;
|
||||
}
|
||||
|
||||
public int getAvatarId() {
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
public void setAvatarId(int avatarId) {
|
||||
this.avatarId = avatarId;
|
||||
}
|
||||
|
||||
public List<Avatar> getAvatars() {
|
||||
return avatars;
|
||||
}
|
||||
|
||||
public void setAvatars(List<Avatar> avatars) {
|
||||
this.avatars = avatars;
|
||||
}
|
||||
|
||||
public Avatar getCustomAvatar() {
|
||||
return customAvatar;
|
||||
}
|
||||
|
||||
public void setCustomAvatar(Avatar customAvatar) {
|
||||
this.customAvatar = customAvatar;
|
||||
}
|
||||
|
||||
public UserSettings.Visibility getMainVisibility() {
|
||||
return mainVisibility;
|
||||
}
|
||||
|
||||
public void setMainVisibility(UserSettings.Visibility mainVisibility) {
|
||||
this.mainVisibility = mainVisibility;
|
||||
}
|
||||
|
||||
public UserSettings.Visibility getPlaylistVisibility() {
|
||||
return playlistVisibility;
|
||||
}
|
||||
|
||||
public void setPlaylistVisibility(UserSettings.Visibility playlistVisibility) {
|
||||
this.playlistVisibility = playlistVisibility;
|
||||
}
|
||||
|
||||
public boolean isPartyModeEnabled() {
|
||||
return partyModeEnabled;
|
||||
}
|
||||
|
||||
public void setPartyModeEnabled(boolean partyModeEnabled) {
|
||||
this.partyModeEnabled = partyModeEnabled;
|
||||
}
|
||||
|
||||
public boolean isShowNowPlayingEnabled() {
|
||||
return showNowPlayingEnabled;
|
||||
}
|
||||
|
||||
public void setShowNowPlayingEnabled(boolean showNowPlayingEnabled) {
|
||||
this.showNowPlayingEnabled = showNowPlayingEnabled;
|
||||
}
|
||||
|
||||
public boolean isShowChatEnabled() {
|
||||
return showChatEnabled;
|
||||
}
|
||||
|
||||
public void setShowChatEnabled(boolean showChatEnabled) {
|
||||
this.showChatEnabled = showChatEnabled;
|
||||
}
|
||||
|
||||
public boolean isShowArtistInfoEnabled() {
|
||||
return showArtistInfoEnabled;
|
||||
}
|
||||
|
||||
public void setShowArtistInfoEnabled(boolean showArtistInfoEnabled) {
|
||||
this.showArtistInfoEnabled = showArtistInfoEnabled;
|
||||
}
|
||||
|
||||
public boolean isNowPlayingAllowed() {
|
||||
return nowPlayingAllowed;
|
||||
}
|
||||
|
||||
public void setNowPlayingAllowed(boolean nowPlayingAllowed) {
|
||||
this.nowPlayingAllowed = nowPlayingAllowed;
|
||||
}
|
||||
|
||||
public boolean isFinalVersionNotificationEnabled() {
|
||||
return finalVersionNotificationEnabled;
|
||||
}
|
||||
|
||||
public void setFinalVersionNotificationEnabled(boolean finalVersionNotificationEnabled) {
|
||||
this.finalVersionNotificationEnabled = finalVersionNotificationEnabled;
|
||||
}
|
||||
|
||||
public boolean isBetaVersionNotificationEnabled() {
|
||||
return betaVersionNotificationEnabled;
|
||||
}
|
||||
|
||||
public void setBetaVersionNotificationEnabled(boolean betaVersionNotificationEnabled) {
|
||||
this.betaVersionNotificationEnabled = betaVersionNotificationEnabled;
|
||||
}
|
||||
|
||||
public void setSongNotificationEnabled(boolean songNotificationEnabled) {
|
||||
this.songNotificationEnabled = songNotificationEnabled;
|
||||
}
|
||||
|
||||
public boolean isSongNotificationEnabled() {
|
||||
return songNotificationEnabled;
|
||||
}
|
||||
|
||||
public boolean isAutoHidePlayQueue() {
|
||||
return autoHidePlayQueue;
|
||||
}
|
||||
|
||||
public void setAutoHidePlayQueue(boolean autoHidePlayQueue) {
|
||||
this.autoHidePlayQueue = autoHidePlayQueue;
|
||||
}
|
||||
|
||||
public boolean isLastFmEnabled() {
|
||||
return lastFmEnabled;
|
||||
}
|
||||
|
||||
public void setLastFmEnabled(boolean lastFmEnabled) {
|
||||
this.lastFmEnabled = lastFmEnabled;
|
||||
}
|
||||
|
||||
public String getLastFmUsername() {
|
||||
return lastFmUsername;
|
||||
}
|
||||
|
||||
public void setLastFmUsername(String lastFmUsername) {
|
||||
this.lastFmUsername = lastFmUsername;
|
||||
}
|
||||
|
||||
public String getLastFmPassword() {
|
||||
return lastFmPassword;
|
||||
}
|
||||
|
||||
public void setLastFmPassword(String lastFmPassword) {
|
||||
this.lastFmPassword = lastFmPassword;
|
||||
}
|
||||
|
||||
public boolean isReloadNeeded() {
|
||||
return isReloadNeeded;
|
||||
}
|
||||
|
||||
public void setReloadNeeded(boolean reloadNeeded) {
|
||||
isReloadNeeded = reloadNeeded;
|
||||
}
|
||||
|
||||
public boolean isQueueFollowingSongs() {
|
||||
return queueFollowingSongs;
|
||||
}
|
||||
|
||||
public void setQueueFollowingSongs(boolean queueFollowingSongs) {
|
||||
this.queueFollowingSongs = queueFollowingSongs;
|
||||
}
|
||||
}
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.libresonic.player.controller.PlayerSettingsController;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.PlayerTechnology;
|
||||
import org.libresonic.player.domain.TranscodeScheme;
|
||||
import org.libresonic.player.domain.Transcoding;
|
||||
|
||||
/**
|
||||
* Command used in {@link PlayerSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlayerSettingsCommand {
|
||||
private String playerId;
|
||||
private String name;
|
||||
private String description;
|
||||
private String type;
|
||||
private Date lastSeen;
|
||||
private boolean isDynamicIp;
|
||||
private boolean isAutoControlEnabled;
|
||||
private String technologyName;
|
||||
private String transcodeSchemeName;
|
||||
private boolean transcodingSupported;
|
||||
private String transcodeDirectory;
|
||||
private List<Transcoding> allTranscodings;
|
||||
private int[] activeTranscodingIds;
|
||||
private EnumHolder[] technologyHolders;
|
||||
private EnumHolder[] transcodeSchemeHolders;
|
||||
private Player[] players;
|
||||
private boolean isAdmin;
|
||||
private boolean isReloadNeeded;
|
||||
|
||||
public String getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
|
||||
public void setPlayerId(String playerId) {
|
||||
this.playerId = playerId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Date getLastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
public void setLastSeen(Date lastSeen) {
|
||||
this.lastSeen = lastSeen;
|
||||
}
|
||||
|
||||
public boolean isDynamicIp() {
|
||||
return isDynamicIp;
|
||||
}
|
||||
|
||||
public void setDynamicIp(boolean dynamicIp) {
|
||||
isDynamicIp = dynamicIp;
|
||||
}
|
||||
|
||||
public boolean isAutoControlEnabled() {
|
||||
return isAutoControlEnabled;
|
||||
}
|
||||
|
||||
public void setAutoControlEnabled(boolean autoControlEnabled) {
|
||||
isAutoControlEnabled = autoControlEnabled;
|
||||
}
|
||||
|
||||
public String getTranscodeSchemeName() {
|
||||
return transcodeSchemeName;
|
||||
}
|
||||
|
||||
public void setTranscodeSchemeName(String transcodeSchemeName) {
|
||||
this.transcodeSchemeName = transcodeSchemeName;
|
||||
}
|
||||
|
||||
public boolean isTranscodingSupported() {
|
||||
return transcodingSupported;
|
||||
}
|
||||
|
||||
public void setTranscodingSupported(boolean transcodingSupported) {
|
||||
this.transcodingSupported = transcodingSupported;
|
||||
}
|
||||
|
||||
public String getTranscodeDirectory() {
|
||||
return transcodeDirectory;
|
||||
}
|
||||
|
||||
public void setTranscodeDirectory(String transcodeDirectory) {
|
||||
this.transcodeDirectory = transcodeDirectory;
|
||||
}
|
||||
|
||||
public List<Transcoding> getAllTranscodings() {
|
||||
return allTranscodings;
|
||||
}
|
||||
|
||||
public void setAllTranscodings(List<Transcoding> allTranscodings) {
|
||||
this.allTranscodings = allTranscodings;
|
||||
}
|
||||
|
||||
public int[] getActiveTranscodingIds() {
|
||||
return activeTranscodingIds;
|
||||
}
|
||||
|
||||
public void setActiveTranscodingIds(int[] activeTranscodingIds) {
|
||||
this.activeTranscodingIds = activeTranscodingIds;
|
||||
}
|
||||
|
||||
public EnumHolder[] getTechnologyHolders() {
|
||||
return technologyHolders;
|
||||
}
|
||||
|
||||
public void setTechnologies(PlayerTechnology[] technologies) {
|
||||
technologyHolders = new EnumHolder[technologies.length];
|
||||
for (int i = 0; i < technologies.length; i++) {
|
||||
PlayerTechnology technology = technologies[i];
|
||||
technologyHolders[i] = new EnumHolder(technology.name(), technology.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public EnumHolder[] getTranscodeSchemeHolders() {
|
||||
return transcodeSchemeHolders;
|
||||
}
|
||||
|
||||
public void setTranscodeSchemes(TranscodeScheme[] transcodeSchemes) {
|
||||
transcodeSchemeHolders = new EnumHolder[transcodeSchemes.length];
|
||||
for (int i = 0; i < transcodeSchemes.length; i++) {
|
||||
TranscodeScheme scheme = transcodeSchemes[i];
|
||||
transcodeSchemeHolders[i] = new EnumHolder(scheme.name(), scheme.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public String getTechnologyName() {
|
||||
return technologyName;
|
||||
}
|
||||
|
||||
public void setTechnologyName(String technologyName) {
|
||||
this.technologyName = technologyName;
|
||||
}
|
||||
|
||||
public Player[] getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
public void setPlayers(Player[] players) {
|
||||
this.players = players;
|
||||
}
|
||||
|
||||
public boolean isAdmin() {
|
||||
return isAdmin;
|
||||
}
|
||||
|
||||
public void setAdmin(boolean admin) {
|
||||
isAdmin = admin;
|
||||
}
|
||||
|
||||
public boolean isReloadNeeded() {
|
||||
return isReloadNeeded;
|
||||
}
|
||||
|
||||
public void setReloadNeeded(boolean reloadNeeded) {
|
||||
isReloadNeeded = reloadNeeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the transcoding and whether it is active for the given player.
|
||||
*/
|
||||
public static class TranscodingHolder {
|
||||
private Transcoding transcoding;
|
||||
private boolean isActive;
|
||||
|
||||
public TranscodingHolder(Transcoding transcoding, boolean isActive) {
|
||||
this.transcoding = transcoding;
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
public Transcoding getTranscoding() {
|
||||
return transcoding;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return isActive;
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
import org.libresonic.player.controller.PodcastSettingsController;
|
||||
|
||||
/**
|
||||
* Command used in {@link PodcastSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastSettingsCommand {
|
||||
|
||||
private String interval;
|
||||
private String folder;
|
||||
private String episodeRetentionCount;
|
||||
private String episodeDownloadCount;
|
||||
private boolean toast;
|
||||
|
||||
public String getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
public void setInterval(String interval) {
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
public String getFolder() {
|
||||
return folder;
|
||||
}
|
||||
|
||||
public void setFolder(String folder) {
|
||||
this.folder = folder;
|
||||
}
|
||||
|
||||
public String getEpisodeRetentionCount() {
|
||||
return episodeRetentionCount;
|
||||
}
|
||||
|
||||
public void setEpisodeRetentionCount(String episodeRetentionCount) {
|
||||
this.episodeRetentionCount = episodeRetentionCount;
|
||||
}
|
||||
|
||||
public String getEpisodeDownloadCount() {
|
||||
return episodeDownloadCount;
|
||||
}
|
||||
|
||||
public void setEpisodeDownloadCount(String episodeDownloadCount) {
|
||||
this.episodeDownloadCount = episodeDownloadCount;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import org.libresonic.player.controller.PremiumSettingsController;
|
||||
import org.libresonic.player.domain.LicenseInfo;
|
||||
import org.libresonic.player.domain.User;
|
||||
|
||||
/**
|
||||
* Command used in {@link PremiumSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PremiumSettingsCommand {
|
||||
|
||||
private String path;
|
||||
private String brand;
|
||||
private LicenseInfo licenseInfo;
|
||||
private String licenseCode;
|
||||
private boolean forceChange;
|
||||
private boolean submissionError;
|
||||
private User user;
|
||||
private boolean toast;
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getBrand() {
|
||||
return brand;
|
||||
}
|
||||
|
||||
public void setBrand(String brand) {
|
||||
this.brand = brand;
|
||||
}
|
||||
|
||||
public LicenseInfo getLicenseInfo() {
|
||||
return licenseInfo;
|
||||
}
|
||||
|
||||
public String getLicenseCode() {
|
||||
return licenseCode;
|
||||
}
|
||||
|
||||
public void setLicenseCode(String licenseCode) {
|
||||
this.licenseCode = StringUtils.trimToNull(licenseCode);
|
||||
}
|
||||
|
||||
public void setLicenseInfo(LicenseInfo licenseInfo) {
|
||||
this.licenseInfo = licenseInfo;
|
||||
}
|
||||
|
||||
public boolean isForceChange() {
|
||||
return forceChange;
|
||||
}
|
||||
|
||||
public void setForceChange(boolean forceChange) {
|
||||
this.forceChange = forceChange;
|
||||
}
|
||||
|
||||
public boolean isSubmissionError() {
|
||||
return submissionError;
|
||||
}
|
||||
|
||||
public void setSubmissionError(boolean submissionError) {
|
||||
this.submissionError = submissionError;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
import org.libresonic.player.domain.*;
|
||||
import org.libresonic.player.controller.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Command used in {@link SearchController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SearchCommand {
|
||||
|
||||
private String query;
|
||||
private List<MediaFile> artists;
|
||||
private List<MediaFile> albums;
|
||||
private List<MediaFile> songs;
|
||||
private boolean isIndexBeingCreated;
|
||||
private User user;
|
||||
private boolean partyModeEnabled;
|
||||
private Player player;
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void setQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public boolean isIndexBeingCreated() {
|
||||
return isIndexBeingCreated;
|
||||
}
|
||||
|
||||
public void setIndexBeingCreated(boolean indexBeingCreated) {
|
||||
isIndexBeingCreated = indexBeingCreated;
|
||||
}
|
||||
|
||||
public List<MediaFile> getArtists() {
|
||||
return artists;
|
||||
}
|
||||
|
||||
public void setArtists(List<MediaFile> artists) {
|
||||
this.artists = artists;
|
||||
}
|
||||
|
||||
public List<MediaFile> getAlbums() {
|
||||
return albums;
|
||||
}
|
||||
|
||||
public void setAlbums(List<MediaFile> albums) {
|
||||
this.albums = albums;
|
||||
}
|
||||
|
||||
public List<MediaFile> getSongs() {
|
||||
return songs;
|
||||
}
|
||||
|
||||
public void setSongs(List<MediaFile> songs) {
|
||||
this.songs = songs;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public boolean isPartyModeEnabled() {
|
||||
return partyModeEnabled;
|
||||
}
|
||||
|
||||
public void setPartyModeEnabled(boolean partyModeEnabled) {
|
||||
this.partyModeEnabled = partyModeEnabled;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public static class Match {
|
||||
private MediaFile mediaFile;
|
||||
private String title;
|
||||
private String album;
|
||||
private String artist;
|
||||
|
||||
public Match(MediaFile mediaFile, String title, String album, String artist) {
|
||||
this.mediaFile = mediaFile;
|
||||
this.title = title;
|
||||
this.album = album;
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public MediaFile getMediaFile() {
|
||||
return mediaFile;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.command;
|
||||
|
||||
import org.libresonic.player.controller.UserSettingsController;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.TranscodeScheme;
|
||||
import org.libresonic.player.domain.User;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command used in {@link UserSettingsController}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class UserSettingsCommand {
|
||||
private String username;
|
||||
private boolean isAdminRole;
|
||||
private boolean isDownloadRole;
|
||||
private boolean isUploadRole;
|
||||
private boolean isCoverArtRole;
|
||||
private boolean isCommentRole;
|
||||
private boolean isPodcastRole;
|
||||
private boolean isStreamRole;
|
||||
private boolean isJukeboxRole;
|
||||
private boolean isSettingsRole;
|
||||
private boolean isShareRole;
|
||||
|
||||
private List<User> users;
|
||||
private boolean isAdmin;
|
||||
private boolean isPasswordChange;
|
||||
private boolean isNewUser;
|
||||
private boolean isDeleteUser;
|
||||
private String password;
|
||||
private String confirmPassword;
|
||||
private String email;
|
||||
private boolean isLdapAuthenticated;
|
||||
private boolean isLdapEnabled;
|
||||
private List<MusicFolder> allMusicFolders;
|
||||
private int[] allowedMusicFolderIds;
|
||||
|
||||
private String transcodeSchemeName;
|
||||
private EnumHolder[] transcodeSchemeHolders;
|
||||
private boolean transcodingSupported;
|
||||
private String transcodeDirectory;
|
||||
private boolean toast;
|
||||
private boolean reload;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public boolean isAdminRole() {
|
||||
return isAdminRole;
|
||||
}
|
||||
|
||||
public void setAdminRole(boolean adminRole) {
|
||||
isAdminRole = adminRole;
|
||||
}
|
||||
|
||||
public boolean isDownloadRole() {
|
||||
return isDownloadRole;
|
||||
}
|
||||
|
||||
public void setDownloadRole(boolean downloadRole) {
|
||||
isDownloadRole = downloadRole;
|
||||
}
|
||||
|
||||
public boolean isUploadRole() {
|
||||
return isUploadRole;
|
||||
}
|
||||
|
||||
public void setUploadRole(boolean uploadRole) {
|
||||
isUploadRole = uploadRole;
|
||||
}
|
||||
|
||||
public boolean isCoverArtRole() {
|
||||
return isCoverArtRole;
|
||||
}
|
||||
|
||||
public void setCoverArtRole(boolean coverArtRole) {
|
||||
isCoverArtRole = coverArtRole;
|
||||
}
|
||||
|
||||
public boolean isCommentRole() {
|
||||
return isCommentRole;
|
||||
}
|
||||
|
||||
public void setCommentRole(boolean commentRole) {
|
||||
isCommentRole = commentRole;
|
||||
}
|
||||
|
||||
public boolean isPodcastRole() {
|
||||
return isPodcastRole;
|
||||
}
|
||||
|
||||
public void setPodcastRole(boolean podcastRole) {
|
||||
isPodcastRole = podcastRole;
|
||||
}
|
||||
|
||||
public boolean isStreamRole() {
|
||||
return isStreamRole;
|
||||
}
|
||||
|
||||
public void setStreamRole(boolean streamRole) {
|
||||
isStreamRole = streamRole;
|
||||
}
|
||||
|
||||
public boolean isJukeboxRole() {
|
||||
return isJukeboxRole;
|
||||
}
|
||||
|
||||
public void setJukeboxRole(boolean jukeboxRole) {
|
||||
isJukeboxRole = jukeboxRole;
|
||||
}
|
||||
|
||||
public boolean isSettingsRole() {
|
||||
return isSettingsRole;
|
||||
}
|
||||
|
||||
public void setSettingsRole(boolean settingsRole) {
|
||||
isSettingsRole = settingsRole;
|
||||
}
|
||||
|
||||
public boolean isShareRole() {
|
||||
return isShareRole;
|
||||
}
|
||||
|
||||
public void setShareRole(boolean shareRole) {
|
||||
isShareRole = shareRole;
|
||||
}
|
||||
|
||||
public List<User> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public void setUsers(List<User> users) {
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
public boolean isAdmin() {
|
||||
return isAdmin;
|
||||
}
|
||||
|
||||
public void setAdmin(boolean admin) {
|
||||
isAdmin = admin;
|
||||
}
|
||||
|
||||
public boolean isPasswordChange() {
|
||||
return isPasswordChange;
|
||||
}
|
||||
|
||||
public void setPasswordChange(boolean passwordChange) {
|
||||
isPasswordChange = passwordChange;
|
||||
}
|
||||
|
||||
public boolean isNewUser() {
|
||||
return isNewUser;
|
||||
}
|
||||
|
||||
public void setNewUser(boolean isNewUser) {
|
||||
this.isNewUser = isNewUser;
|
||||
}
|
||||
|
||||
public boolean isDeleteUser() {
|
||||
return isDeleteUser;
|
||||
}
|
||||
|
||||
public void setDeleteUser(boolean deleteUser) {
|
||||
this.isDeleteUser = deleteUser;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getConfirmPassword() {
|
||||
return confirmPassword;
|
||||
}
|
||||
|
||||
public void setConfirmPassword(String confirmPassword) {
|
||||
this.confirmPassword = confirmPassword;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public boolean isLdapAuthenticated() {
|
||||
return isLdapAuthenticated;
|
||||
}
|
||||
|
||||
public void setLdapAuthenticated(boolean ldapAuthenticated) {
|
||||
isLdapAuthenticated = ldapAuthenticated;
|
||||
}
|
||||
|
||||
public boolean isLdapEnabled() {
|
||||
return isLdapEnabled;
|
||||
}
|
||||
|
||||
public void setLdapEnabled(boolean ldapEnabled) {
|
||||
isLdapEnabled = ldapEnabled;
|
||||
}
|
||||
|
||||
public List<MusicFolder> getAllMusicFolders() {
|
||||
return allMusicFolders;
|
||||
}
|
||||
|
||||
public void setAllMusicFolders(List<MusicFolder> allMusicFolders) {
|
||||
this.allMusicFolders = allMusicFolders;
|
||||
}
|
||||
|
||||
public int[] getAllowedMusicFolderIds() {
|
||||
return allowedMusicFolderIds;
|
||||
}
|
||||
|
||||
public void setAllowedMusicFolderIds(int[] allowedMusicFolderIds) {
|
||||
this.allowedMusicFolderIds = allowedMusicFolderIds;
|
||||
}
|
||||
|
||||
public String getTranscodeSchemeName() {
|
||||
return transcodeSchemeName;
|
||||
}
|
||||
|
||||
public void setTranscodeSchemeName(String transcodeSchemeName) {
|
||||
this.transcodeSchemeName = transcodeSchemeName;
|
||||
}
|
||||
|
||||
public EnumHolder[] getTranscodeSchemeHolders() {
|
||||
return transcodeSchemeHolders;
|
||||
}
|
||||
|
||||
public void setTranscodeSchemes(TranscodeScheme[] transcodeSchemes) {
|
||||
transcodeSchemeHolders = new EnumHolder[transcodeSchemes.length];
|
||||
for (int i = 0; i < transcodeSchemes.length; i++) {
|
||||
TranscodeScheme scheme = transcodeSchemes[i];
|
||||
transcodeSchemeHolders[i] = new EnumHolder(scheme.name(), scheme.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTranscodingSupported() {
|
||||
return transcodingSupported;
|
||||
}
|
||||
|
||||
public void setTranscodingSupported(boolean transcodingSupported) {
|
||||
this.transcodingSupported = transcodingSupported;
|
||||
}
|
||||
|
||||
public String getTranscodeDirectory() {
|
||||
return transcodeDirectory;
|
||||
}
|
||||
|
||||
public void setTranscodeDirectory(String transcodeDirectory) {
|
||||
this.transcodeDirectory = transcodeDirectory;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
username = user == null ? null : user.getUsername();
|
||||
isAdminRole = user != null && user.isAdminRole();
|
||||
isDownloadRole = user != null && user.isDownloadRole();
|
||||
isUploadRole = user != null && user.isUploadRole();
|
||||
isCoverArtRole = user != null && user.isCoverArtRole();
|
||||
isCommentRole = user != null && user.isCommentRole();
|
||||
isPodcastRole = user != null && user.isPodcastRole();
|
||||
isStreamRole = user != null && user.isStreamRole();
|
||||
isJukeboxRole = user != null && user.isJukeboxRole();
|
||||
isSettingsRole = user != null && user.isSettingsRole();
|
||||
isShareRole = user != null && user.isShareRole();
|
||||
isLdapAuthenticated = user != null && user.isLdapAuthenticated();
|
||||
}
|
||||
|
||||
public void setToast(boolean toast) {
|
||||
this.toast = toast;
|
||||
}
|
||||
|
||||
public boolean isToast() {
|
||||
return toast;
|
||||
}
|
||||
|
||||
public boolean isReload() {
|
||||
return reload;
|
||||
}
|
||||
|
||||
public void setReload(boolean reload) {
|
||||
this.reload = reload;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.springframework.web.servlet.support.*;
|
||||
import org.springframework.web.servlet.mvc.*;
|
||||
import org.springframework.ui.context.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Abstract super class for controllers which generate charts.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public abstract class AbstractChartController implements Controller {
|
||||
|
||||
/**
|
||||
* Returns the chart background color for the current theme.
|
||||
* @param request The servlet request.
|
||||
* @return The chart background color.
|
||||
*/
|
||||
protected Color getBackground(HttpServletRequest request) {
|
||||
return getColor("backgroundColor", request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chart foreground color for the current theme.
|
||||
* @param request The servlet request.
|
||||
* @return The chart foreground color.
|
||||
*/
|
||||
protected Color getForeground(HttpServletRequest request) {
|
||||
return getColor("textColor", request);
|
||||
}
|
||||
|
||||
private Color getColor(String code, HttpServletRequest request) {
|
||||
Theme theme = RequestContextUtils.getTheme(request);
|
||||
Locale locale = RequestContextUtils.getLocale(request);
|
||||
String colorHex = theme.getMessageSource().getMessage(code, new Object[0], locale);
|
||||
return new Color(Integer.parseInt(colorHex, 16));
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.libresonic.player.command.AdvancedSettingsCommand;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate advanced settings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class AdvancedSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
AdvancedSettingsCommand command = new AdvancedSettingsCommand();
|
||||
command.setDownloadLimit(String.valueOf(settingsService.getDownloadBitrateLimit()));
|
||||
command.setUploadLimit(String.valueOf(settingsService.getUploadBitrateLimit()));
|
||||
command.setLdapEnabled(settingsService.isLdapEnabled());
|
||||
command.setLdapUrl(settingsService.getLdapUrl());
|
||||
command.setLdapSearchFilter(settingsService.getLdapSearchFilter());
|
||||
command.setLdapManagerDn(settingsService.getLdapManagerDn());
|
||||
command.setLdapAutoShadowing(settingsService.isLdapAutoShadowing());
|
||||
command.setBrand(settingsService.getBrand());
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
AdvancedSettingsCommand command = (AdvancedSettingsCommand) comm;
|
||||
|
||||
command.setToast(true);
|
||||
command.setReloadNeeded(false);
|
||||
|
||||
try {
|
||||
settingsService.setDownloadBitrateLimit(Long.parseLong(command.getDownloadLimit()));
|
||||
} catch (NumberFormatException x) { /* Intentionally ignored. */ }
|
||||
try {
|
||||
settingsService.setUploadBitrateLimit(Long.parseLong(command.getUploadLimit()));
|
||||
} catch (NumberFormatException x) { /* Intentionally ignored. */ }
|
||||
|
||||
settingsService.setLdapEnabled(command.isLdapEnabled());
|
||||
settingsService.setLdapUrl(command.getLdapUrl());
|
||||
settingsService.setLdapSearchFilter(command.getLdapSearchFilter());
|
||||
settingsService.setLdapManagerDn(command.getLdapManagerDn());
|
||||
settingsService.setLdapAutoShadowing(command.isLdapAutoShadowing());
|
||||
|
||||
if (StringUtils.isNotEmpty(command.getLdapManagerPassword())) {
|
||||
settingsService.setLdapManagerPassword(command.getLdapManagerPassword());
|
||||
}
|
||||
|
||||
settingsService.save();
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.springframework.web.servlet.*;
|
||||
import org.springframework.web.servlet.mvc.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/**
|
||||
* Controller for the page which forwards to allmusic.com.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class AllmusicController extends ParameterizableViewController {
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("album", request.getParameter("album"));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2013 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AutoCoverDemo {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
JFrame frame = new JFrame();
|
||||
JPanel panel = new JPanel();
|
||||
panel.add(new AlbumComponent(110, 110));
|
||||
panel.add(new AlbumComponent(150, 150));
|
||||
panel.add(new AlbumComponent(200, 200));
|
||||
panel.add(new AlbumComponent(300, 300));
|
||||
panel.add(new AlbumComponent(400, 240));
|
||||
panel.add(new AlbumComponent(240, 400));
|
||||
|
||||
panel.setBackground(Color.LIGHT_GRAY);
|
||||
frame.add(panel);
|
||||
frame.setSize(1000, 800);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private static class AlbumComponent extends JComponent {
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public AlbumComponent(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
setPreferredSize(new Dimension(width, height));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
String key = RandomStringUtils.random(5);
|
||||
new CoverArtController.AutoCover((Graphics2D) g, key, "Artist with a very long name", "Album", width, height).paintCover();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
import org.springframework.web.servlet.mvc.LastModified;
|
||||
|
||||
import org.libresonic.player.domain.Avatar;
|
||||
import org.libresonic.player.domain.AvatarScheme;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller which produces avatar images.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class AvatarController implements Controller, LastModified {
|
||||
|
||||
private SettingsService settingsService;
|
||||
|
||||
public long getLastModified(HttpServletRequest request) {
|
||||
Avatar avatar = getAvatar(request);
|
||||
long result = avatar == null ? -1L : avatar.getCreatedDate().getTime();
|
||||
|
||||
String username = request.getParameter("username");
|
||||
if (username != null) {
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
result = Math.max(result, userSettings.getChanged().getTime());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Avatar avatar = getAvatar(request);
|
||||
|
||||
if (avatar == null) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return null;
|
||||
}
|
||||
|
||||
response.setContentType(avatar.getMimeType());
|
||||
response.getOutputStream().write(avatar.getData());
|
||||
return null;
|
||||
}
|
||||
|
||||
private Avatar getAvatar(HttpServletRequest request) {
|
||||
String id = request.getParameter("id");
|
||||
boolean forceCustom = ServletRequestUtils.getBooleanParameter(request, "forceCustom", false);
|
||||
|
||||
if (id != null) {
|
||||
return settingsService.getSystemAvatar(Integer.parseInt(id));
|
||||
}
|
||||
|
||||
String username = request.getParameter("username");
|
||||
if (username == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
if (userSettings.getAvatarScheme() == AvatarScheme.CUSTOM || forceCustom) {
|
||||
return settingsService.getCustomAvatar(username);
|
||||
}
|
||||
return settingsService.getSystemAvatar(userSettings.getSystemAvatarId());
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.domain.Avatar;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.FileItemFactory;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller which receives uploaded avatar images.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class AvatarUploadController extends ParameterizableViewController {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(AvatarUploadController.class);
|
||||
private static final int MAX_AVATAR_SIZE = 64;
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
|
||||
// Check that we have a file upload request.
|
||||
if (!ServletFileUpload.isMultipartContent(request)) {
|
||||
throw new Exception("Illegal request.");
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
FileItemFactory factory = new DiskFileItemFactory();
|
||||
ServletFileUpload upload = new ServletFileUpload(factory);
|
||||
List<?> items = upload.parseRequest(request);
|
||||
|
||||
// Look for file items.
|
||||
for (Object o : items) {
|
||||
FileItem item = (FileItem) o;
|
||||
|
||||
if (!item.isFormField()) {
|
||||
String fileName = item.getName();
|
||||
byte[] data = item.get();
|
||||
|
||||
if (StringUtils.isNotBlank(fileName) && data.length > 0) {
|
||||
createAvatar(fileName, data, username, map);
|
||||
} else {
|
||||
map.put("error", new Exception("Missing file."));
|
||||
LOG.warn("Failed to upload personal image. No file specified.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
map.put("username", username);
|
||||
map.put("avatar", settingsService.getCustomAvatar(username));
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void createAvatar(String fileName, byte[] data, String username, Map<String, Object> map) throws IOException {
|
||||
|
||||
BufferedImage image;
|
||||
try {
|
||||
image = ImageIO.read(new ByteArrayInputStream(data));
|
||||
if (image == null) {
|
||||
throw new Exception("Failed to decode incoming image: " + fileName + " (" + data.length + " bytes).");
|
||||
}
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
String mimeType = StringUtil.getMimeType(FilenameUtils.getExtension(fileName));
|
||||
|
||||
// Scale down image if necessary.
|
||||
if (width > MAX_AVATAR_SIZE || height > MAX_AVATAR_SIZE) {
|
||||
double scaleFactor = (double) MAX_AVATAR_SIZE / (double) Math.max(width, height);
|
||||
height = (int) (height * scaleFactor);
|
||||
width = (int) (width * scaleFactor);
|
||||
image = CoverArtController.scale(image, width, height);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "jpeg", out);
|
||||
data = out.toByteArray();
|
||||
mimeType = StringUtil.getMimeType("jpeg");
|
||||
map.put("resized", true);
|
||||
}
|
||||
Avatar avatar = new Avatar(0, fileName, new Date(), mimeType, width, height, data);
|
||||
settingsService.setCustomAvatar(avatar, username);
|
||||
LOG.info("Created avatar '" + fileName + "' (" + data.length + " bytes) for user " + username);
|
||||
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to upload personal image: " + x, x);
|
||||
map.put("error", x);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
|
||||
/**
|
||||
* Controller for changing cover art.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ChangeCoverArtController extends ParameterizableViewController {
|
||||
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
String artist = request.getParameter("artist");
|
||||
String album = request.getParameter("album");
|
||||
MediaFile dir = mediaFileService.getMediaFile(id);
|
||||
|
||||
if (artist == null) {
|
||||
artist = dir.getArtist();
|
||||
}
|
||||
if (album == null) {
|
||||
album = dir.getAlbumName();
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("id", id);
|
||||
map.put("artist", artist);
|
||||
map.put("album", album);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+731
@@ -0,0 +1,731 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.GradientPaint;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
import org.springframework.web.servlet.mvc.LastModified;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.dao.AlbumDao;
|
||||
import org.libresonic.player.dao.ArtistDao;
|
||||
import org.libresonic.player.domain.Album;
|
||||
import org.libresonic.player.domain.Artist;
|
||||
import org.libresonic.player.domain.CoverArtScheme;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.Playlist;
|
||||
import org.libresonic.player.domain.PodcastChannel;
|
||||
import org.libresonic.player.domain.Transcoding;
|
||||
import org.libresonic.player.domain.VideoTranscodingSettings;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.PlaylistService;
|
||||
import org.libresonic.player.service.PodcastService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.service.TranscodingService;
|
||||
import org.libresonic.player.service.metadata.JaudiotaggerParser;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller which produces cover art images.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class CoverArtController implements Controller, LastModified {
|
||||
|
||||
public static final String ALBUM_COVERART_PREFIX = "al-";
|
||||
public static final String ARTIST_COVERART_PREFIX = "ar-";
|
||||
public static final String PLAYLIST_COVERART_PREFIX = "pl-";
|
||||
public static final String PODCAST_COVERART_PREFIX = "pod-";
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(CoverArtController.class);
|
||||
|
||||
private MediaFileService mediaFileService;
|
||||
private TranscodingService transcodingService;
|
||||
private SettingsService settingsService;
|
||||
private PlaylistService playlistService;
|
||||
private PodcastService podcastService;
|
||||
private ArtistDao artistDao;
|
||||
private AlbumDao albumDao;
|
||||
private Semaphore semaphore;
|
||||
|
||||
public void init() {
|
||||
semaphore = new Semaphore(settingsService.getCoverArtConcurrency());
|
||||
}
|
||||
|
||||
public long getLastModified(HttpServletRequest request) {
|
||||
CoverArtRequest coverArtRequest = createCoverArtRequest(request);
|
||||
long result = coverArtRequest.lastModified();
|
||||
// LOG.info("getLastModified - " + coverArtRequest + ": " + new Date(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
CoverArtRequest coverArtRequest = createCoverArtRequest(request);
|
||||
// LOG.info("handleRequest - " + coverArtRequest);
|
||||
Integer size = ServletRequestUtils.getIntParameter(request, "size");
|
||||
|
||||
// Send fallback image if no ID is given. (No need to cache it, since it will be cached in browser.)
|
||||
if (coverArtRequest == null) {
|
||||
sendFallback(size, response);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Optimize if no scaling is required.
|
||||
if (size == null && coverArtRequest.getCoverArt() != null) {
|
||||
// LOG.info("sendUnscaled - " + coverArtRequest);
|
||||
sendUnscaled(coverArtRequest, response);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Send cached image, creating it if necessary.
|
||||
if (size == null) {
|
||||
size = CoverArtScheme.LARGE.getSize() * 2;
|
||||
}
|
||||
try {
|
||||
File cachedImage = getCachedImage(coverArtRequest, size);
|
||||
sendImage(cachedImage, response);
|
||||
} catch (IOException e) {
|
||||
sendFallback(size, response);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private CoverArtRequest createCoverArtRequest(HttpServletRequest request) {
|
||||
String id = request.getParameter("id");
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id.startsWith(ALBUM_COVERART_PREFIX)) {
|
||||
return createAlbumCoverArtRequest(Integer.valueOf(id.replace(ALBUM_COVERART_PREFIX, "")));
|
||||
}
|
||||
if (id.startsWith(ARTIST_COVERART_PREFIX)) {
|
||||
return createArtistCoverArtRequest(Integer.valueOf(id.replace(ARTIST_COVERART_PREFIX, "")));
|
||||
}
|
||||
if (id.startsWith(PLAYLIST_COVERART_PREFIX)) {
|
||||
return createPlaylistCoverArtRequest(Integer.valueOf(id.replace(PLAYLIST_COVERART_PREFIX, "")));
|
||||
}
|
||||
if (id.startsWith(PODCAST_COVERART_PREFIX)) {
|
||||
return createPodcastCoverArtRequest(Integer.valueOf(id.replace(PODCAST_COVERART_PREFIX, "")), request);
|
||||
}
|
||||
return createMediaFileCoverArtRequest(Integer.valueOf(id), request);
|
||||
}
|
||||
|
||||
private CoverArtRequest createAlbumCoverArtRequest(int id) {
|
||||
Album album = albumDao.getAlbum(id);
|
||||
return album == null ? null : new AlbumCoverArtRequest(album);
|
||||
}
|
||||
|
||||
private CoverArtRequest createArtistCoverArtRequest(int id) {
|
||||
Artist artist = artistDao.getArtist(id);
|
||||
return artist == null ? null : new ArtistCoverArtRequest(artist);
|
||||
}
|
||||
|
||||
private PlaylistCoverArtRequest createPlaylistCoverArtRequest(int id) {
|
||||
Playlist playlist = playlistService.getPlaylist(id);
|
||||
return playlist == null ? null : new PlaylistCoverArtRequest(playlist);
|
||||
}
|
||||
|
||||
private CoverArtRequest createPodcastCoverArtRequest(int id, HttpServletRequest request) {
|
||||
PodcastChannel channel = podcastService.getChannel(id);
|
||||
if (channel == null) {
|
||||
return null;
|
||||
}
|
||||
if (channel.getMediaFileId() == null) {
|
||||
return new PodcastCoverArtRequest(channel);
|
||||
}
|
||||
return createMediaFileCoverArtRequest(channel.getMediaFileId(), request);
|
||||
}
|
||||
|
||||
private CoverArtRequest createMediaFileCoverArtRequest(int id, HttpServletRequest request) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(id);
|
||||
if (mediaFile == null) {
|
||||
return null;
|
||||
}
|
||||
if (mediaFile.isVideo()) {
|
||||
int offset = ServletRequestUtils.getIntParameter(request, "offset", 60);
|
||||
return new VideoCoverArtRequest(mediaFile, offset);
|
||||
}
|
||||
return new MediaFileCoverArtRequest(mediaFile);
|
||||
}
|
||||
|
||||
private void sendImage(File file, HttpServletResponse response) throws IOException {
|
||||
response.setContentType(StringUtil.getMimeType(FilenameUtils.getExtension(file.getName())));
|
||||
InputStream in = new FileInputStream(file);
|
||||
try {
|
||||
IOUtils.copy(in, response.getOutputStream());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendFallback(Integer size, HttpServletResponse response) throws IOException {
|
||||
if (response.getContentType() == null) {
|
||||
response.setContentType(StringUtil.getMimeType("jpeg"));
|
||||
}
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getClass().getResourceAsStream("default_cover.jpg");
|
||||
BufferedImage image = ImageIO.read(in);
|
||||
if (size != null) {
|
||||
image = scale(image, size, size);
|
||||
}
|
||||
ImageIO.write(image, "jpeg", response.getOutputStream());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendUnscaled(CoverArtRequest coverArtRequest, HttpServletResponse response) throws IOException {
|
||||
File file = coverArtRequest.getCoverArt();
|
||||
JaudiotaggerParser parser = new JaudiotaggerParser();
|
||||
if (!parser.isApplicable(file)) {
|
||||
response.setContentType(StringUtil.getMimeType(FilenameUtils.getExtension(file.getName())));
|
||||
}
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getImageInputStream(file);
|
||||
IOUtils.copy(in, response.getOutputStream());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
private File getCachedImage(CoverArtRequest request, int size) throws IOException {
|
||||
String hash = DigestUtils.md5Hex(request.getKey());
|
||||
String encoding = request.getCoverArt() != null ? "jpeg" : "png";
|
||||
File cachedImage = new File(getImageCacheDirectory(size), hash + "." + encoding);
|
||||
|
||||
// Synchronize to avoid concurrent writing to the same file.
|
||||
synchronized (hash.intern()) {
|
||||
|
||||
// Is cache missing or obsolete?
|
||||
if (!cachedImage.exists() || request.lastModified() > cachedImage.lastModified()) {
|
||||
// LOG.info("Cache MISS - " + request + " (" + size + ")");
|
||||
OutputStream out = null;
|
||||
try {
|
||||
semaphore.acquire();
|
||||
BufferedImage image = request.createImage(size);
|
||||
if (image == null) {
|
||||
throw new Exception("Unable to decode image.");
|
||||
}
|
||||
out = new FileOutputStream(cachedImage);
|
||||
ImageIO.write(image, encoding, out);
|
||||
|
||||
} catch (Throwable x) {
|
||||
// Delete corrupt (probably empty) thumbnail cache.
|
||||
LOG.warn("Failed to create thumbnail for " + request, x);
|
||||
IOUtils.closeQuietly(out);
|
||||
cachedImage.delete();
|
||||
throw new IOException("Failed to create thumbnail for " + request + ". " + x.getMessage());
|
||||
|
||||
} finally {
|
||||
semaphore.release();
|
||||
IOUtils.closeQuietly(out);
|
||||
}
|
||||
} else {
|
||||
// LOG.info("Cache HIT - " + request + " (" + size + ")");
|
||||
}
|
||||
return cachedImage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input stream to the image in the given file. If the file is an audio file,
|
||||
* the embedded album art is returned.
|
||||
*/
|
||||
private InputStream getImageInputStream(File file) throws IOException {
|
||||
JaudiotaggerParser parser = new JaudiotaggerParser();
|
||||
if (parser.isApplicable(file)) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(file);
|
||||
return new ByteArrayInputStream(parser.getImageData(mediaFile));
|
||||
} else {
|
||||
return new FileInputStream(file);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream getImageInputStreamForVideo(MediaFile mediaFile, int width, int height, int offset) throws Exception {
|
||||
VideoTranscodingSettings videoSettings = new VideoTranscodingSettings(width, height, offset, 0, false);
|
||||
TranscodingService.Parameters parameters = new TranscodingService.Parameters(mediaFile, videoSettings);
|
||||
String command = settingsService.getVideoImageCommand();
|
||||
parameters.setTranscoding(new Transcoding(null, null, null, null, command, null, null, false));
|
||||
return transcodingService.getTranscodedInputStream(parameters);
|
||||
}
|
||||
|
||||
private synchronized File getImageCacheDirectory(int size) {
|
||||
File dir = new File(SettingsService.getLibresonicHome(), "thumbs");
|
||||
dir = new File(dir, String.valueOf(size));
|
||||
if (!dir.exists()) {
|
||||
if (dir.mkdirs()) {
|
||||
LOG.info("Created thumbnail cache " + dir);
|
||||
} else {
|
||||
LOG.error("Failed to create thumbnail cache " + dir);
|
||||
}
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
public static BufferedImage scale(BufferedImage image, int width, int height) {
|
||||
int w = image.getWidth();
|
||||
int h = image.getHeight();
|
||||
BufferedImage thumb = image;
|
||||
|
||||
// For optimal results, use step by step bilinear resampling - halfing the size at each step.
|
||||
do {
|
||||
w /= 2;
|
||||
h /= 2;
|
||||
if (w < width) {
|
||||
w = width;
|
||||
}
|
||||
if (h < height) {
|
||||
h = height;
|
||||
}
|
||||
|
||||
BufferedImage temp = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2 = temp.createGraphics();
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
|
||||
g2.dispose();
|
||||
|
||||
thumb = temp;
|
||||
} while (w != width);
|
||||
|
||||
return thumb;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setArtistDao(ArtistDao artistDao) {
|
||||
this.artistDao = artistDao;
|
||||
}
|
||||
|
||||
public void setAlbumDao(AlbumDao albumDao) {
|
||||
this.albumDao = albumDao;
|
||||
}
|
||||
|
||||
public void setTranscodingService(TranscodingService transcodingService) {
|
||||
this.transcodingService = transcodingService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
|
||||
private abstract class CoverArtRequest {
|
||||
|
||||
protected File coverArt;
|
||||
|
||||
private CoverArtRequest() {
|
||||
}
|
||||
|
||||
private CoverArtRequest(String coverArtPath) {
|
||||
this.coverArt = coverArtPath == null ? null : new File(coverArtPath);
|
||||
}
|
||||
|
||||
private File getCoverArt() {
|
||||
return coverArt;
|
||||
}
|
||||
|
||||
public abstract String getKey();
|
||||
|
||||
public abstract long lastModified();
|
||||
|
||||
public BufferedImage createImage(int size) {
|
||||
if (coverArt != null) {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getImageInputStream(coverArt);
|
||||
return scale(ImageIO.read(in), size, size);
|
||||
} catch (Throwable x) {
|
||||
LOG.warn("Failed to process cover art " + coverArt + ": " + x, x);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
return createAutoCover(size, size);
|
||||
}
|
||||
|
||||
protected BufferedImage createAutoCover(int width, int height) {
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
AutoCover autoCover = new AutoCover(graphics, getKey(), getArtist(), getAlbum(), width, height);
|
||||
autoCover.paintCover();
|
||||
graphics.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
public abstract String getAlbum();
|
||||
|
||||
public abstract String getArtist();
|
||||
}
|
||||
|
||||
private class ArtistCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final Artist artist;
|
||||
|
||||
private ArtistCoverArtRequest(Artist artist) {
|
||||
super(artist.getCoverArtPath());
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return artist.getCoverArtPath() != null ? artist.getCoverArtPath() : (ARTIST_COVERART_PREFIX + artist.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return coverArt != null ? coverArt.lastModified() : artist.getLastScanned().getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return artist.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Artist " + artist.getId() + " - " + artist.getName();
|
||||
}
|
||||
}
|
||||
|
||||
private class AlbumCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final Album album;
|
||||
|
||||
private AlbumCoverArtRequest(Album album) {
|
||||
super(album.getCoverArtPath());
|
||||
this.album = album;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return album.getCoverArtPath() != null ? album.getCoverArtPath() : (ALBUM_COVERART_PREFIX + album.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return coverArt != null ? coverArt.lastModified() : album.getLastScanned().getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return album.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return album.getArtist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Album " + album.getId() + " - " + album.getName();
|
||||
}
|
||||
}
|
||||
|
||||
private class PlaylistCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final Playlist playlist;
|
||||
|
||||
private PlaylistCoverArtRequest(Playlist playlist) {
|
||||
super(null);
|
||||
this.playlist = playlist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return PLAYLIST_COVERART_PREFIX + playlist.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return playlist.getChanged().getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return playlist.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Playlist " + playlist.getId() + " - " + playlist.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage createImage(int size) {
|
||||
List<MediaFile> albums = getRepresentativeAlbums();
|
||||
if (albums.isEmpty()) {
|
||||
return createAutoCover(size, size);
|
||||
}
|
||||
if (albums.size() < 4) {
|
||||
return new MediaFileCoverArtRequest(albums.get(0)).createImage(size);
|
||||
}
|
||||
|
||||
BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
|
||||
int half = size / 2;
|
||||
graphics.drawImage(new MediaFileCoverArtRequest(albums.get(0)).createImage(half), null, 0, 0);
|
||||
graphics.drawImage(new MediaFileCoverArtRequest(albums.get(1)).createImage(half), null, half, 0);
|
||||
graphics.drawImage(new MediaFileCoverArtRequest(albums.get(2)).createImage(half), null, 0, half);
|
||||
graphics.drawImage(new MediaFileCoverArtRequest(albums.get(3)).createImage(half), null, half, half);
|
||||
graphics.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
private List<MediaFile> getRepresentativeAlbums() {
|
||||
Set<MediaFile> albums = new LinkedHashSet<MediaFile>();
|
||||
for (MediaFile song : playlistService.getFilesInPlaylist(playlist.getId())) {
|
||||
MediaFile album = mediaFileService.getParentOf(song);
|
||||
if (album != null && !mediaFileService.isRoot(album)) {
|
||||
albums.add(album);
|
||||
}
|
||||
}
|
||||
return new ArrayList<MediaFile>(albums);
|
||||
}
|
||||
}
|
||||
|
||||
private class PodcastCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final PodcastChannel channel;
|
||||
|
||||
public PodcastCoverArtRequest(PodcastChannel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return PODCAST_COVERART_PREFIX + channel.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return channel.getTitle() != null ? channel.getTitle() : channel.getUrl();
|
||||
}
|
||||
}
|
||||
|
||||
private class MediaFileCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final MediaFile mediaFile;
|
||||
private final MediaFile dir;
|
||||
|
||||
private MediaFileCoverArtRequest(MediaFile mediaFile) {
|
||||
this.mediaFile = mediaFile;
|
||||
dir = mediaFile.isDirectory() ? mediaFile : mediaFileService.getParentOf(mediaFile);
|
||||
coverArt = mediaFileService.getCoverArt(mediaFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return coverArt != null ? coverArt.getPath() : dir.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return coverArt != null ? coverArt.lastModified() : dir.getChanged().getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return dir.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return dir.getAlbumArtist() != null ? dir.getAlbumArtist() : dir.getArtist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Media file " + mediaFile.getId() + " - " + mediaFile;
|
||||
}
|
||||
}
|
||||
|
||||
private class VideoCoverArtRequest extends CoverArtRequest {
|
||||
|
||||
private final MediaFile mediaFile;
|
||||
private final int offset;
|
||||
|
||||
private VideoCoverArtRequest(MediaFile mediaFile, int offset) {
|
||||
this.mediaFile = mediaFile;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage createImage(int size) {
|
||||
int height = size;
|
||||
int width = height * 16 / 9;
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getImageInputStreamForVideo(mediaFile, width, height, offset);
|
||||
BufferedImage result = ImageIO.read(in);
|
||||
if (result == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return result;
|
||||
} catch (Throwable x) {
|
||||
LOG.warn("Failed to process cover art for " + mediaFile + ": " + x, x);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
return createAutoCover(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return mediaFile.getPath() + "/" + offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return mediaFile.getChanged().getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlbum() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
return mediaFile.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Video file " + mediaFile.getId() + " - " + mediaFile;
|
||||
}
|
||||
}
|
||||
|
||||
static class AutoCover {
|
||||
|
||||
private final static int[] COLORS = {0x33B5E5, 0xAA66CC, 0x99CC00, 0xFFBB33, 0xFF4444};
|
||||
private final Graphics2D graphics;
|
||||
private final String artist;
|
||||
private final String album;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final Color color;
|
||||
|
||||
public AutoCover(Graphics2D graphics, String key, String artist, String album, int width, int height) {
|
||||
this.graphics = graphics;
|
||||
this.artist = artist;
|
||||
this.album = album;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
int hash = key.hashCode();
|
||||
int rgb = COLORS[Math.abs(hash) % COLORS.length];
|
||||
this.color = new Color(rgb);
|
||||
}
|
||||
|
||||
public void paintCover() {
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
|
||||
graphics.setPaint(color);
|
||||
graphics.fillRect(0, 0, width, height);
|
||||
|
||||
int y = height * 2 / 3;
|
||||
graphics.setPaint(new GradientPaint(0, y, new Color(82, 82, 82), 0, height, Color.BLACK));
|
||||
graphics.fillRect(0, y, width, height / 3);
|
||||
|
||||
graphics.setPaint(Color.WHITE);
|
||||
float fontSize = 3.0f + height * 0.07f;
|
||||
Font font = new Font(Font.SANS_SERIF, Font.BOLD, (int) fontSize);
|
||||
graphics.setFont(font);
|
||||
|
||||
if (album != null) {
|
||||
graphics.drawString(album, width * 0.05f, height * 0.6f);
|
||||
}
|
||||
if (artist != null) {
|
||||
graphics.drawString(artist, width * 0.05f, height * 0.8f);
|
||||
}
|
||||
|
||||
int borderWidth = height / 50;
|
||||
graphics.fillRect(0, 0, borderWidth, height);
|
||||
graphics.fillRect(width - borderWidth, 0, height - borderWidth, height);
|
||||
graphics.fillRect(0, 0, width, borderWidth);
|
||||
graphics.fillRect(0, height - borderWidth, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.libresonic.player.dao.DaoHelper;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.ColumnMapRowMapper;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for the DB admin page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class DBController extends ParameterizableViewController {
|
||||
|
||||
private DaoHelper daoHelper;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
String query = request.getParameter("query");
|
||||
if (query != null) {
|
||||
map.put("query", query);
|
||||
|
||||
try {
|
||||
List<?> result = daoHelper.getJdbcTemplate().query(query, new ColumnMapRowMapper());
|
||||
map.put("result", result);
|
||||
} catch (DataAccessException x) {
|
||||
map.put("error", ExceptionUtils.getRootCause(x).getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setDaoHelper(DaoHelper daoHelper) {
|
||||
this.daoHelper = daoHelper;
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of Libresonic.
|
||||
*
|
||||
* Libresonic 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.
|
||||
*
|
||||
* Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2013 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.service.UPnPService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate the UPnP/DLNA server settings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class DLNASettingsController extends ParameterizableViewController {
|
||||
|
||||
private UPnPService upnpService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
if (isFormSubmission(request)) {
|
||||
handleParameters(request);
|
||||
map.put("toast", true);
|
||||
}
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
map.put("dlnaEnabled", settingsService.isDlnaEnabled());
|
||||
map.put("dlnaServerName", settingsService.getDlnaServerName());
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given request represents a form submission.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return if the request represents a form submission
|
||||
*/
|
||||
private boolean isFormSubmission(HttpServletRequest request) {
|
||||
return "POST".equals(request.getMethod());
|
||||
}
|
||||
|
||||
private void handleParameters(HttpServletRequest request) {
|
||||
boolean dlnaEnabled = ServletRequestUtils.getBooleanParameter(request, "dlnaEnabled", false);
|
||||
String dlnaServerName = StringUtils.trimToNull(request.getParameter("dlnaServerName"));
|
||||
if (dlnaServerName == null) {
|
||||
dlnaServerName = "Libresonic";
|
||||
}
|
||||
|
||||
upnpService.setMediaServerEnabled(false);
|
||||
settingsService.setDlnaEnabled(dlnaEnabled);
|
||||
settingsService.setDlnaServerName(dlnaServerName);
|
||||
settingsService.save();
|
||||
upnpService.setMediaServerEnabled(dlnaEnabled);
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setUpnpService(UPnPService upnpService) {
|
||||
this.upnpService = upnpService;
|
||||
}
|
||||
}
|
||||
+414
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
import org.springframework.web.servlet.mvc.LastModified;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.PlayQueue;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.Playlist;
|
||||
import org.libresonic.player.domain.TransferStatus;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.io.RangeOutputStream;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.PlaylistService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.service.StatusService;
|
||||
import org.libresonic.player.util.FileUtil;
|
||||
import org.libresonic.player.util.HttpRange;
|
||||
import org.libresonic.player.util.Util;
|
||||
|
||||
/**
|
||||
* A controller used for downloading files to a remote client. If the requested path refers to a file, the
|
||||
* given file is downloaded. If the requested path refers to a directory, the entire directory (including
|
||||
* sub-directories) are downloaded as an uncompressed zip-file.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class DownloadController implements Controller, LastModified {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(DownloadController.class);
|
||||
|
||||
private PlayerService playerService;
|
||||
private StatusService statusService;
|
||||
private SecurityService securityService;
|
||||
private PlaylistService playlistService;
|
||||
private SettingsService settingsService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
public long getLastModified(HttpServletRequest request) {
|
||||
try {
|
||||
MediaFile mediaFile = getMediaFile(request);
|
||||
if (mediaFile == null || mediaFile.isDirectory() || mediaFile.getChanged() == null) {
|
||||
return -1;
|
||||
}
|
||||
return mediaFile.getChanged().getTime();
|
||||
} catch (ServletRequestBindingException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
TransferStatus status = null;
|
||||
try {
|
||||
|
||||
status = statusService.createDownloadStatus(playerService.getPlayer(request, response, false, false));
|
||||
|
||||
MediaFile mediaFile = getMediaFile(request);
|
||||
|
||||
Integer playlistId = ServletRequestUtils.getIntParameter(request, "playlist");
|
||||
String playerId = request.getParameter("player");
|
||||
int[] indexes = request.getParameter("i") == null ? null : ServletRequestUtils.getIntParameters(request, "i");
|
||||
|
||||
if (mediaFile != null) {
|
||||
response.setIntHeader("ETag", mediaFile.getId());
|
||||
response.setHeader("Accept-Ranges", "bytes");
|
||||
}
|
||||
|
||||
HttpRange range = HttpRange.valueOf(request.getHeader("Range"));
|
||||
if (range != null) {
|
||||
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
||||
LOG.info("Got HTTP range: " + range);
|
||||
}
|
||||
|
||||
if (mediaFile != null) {
|
||||
if (!securityService.isFolderAccessAllowed(mediaFile, user.getUsername())) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||
"Access to file " + mediaFile.getId() + " is forbidden for user " + user.getUsername());
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mediaFile.isFile()) {
|
||||
downloadFile(response, status, mediaFile.getFile(), range);
|
||||
} else {
|
||||
List<MediaFile> children = mediaFileService.getChildrenOf(mediaFile, true, false, true);
|
||||
String zipFileName = FilenameUtils.getBaseName(mediaFile.getPath()) + ".zip";
|
||||
File coverArtFile = indexes == null ? mediaFile.getCoverArtFile() : null;
|
||||
downloadFiles(response, status, children, indexes, coverArtFile, range, zipFileName);
|
||||
}
|
||||
|
||||
} else if (playlistId != null) {
|
||||
List<MediaFile> songs = playlistService.getFilesInPlaylist(playlistId);
|
||||
Playlist playlist = playlistService.getPlaylist(playlistId);
|
||||
downloadFiles(response, status, songs, null, null, range, playlist.getName() + ".zip");
|
||||
|
||||
} else if (playerId != null) {
|
||||
Player player = playerService.getPlayerById(playerId);
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
playQueue.setName("Playlist");
|
||||
downloadFiles(response, status, playQueue.getFiles(), indexes, null, range, "download.zip");
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (status != null) {
|
||||
statusService.removeDownloadStatus(status);
|
||||
securityService.updateUserByteCounts(user, 0L, status.getBytesTransfered(), 0L);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private MediaFile getMediaFile(HttpServletRequest request) throws ServletRequestBindingException {
|
||||
Integer id = ServletRequestUtils.getIntParameter(request, "id");
|
||||
return id == null ? null : mediaFileService.getMediaFile(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a single file.
|
||||
*
|
||||
*
|
||||
* @param response The HTTP response.
|
||||
* @param status The download status.
|
||||
* @param file The file to download.
|
||||
* @param range The byte range, may be <code>null</code>.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private void downloadFile(HttpServletResponse response, TransferStatus status, File file, HttpRange range) throws IOException {
|
||||
LOG.info("Starting to download '" + FileUtil.getShortPath(file) + "' to " + status.getPlayer());
|
||||
status.setFile(file);
|
||||
|
||||
response.setContentType("application/x-download");
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodeAsRFC5987(file.getName()));
|
||||
if (range == null) {
|
||||
Util.setContentLength(response, file.length());
|
||||
}
|
||||
|
||||
copyFileToStream(file, RangeOutputStream.wrap(response.getOutputStream(), range), status, range);
|
||||
LOG.info("Downloaded '" + FileUtil.getShortPath(file) + "' to " + status.getPlayer());
|
||||
}
|
||||
|
||||
private String encodeAsRFC5987(String string) throws UnsupportedEncodingException {
|
||||
byte[] stringAsByteArray = string.getBytes("UTF-8");
|
||||
char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
byte[] attrChar = {'!', '#', '$', '&', '+', '-', '.', '^', '_', '`', '|', '~', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : stringAsByteArray) {
|
||||
if (Arrays.binarySearch(attrChar, b) >= 0) {
|
||||
sb.append((char) b);
|
||||
} else {
|
||||
sb.append('%');
|
||||
sb.append(digits[0x0f & (b >>> 4)]);
|
||||
sb.append(digits[b & 0x0f]);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the given files. The files are packed together in an
|
||||
* uncompressed zip-file.
|
||||
*
|
||||
*
|
||||
* @param response The HTTP response.
|
||||
* @param status The download status.
|
||||
* @param files The files to download.
|
||||
* @param indexes Only download songs at these indexes. May be <code>null</code>.
|
||||
* @param coverArtFile The cover art file to include, may be {@code null}.
|
||||
*@param range The byte range, may be <code>null</code>.
|
||||
* @param zipFileName The name of the resulting zip file. @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private void downloadFiles(HttpServletResponse response, TransferStatus status, List<MediaFile> files, int[] indexes, File coverArtFile, HttpRange range, String zipFileName) throws IOException {
|
||||
if (indexes != null && indexes.length == 1) {
|
||||
downloadFile(response, status, files.get(indexes[0]).getFile(), range);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Starting to download '" + zipFileName + "' to " + status.getPlayer());
|
||||
response.setContentType("application/x-download");
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodeAsRFC5987(zipFileName));
|
||||
|
||||
ZipOutputStream out = new ZipOutputStream(RangeOutputStream.wrap(response.getOutputStream(), range));
|
||||
out.setMethod(ZipOutputStream.STORED); // No compression.
|
||||
|
||||
List<MediaFile> filesToDownload = new ArrayList<MediaFile>();
|
||||
if (indexes == null) {
|
||||
filesToDownload.addAll(files);
|
||||
} else {
|
||||
for (int index : indexes) {
|
||||
try {
|
||||
filesToDownload.add(files.get(index));
|
||||
} catch (IndexOutOfBoundsException x) { /* Ignored */}
|
||||
}
|
||||
}
|
||||
|
||||
for (MediaFile mediaFile : filesToDownload) {
|
||||
zip(out, mediaFile.getParentFile(), mediaFile.getFile(), status, range);
|
||||
}
|
||||
if (coverArtFile != null && coverArtFile.exists()) {
|
||||
zip(out, coverArtFile.getParentFile(), coverArtFile, status, range);
|
||||
}
|
||||
|
||||
|
||||
out.close();
|
||||
LOG.info("Downloaded '" + zipFileName + "' to " + status.getPlayer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for writing the content of a given file to a given output stream.
|
||||
*
|
||||
*
|
||||
* @param file The file to copy.
|
||||
* @param out The output stream to write to.
|
||||
* @param status The download status.
|
||||
* @param range The byte range, may be <code>null</code>.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private void copyFileToStream(File file, OutputStream out, TransferStatus status, HttpRange range) throws IOException {
|
||||
LOG.info("Downloading '" + FileUtil.getShortPath(file) + "' to " + status.getPlayer());
|
||||
|
||||
final int bufferSize = 16 * 1024; // 16 Kbit
|
||||
InputStream in = new BufferedInputStream(new FileInputStream(file), bufferSize);
|
||||
|
||||
try {
|
||||
byte[] buf = new byte[bufferSize];
|
||||
long bitrateLimit = 0;
|
||||
long lastLimitCheck = 0;
|
||||
|
||||
while (true) {
|
||||
long before = System.currentTimeMillis();
|
||||
int n = in.read(buf);
|
||||
if (n == -1) {
|
||||
break;
|
||||
}
|
||||
out.write(buf, 0, n);
|
||||
|
||||
// Don't sleep if outside range.
|
||||
if (range != null && !range.contains(status.getBytesSkipped() + status.getBytesTransfered())) {
|
||||
status.addBytesSkipped(n);
|
||||
continue;
|
||||
}
|
||||
|
||||
status.addBytesTransfered(n);
|
||||
long after = System.currentTimeMillis();
|
||||
|
||||
// Calculate bitrate limit every 5 seconds.
|
||||
if (after - lastLimitCheck > 5000) {
|
||||
bitrateLimit = 1024L * settingsService.getDownloadBitrateLimit() /
|
||||
Math.max(1, statusService.getAllDownloadStatuses().size());
|
||||
lastLimitCheck = after;
|
||||
}
|
||||
|
||||
// Sleep for a while to throttle bitrate.
|
||||
if (bitrateLimit != 0) {
|
||||
long sleepTime = 8L * 1000 * bufferSize / bitrateLimit - (after - before);
|
||||
if (sleepTime > 0L) {
|
||||
try {
|
||||
Thread.sleep(sleepTime);
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to sleep.", x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
out.flush();
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a file or a directory structure to a zip output stream. File entries in the zip file are relative
|
||||
* to the given root.
|
||||
*
|
||||
*
|
||||
* @param out The zip output stream.
|
||||
* @param root The root of the directory structure. Used to create path information in the zip file.
|
||||
* @param file The file or directory to zip.
|
||||
* @param status The download status.
|
||||
* @param range The byte range, may be <code>null</code>.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private void zip(ZipOutputStream out, File root, File file, TransferStatus status, HttpRange range) throws IOException {
|
||||
|
||||
// Exclude all hidden files starting with a "."
|
||||
if (file.getName().startsWith(".")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String zipName = file.getCanonicalPath().substring(root.getCanonicalPath().length() + 1);
|
||||
|
||||
if (file.isFile()) {
|
||||
status.setFile(file);
|
||||
|
||||
ZipEntry zipEntry = new ZipEntry(zipName);
|
||||
zipEntry.setSize(file.length());
|
||||
zipEntry.setCompressedSize(file.length());
|
||||
zipEntry.setCrc(computeCrc(file));
|
||||
|
||||
out.putNextEntry(zipEntry);
|
||||
copyFileToStream(file, out, status, range);
|
||||
out.closeEntry();
|
||||
|
||||
} else {
|
||||
ZipEntry zipEntry = new ZipEntry(zipName + '/');
|
||||
zipEntry.setSize(0);
|
||||
zipEntry.setCompressedSize(0);
|
||||
zipEntry.setCrc(0);
|
||||
|
||||
out.putNextEntry(zipEntry);
|
||||
out.closeEntry();
|
||||
|
||||
File[] children = FileUtil.listFiles(file);
|
||||
for (File child : children) {
|
||||
zip(out, root, child, status, range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the CRC checksum for the given file.
|
||||
*
|
||||
* @param file The file to compute checksum for.
|
||||
* @return A CRC32 checksum.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
private long computeCrc(File file) throws IOException {
|
||||
CRC32 crc = new CRC32();
|
||||
InputStream in = new FileInputStream(file);
|
||||
|
||||
try {
|
||||
|
||||
byte[] buf = new byte[8192];
|
||||
int n = in.read(buf);
|
||||
while (n != -1) {
|
||||
crc.update(buf, 0, n);
|
||||
n = in.read(buf);
|
||||
}
|
||||
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
|
||||
return crc.getValue();
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setStatusService(StatusService statusService) {
|
||||
this.statusService = statusService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.metadata.JaudiotaggerParser;
|
||||
import org.libresonic.player.service.metadata.MetaDataParser;
|
||||
import org.libresonic.player.service.metadata.MetaDataParserFactory;
|
||||
|
||||
/**
|
||||
* Controller for the page used to edit MP3 tags.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class EditTagsController extends ParameterizableViewController {
|
||||
|
||||
private MetaDataParserFactory metaDataParserFactory;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
MediaFile dir = mediaFileService.getMediaFile(id);
|
||||
List<MediaFile> files = mediaFileService.getChildrenOf(dir, true, false, true, false);
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
if (!files.isEmpty()) {
|
||||
map.put("defaultArtist", files.get(0).getArtist());
|
||||
map.put("defaultAlbum", files.get(0).getAlbumName());
|
||||
map.put("defaultYear", files.get(0).getYear());
|
||||
map.put("defaultGenre", files.get(0).getGenre());
|
||||
}
|
||||
map.put("allGenres", JaudiotaggerParser.getID3V1Genres());
|
||||
|
||||
List<Song> songs = new ArrayList<Song>();
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
songs.add(createSong(files.get(i), i));
|
||||
}
|
||||
map.put("id", id);
|
||||
map.put("songs", songs);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Song createSong(MediaFile file, int index) {
|
||||
MetaDataParser parser = metaDataParserFactory.getParser(file.getFile());
|
||||
|
||||
Song song = new Song();
|
||||
song.setId(file.getId());
|
||||
song.setFileName(FilenameUtils.getBaseName(file.getPath()));
|
||||
song.setTrack(file.getTrackNumber());
|
||||
song.setSuggestedTrack(index + 1);
|
||||
song.setTitle(file.getTitle());
|
||||
song.setSuggestedTitle(parser.guessTitle(file.getFile()));
|
||||
song.setArtist(file.getArtist());
|
||||
song.setAlbum(file.getAlbumName());
|
||||
song.setYear(file.getYear());
|
||||
song.setGenre(file.getGenre());
|
||||
return song;
|
||||
}
|
||||
|
||||
public void setMetaDataParserFactory(MetaDataParserFactory metaDataParserFactory) {
|
||||
this.metaDataParserFactory = metaDataParserFactory;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains information about a single song.
|
||||
*/
|
||||
public static class Song {
|
||||
private int id;
|
||||
private String fileName;
|
||||
private Integer suggestedTrack;
|
||||
private Integer track;
|
||||
private String suggestedTitle;
|
||||
private String title;
|
||||
private String artist;
|
||||
private String album;
|
||||
private Integer year;
|
||||
private String genre;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public Integer getSuggestedTrack() {
|
||||
return suggestedTrack;
|
||||
}
|
||||
|
||||
public void setSuggestedTrack(Integer suggestedTrack) {
|
||||
this.suggestedTrack = suggestedTrack;
|
||||
}
|
||||
|
||||
public Integer getTrack() {
|
||||
return track;
|
||||
}
|
||||
|
||||
public void setTrack(Integer track) {
|
||||
this.track = track;
|
||||
}
|
||||
|
||||
public String getSuggestedTitle() {
|
||||
return suggestedTitle;
|
||||
}
|
||||
|
||||
public void setSuggestedTitle(String suggestedTitle) {
|
||||
this.suggestedTitle = suggestedTitle;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public void setArtist(String artist) {
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public void setAlbum(String album) {
|
||||
this.album = album;
|
||||
}
|
||||
|
||||
public Integer getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
public void setYear(Integer year) {
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
public String getGenre() {
|
||||
return genre;
|
||||
}
|
||||
|
||||
public void setGenre(String genre) {
|
||||
this.genre = genre;
|
||||
}
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.Share;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.service.ShareService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to play shared music (Twitter, Facebook etc).
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ExternalPlayerController extends ParameterizableViewController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private PlayerService playerService;
|
||||
private ShareService shareService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
String pathInfo = request.getPathInfo();
|
||||
|
||||
if (pathInfo == null || !pathInfo.startsWith("/")) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return null;
|
||||
}
|
||||
|
||||
Share share = shareService.getShareByName(pathInfo.substring(1));
|
||||
|
||||
if (share != null && share.getExpires() != null && share.getExpires().before(new Date())) {
|
||||
share = null;
|
||||
}
|
||||
|
||||
if (share != null) {
|
||||
share.setLastVisited(new Date());
|
||||
share.setVisitCount(share.getVisitCount() + 1);
|
||||
shareService.updateShare(share);
|
||||
}
|
||||
|
||||
Player player = playerService.getGuestPlayer(request);
|
||||
|
||||
map.put("share", share);
|
||||
map.put("songs", getSongs(share, player.getUsername()));
|
||||
map.put("redirectUrl", settingsService.getUrlRedirectUrl());
|
||||
map.put("player", player.getId());
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<MediaFile> getSongs(Share share, String username) throws IOException {
|
||||
List<MediaFile> result = new ArrayList<MediaFile>();
|
||||
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
|
||||
if (share != null) {
|
||||
for (MediaFile file : shareService.getSharedFiles(share.getId(), musicFolders)) {
|
||||
if (file.getFile().exists()) {
|
||||
if (file.isDirectory()) {
|
||||
result.addAll(mediaFileService.getChildrenOf(file, true, false, true));
|
||||
} else {
|
||||
result.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setShareService(ShareService shareService) {
|
||||
this.shareService = shareService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import org.libresonic.player.command.GeneralSettingsCommand;
|
||||
import org.libresonic.player.domain.Theme;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate general settings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class GeneralSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
GeneralSettingsCommand command = new GeneralSettingsCommand();
|
||||
command.setCoverArtFileTypes(settingsService.getCoverArtFileTypes());
|
||||
command.setIgnoredArticles(settingsService.getIgnoredArticles());
|
||||
command.setShortcuts(settingsService.getShortcuts());
|
||||
command.setIndex(settingsService.getIndexString());
|
||||
command.setPlaylistFolder(settingsService.getPlaylistFolder());
|
||||
command.setMusicFileTypes(settingsService.getMusicFileTypes());
|
||||
command.setVideoFileTypes(settingsService.getVideoFileTypes());
|
||||
command.setSortAlbumsByYear(settingsService.isSortAlbumsByYear());
|
||||
command.setGettingStartedEnabled(settingsService.isGettingStartedEnabled());
|
||||
command.setWelcomeTitle(settingsService.getWelcomeTitle());
|
||||
command.setWelcomeSubtitle(settingsService.getWelcomeSubtitle());
|
||||
command.setWelcomeMessage(settingsService.getWelcomeMessage());
|
||||
command.setLoginMessage(settingsService.getLoginMessage());
|
||||
|
||||
Theme[] themes = settingsService.getAvailableThemes();
|
||||
command.setThemes(themes);
|
||||
String currentThemeId = settingsService.getThemeId();
|
||||
for (int i = 0; i < themes.length; i++) {
|
||||
if (currentThemeId.equals(themes[i].getId())) {
|
||||
command.setThemeIndex(String.valueOf(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Locale currentLocale = settingsService.getLocale();
|
||||
Locale[] locales = settingsService.getAvailableLocales();
|
||||
String[] localeStrings = new String[locales.length];
|
||||
for (int i = 0; i < locales.length; i++) {
|
||||
localeStrings[i] = locales[i].getDisplayName(locales[i]);
|
||||
|
||||
if (currentLocale.equals(locales[i])) {
|
||||
command.setLocaleIndex(String.valueOf(i));
|
||||
}
|
||||
}
|
||||
command.setLocales(localeStrings);
|
||||
|
||||
return command;
|
||||
|
||||
}
|
||||
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
GeneralSettingsCommand command = (GeneralSettingsCommand) comm;
|
||||
|
||||
int themeIndex = Integer.parseInt(command.getThemeIndex());
|
||||
Theme theme = settingsService.getAvailableThemes()[themeIndex];
|
||||
|
||||
int localeIndex = Integer.parseInt(command.getLocaleIndex());
|
||||
Locale locale = settingsService.getAvailableLocales()[localeIndex];
|
||||
|
||||
command.setToast(true);
|
||||
command.setReloadNeeded(!settingsService.getIndexString().equals(command.getIndex()) ||
|
||||
!settingsService.getIgnoredArticles().equals(command.getIgnoredArticles()) ||
|
||||
!settingsService.getShortcuts().equals(command.getShortcuts()) ||
|
||||
!settingsService.getThemeId().equals(theme.getId()) ||
|
||||
!settingsService.getLocale().equals(locale));
|
||||
|
||||
settingsService.setIndexString(command.getIndex());
|
||||
settingsService.setIgnoredArticles(command.getIgnoredArticles());
|
||||
settingsService.setShortcuts(command.getShortcuts());
|
||||
settingsService.setPlaylistFolder(command.getPlaylistFolder());
|
||||
settingsService.setMusicFileTypes(command.getMusicFileTypes());
|
||||
settingsService.setVideoFileTypes(command.getVideoFileTypes());
|
||||
settingsService.setCoverArtFileTypes(command.getCoverArtFileTypes());
|
||||
settingsService.setSortAlbumsByYear(command.isSortAlbumsByYear());
|
||||
settingsService.setGettingStartedEnabled(command.isGettingStartedEnabled());
|
||||
settingsService.setWelcomeTitle(command.getWelcomeTitle());
|
||||
settingsService.setWelcomeSubtitle(command.getWelcomeSubtitle());
|
||||
settingsService.setWelcomeMessage(command.getWelcomeMessage());
|
||||
settingsService.setLoginMessage(command.getLoginMessage());
|
||||
settingsService.setThemeId(theme.getId());
|
||||
settingsService.setLocale(locale);
|
||||
settingsService.save();
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.util.Pair;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller which produces the HLS (Http Live Streaming) playlist.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class HLSController implements Controller {
|
||||
|
||||
private static final int SEGMENT_DURATION = 10;
|
||||
private static final Pattern BITRATE_PATTERN = Pattern.compile("(\\d+)(@(\\d+)x(\\d+))?");
|
||||
|
||||
private PlayerService playerService;
|
||||
private MediaFileService mediaFileService;
|
||||
private SecurityService securityService;
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
|
||||
int id = ServletRequestUtils.getIntParameter(request, "id");
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(id);
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
String username = player.getUsername();
|
||||
|
||||
if (mediaFile == null) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Media file not found: " + id);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (username != null && !securityService.isFolderAccessAllowed(mediaFile, username)) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||
"Access to file " + mediaFile.getId() + " is forbidden for user " + username);
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer duration = mediaFile.getDurationSeconds();
|
||||
if (duration == null || duration == 0) {
|
||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unknown duration for media file: " + id);
|
||||
return null;
|
||||
}
|
||||
|
||||
response.setContentType("application/vnd.apple.mpegurl");
|
||||
response.setCharacterEncoding(StringUtil.ENCODING_UTF8);
|
||||
List<Pair<Integer, Dimension>> bitRates = parseBitRates(request);
|
||||
PrintWriter writer = response.getWriter();
|
||||
if (bitRates.size() > 1) {
|
||||
generateVariantPlaylist(request, id, player, bitRates, writer);
|
||||
} else {
|
||||
generateNormalPlaylist(request, id, player, bitRates.size() == 1 ? bitRates.get(0) : null, duration, writer);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Pair<Integer, Dimension>> parseBitRates(HttpServletRequest request) throws IllegalArgumentException {
|
||||
List<Pair<Integer, Dimension>> result = new ArrayList<Pair<Integer, Dimension>>();
|
||||
String[] bitRates = request.getParameterValues("bitRate");
|
||||
if (bitRates != null) {
|
||||
for (String bitRate : bitRates) {
|
||||
result.add(parseBitRate(bitRate));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string containing the bitrate and an optional width/height, e.g., 1200@640x480
|
||||
*/
|
||||
protected Pair<Integer, Dimension> parseBitRate(String bitRate) throws IllegalArgumentException {
|
||||
|
||||
Matcher matcher = BITRATE_PATTERN.matcher(bitRate);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException("Invalid bitrate specification: " + bitRate);
|
||||
}
|
||||
int kbps = Integer.parseInt(matcher.group(1));
|
||||
if (matcher.group(3) == null) {
|
||||
return new Pair<Integer, Dimension>(kbps, null);
|
||||
} else {
|
||||
int width = Integer.parseInt(matcher.group(3));
|
||||
int height = Integer.parseInt(matcher.group(4));
|
||||
return new Pair<Integer, Dimension>(kbps, new Dimension(width, height));
|
||||
}
|
||||
}
|
||||
|
||||
private void generateVariantPlaylist(HttpServletRequest request, int id, Player player, List<Pair<Integer, Dimension>> bitRates, PrintWriter writer) {
|
||||
writer.println("#EXTM3U");
|
||||
writer.println("#EXT-X-VERSION:1");
|
||||
// writer.println("#EXT-X-TARGETDURATION:" + SEGMENT_DURATION);
|
||||
|
||||
String contextPath = getContextPath(request);
|
||||
for (Pair<Integer, Dimension> bitRate : bitRates) {
|
||||
Integer kbps = bitRate.getFirst();
|
||||
writer.println("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + kbps * 1000L);
|
||||
writer.print(contextPath + "hls/hls.m3u8?id=" + id + "&player=" + player.getId() + "&bitRate=" + kbps);
|
||||
Dimension dimension = bitRate.getSecond();
|
||||
if (dimension != null) {
|
||||
writer.print("@" + dimension.width + "x" + dimension.height);
|
||||
}
|
||||
writer.println();
|
||||
}
|
||||
// writer.println("#EXT-X-ENDLIST");
|
||||
}
|
||||
|
||||
private void generateNormalPlaylist(HttpServletRequest request, int id, Player player, Pair<Integer, Dimension> bitRate, int totalDuration, PrintWriter writer) {
|
||||
writer.println("#EXTM3U");
|
||||
writer.println("#EXT-X-VERSION:1");
|
||||
writer.println("#EXT-X-TARGETDURATION:" + SEGMENT_DURATION);
|
||||
|
||||
for (int i = 0; i < totalDuration / SEGMENT_DURATION; i++) {
|
||||
int offset = i * SEGMENT_DURATION;
|
||||
writer.println("#EXTINF:" + SEGMENT_DURATION + ",");
|
||||
writer.println(createStreamUrl(request, player, id, offset, SEGMENT_DURATION, bitRate));
|
||||
}
|
||||
|
||||
int remainder = totalDuration % SEGMENT_DURATION;
|
||||
if (remainder > 0) {
|
||||
writer.println("#EXTINF:" + remainder + ",");
|
||||
int offset = totalDuration - remainder;
|
||||
writer.println(createStreamUrl(request, player, id, offset, remainder, bitRate));
|
||||
}
|
||||
writer.println("#EXT-X-ENDLIST");
|
||||
}
|
||||
|
||||
private String createStreamUrl(HttpServletRequest request, Player player, int id, int offset, int duration, Pair<Integer, Dimension> bitRate) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(getContextPath(request)).append("stream/stream.ts?id=").append(id).append("&hls=true&timeOffset=").append(offset)
|
||||
.append("&player=").append(player.getId()).append("&duration=").append(duration);
|
||||
if (bitRate != null) {
|
||||
builder.append("&maxBitRate=").append(bitRate.getFirst());
|
||||
Dimension dimension = bitRate.getSecond();
|
||||
if (dimension != null) {
|
||||
builder.append("&size=").append(dimension.width).append("x").append(dimension.height);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private String getContextPath(HttpServletRequest request) {
|
||||
String contextPath = request.getContextPath();
|
||||
if (StringUtils.isEmpty(contextPath)) {
|
||||
contextPath = "/";
|
||||
} else {
|
||||
contextPath += "/";
|
||||
}
|
||||
return contextPath;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.service.VersionService;
|
||||
|
||||
/**
|
||||
* Controller for the help page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class HelpController extends ParameterizableViewController {
|
||||
|
||||
private VersionService versionService;
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
if (versionService.isNewFinalVersionAvailable()) {
|
||||
map.put("newVersionAvailable", true);
|
||||
map.put("latestVersion", versionService.getLatestFinalVersion());
|
||||
} else if (versionService.isNewBetaVersionAvailable()) {
|
||||
map.put("newVersionAvailable", true);
|
||||
map.put("latestVersion", versionService.getLatestBetaVersion());
|
||||
}
|
||||
|
||||
long totalMemory = Runtime.getRuntime().totalMemory();
|
||||
long freeMemory = Runtime.getRuntime().freeMemory();
|
||||
|
||||
String serverInfo = request.getSession().getServletContext().getServerInfo() +
|
||||
", java " + System.getProperty("java.version") +
|
||||
", " + System.getProperty("os.name");
|
||||
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("brand", settingsService.getBrand());
|
||||
map.put("localVersion", versionService.getLocalVersion());
|
||||
map.put("buildDate", versionService.getLocalBuildDate());
|
||||
map.put("buildNumber", versionService.getLocalBuildNumber());
|
||||
map.put("serverInfo", serverInfo);
|
||||
map.put("usedMemory", totalMemory - freeMemory);
|
||||
map.put("totalMemory", totalMemory);
|
||||
map.put("logEntries", Logger.getLatestLogEntries());
|
||||
map.put("logFile", Logger.getLogFile());
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setVersionService(VersionService versionService) {
|
||||
this.versionService = versionService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import org.libresonic.player.domain.AlbumListType;
|
||||
import org.libresonic.player.domain.CoverArtScheme;
|
||||
import org.libresonic.player.domain.Genre;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.MediaScannerService;
|
||||
import org.libresonic.player.service.RatingService;
|
||||
import org.libresonic.player.service.SearchService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
import static org.springframework.web.bind.ServletRequestUtils.getIntParameter;
|
||||
import static org.springframework.web.bind.ServletRequestUtils.getStringParameter;
|
||||
|
||||
/**
|
||||
* Controller for the home page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class HomeController extends ParameterizableViewController {
|
||||
|
||||
private static final int LIST_SIZE = 40;
|
||||
|
||||
private SettingsService settingsService;
|
||||
private MediaScannerService mediaScannerService;
|
||||
private RatingService ratingService;
|
||||
private SecurityService securityService;
|
||||
private MediaFileService mediaFileService;
|
||||
private SearchService searchService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
if (user.isAdminRole() && settingsService.isGettingStartedEnabled()) {
|
||||
return new ModelAndView(new RedirectView("gettingStarted.view"));
|
||||
}
|
||||
int listOffset = getIntParameter(request, "listOffset", 0);
|
||||
AlbumListType listType = AlbumListType.fromId(getStringParameter(request, "listType"));
|
||||
if (listType == null) {
|
||||
UserSettings userSettings = settingsService.getUserSettings(user.getUsername());
|
||||
listType = userSettings.getDefaultAlbumList();
|
||||
}
|
||||
|
||||
MusicFolder selectedMusicFolder = settingsService.getSelectedMusicFolder(user.getUsername());
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(user.getUsername(),
|
||||
selectedMusicFolder == null ? null : selectedMusicFolder.getId());
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
List<Album> albums = Collections.emptyList();
|
||||
switch (listType) {
|
||||
case HIGHEST:
|
||||
albums = getHighestRated(listOffset, LIST_SIZE, musicFolders);
|
||||
break;
|
||||
case FREQUENT:
|
||||
albums = getMostFrequent(listOffset, LIST_SIZE, musicFolders);
|
||||
break;
|
||||
case RECENT:
|
||||
albums = getMostRecent(listOffset, LIST_SIZE, musicFolders);
|
||||
break;
|
||||
case NEWEST:
|
||||
albums = getNewest(listOffset, LIST_SIZE, musicFolders);
|
||||
break;
|
||||
case STARRED:
|
||||
albums = getStarred(listOffset, LIST_SIZE, user.getUsername(), musicFolders);
|
||||
break;
|
||||
case RANDOM:
|
||||
albums = getRandom(LIST_SIZE, musicFolders);
|
||||
break;
|
||||
case ALPHABETICAL:
|
||||
albums = getAlphabetical(listOffset, LIST_SIZE, true, musicFolders);
|
||||
break;
|
||||
case DECADE:
|
||||
List<Integer> decades = createDecades();
|
||||
map.put("decades", decades);
|
||||
int decade = getIntParameter(request, "decade", decades.get(0));
|
||||
map.put("decade", decade);
|
||||
albums = getByYear(listOffset, LIST_SIZE, decade, decade + 9, musicFolders);
|
||||
break;
|
||||
case GENRE:
|
||||
List<Genre> genres = mediaFileService.getGenres(true);
|
||||
map.put("genres", genres);
|
||||
if (!genres.isEmpty()) {
|
||||
String genre = getStringParameter(request, "genre", genres.get(0).getName());
|
||||
map.put("genre", genre);
|
||||
albums = getByGenre(listOffset, LIST_SIZE, genre, musicFolders);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
map.put("albums", albums);
|
||||
map.put("welcomeTitle", settingsService.getWelcomeTitle());
|
||||
map.put("welcomeSubtitle", settingsService.getWelcomeSubtitle());
|
||||
map.put("welcomeMessage", settingsService.getWelcomeMessage());
|
||||
map.put("isIndexBeingCreated", mediaScannerService.isScanning());
|
||||
map.put("musicFoldersExist", !settingsService.getAllMusicFolders().isEmpty());
|
||||
map.put("listType", listType.getId());
|
||||
map.put("listSize", LIST_SIZE);
|
||||
map.put("coverArtSize", CoverArtScheme.MEDIUM.getSize());
|
||||
map.put("listOffset", listOffset);
|
||||
map.put("musicFolder", selectedMusicFolder);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getHighestRated(int offset, int count, List<MusicFolder> musicFolders) {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile mediaFile : ratingService.getHighestRatedAlbums(offset, count, musicFolders)) {
|
||||
Album album = createAlbum(mediaFile);
|
||||
album.setRating((int) Math.round(ratingService.getAverageRating(mediaFile) * 10.0D));
|
||||
result.add(album);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getMostFrequent(int offset, int count, List<MusicFolder> musicFolders) {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile mediaFile : mediaFileService.getMostFrequentlyPlayedAlbums(offset, count, musicFolders)) {
|
||||
Album album = createAlbum(mediaFile);
|
||||
album.setPlayCount(mediaFile.getPlayCount());
|
||||
result.add(album);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getMostRecent(int offset, int count, List<MusicFolder> musicFolders) {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile mediaFile : mediaFileService.getMostRecentlyPlayedAlbums(offset, count, musicFolders)) {
|
||||
Album album = createAlbum(mediaFile);
|
||||
album.setLastPlayed(mediaFile.getLastPlayed());
|
||||
result.add(album);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getNewest(int offset, int count, List<MusicFolder> musicFolders) throws IOException {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : mediaFileService.getNewestAlbums(offset, count, musicFolders)) {
|
||||
Album album = createAlbum(file);
|
||||
Date created = file.getCreated();
|
||||
if (created == null) {
|
||||
created = file.getChanged();
|
||||
}
|
||||
album.setCreated(created);
|
||||
result.add(album);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getStarred(int offset, int count, String username, List<MusicFolder> musicFolders) throws IOException {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : mediaFileService.getStarredAlbums(offset, count, username, musicFolders)) {
|
||||
result.add(createAlbum(file));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getRandom(int count, List<MusicFolder> musicFolders) throws IOException {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : searchService.getRandomAlbums(count, musicFolders)) {
|
||||
result.add(createAlbum(file));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getAlphabetical(int offset, int count, boolean byArtist, List<MusicFolder> musicFolders) throws IOException {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : mediaFileService.getAlphabeticalAlbums(offset, count, byArtist, musicFolders)) {
|
||||
result.add(createAlbum(file));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getByYear(int offset, int count, int fromYear, int toYear, List<MusicFolder> musicFolders) {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : mediaFileService.getAlbumsByYear(offset, count, fromYear, toYear, musicFolders)) {
|
||||
Album album = createAlbum(file);
|
||||
album.setYear(file.getYear());
|
||||
result.add(album);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Integer> createDecades() {
|
||||
List<Integer> result = new ArrayList<Integer>();
|
||||
int decade = Calendar.getInstance().get(Calendar.YEAR) / 10;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
result.add((decade - i) * 10);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Album> getByGenre(int offset, int count, String genre, List<MusicFolder> musicFolders) {
|
||||
List<Album> result = new ArrayList<Album>();
|
||||
for (MediaFile file : mediaFileService.getAlbumsByGenre(offset, count, genre, musicFolders)) {
|
||||
result.add(createAlbum(file));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Album createAlbum(MediaFile file) {
|
||||
Album album = new Album();
|
||||
album.setId(file.getId());
|
||||
album.setPath(file.getPath());
|
||||
album.setArtist(file.getArtist());
|
||||
album.setAlbumTitle(file.getAlbumName());
|
||||
album.setCoverArtPath(file.getCoverArtPath());
|
||||
return album;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setMediaScannerService(MediaScannerService mediaScannerService) {
|
||||
this.mediaScannerService = mediaScannerService;
|
||||
}
|
||||
|
||||
public void setRatingService(RatingService ratingService) {
|
||||
this.ratingService = ratingService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains info for a single album.
|
||||
*/
|
||||
public static class Album {
|
||||
private String path;
|
||||
private String coverArtPath;
|
||||
private String artist;
|
||||
private String albumTitle;
|
||||
private Date created;
|
||||
private Date lastPlayed;
|
||||
private Integer playCount;
|
||||
private Integer rating;
|
||||
private int id;
|
||||
private Integer year;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getCoverArtPath() {
|
||||
return coverArtPath;
|
||||
}
|
||||
|
||||
public void setCoverArtPath(String coverArtPath) {
|
||||
this.coverArtPath = coverArtPath;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public void setArtist(String artist) {
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public String getAlbumTitle() {
|
||||
return albumTitle;
|
||||
}
|
||||
|
||||
public void setAlbumTitle(String albumTitle) {
|
||||
this.albumTitle = albumTitle;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Date getLastPlayed() {
|
||||
return lastPlayed;
|
||||
}
|
||||
|
||||
public void setLastPlayed(Date lastPlayed) {
|
||||
this.lastPlayed = lastPlayed;
|
||||
}
|
||||
|
||||
public Integer getPlayCount() {
|
||||
return playCount;
|
||||
}
|
||||
|
||||
public void setPlayCount(Integer playCount) {
|
||||
this.playCount = playCount;
|
||||
}
|
||||
|
||||
public Integer getRating() {
|
||||
return rating;
|
||||
}
|
||||
|
||||
public void setRating(Integer rating) {
|
||||
this.rating = rating;
|
||||
}
|
||||
|
||||
public void setYear(Integer year) {
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
public Integer getYear() {
|
||||
return year;
|
||||
}
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.FileItemFactory;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.domain.Playlist;
|
||||
import org.libresonic.player.service.PlaylistService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ImportPlaylistController extends ParameterizableViewController {
|
||||
|
||||
private static final long MAX_PLAYLIST_SIZE_MB = 5L;
|
||||
|
||||
private SecurityService securityService;
|
||||
private PlaylistService playlistService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
try {
|
||||
if (ServletFileUpload.isMultipartContent(request)) {
|
||||
|
||||
FileItemFactory factory = new DiskFileItemFactory();
|
||||
ServletFileUpload upload = new ServletFileUpload(factory);
|
||||
List<?> items = upload.parseRequest(request);
|
||||
for (Object o : items) {
|
||||
FileItem item = (FileItem) o;
|
||||
|
||||
if ("file".equals(item.getFieldName()) && !StringUtils.isBlank(item.getName())) {
|
||||
if (item.getSize() > MAX_PLAYLIST_SIZE_MB * 1024L * 1024L) {
|
||||
throw new Exception("The playlist file is too large. Max file size is " + MAX_PLAYLIST_SIZE_MB + " MB.");
|
||||
}
|
||||
String playlistName = FilenameUtils.getBaseName(item.getName());
|
||||
String fileName = FilenameUtils.getName(item.getName());
|
||||
String format = StringUtils.lowerCase(FilenameUtils.getExtension(item.getName()));
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
Playlist playlist = playlistService.importPlaylist(username, playlistName, fileName, format, item.getInputStream(), null);
|
||||
map.put("playlist", playlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
map.put("error", e.getMessage());
|
||||
}
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.libresonic.player.domain.InternetRadio;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate the set of internet radio/tv stations.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class InternetRadioSettingsController extends ParameterizableViewController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
if (isFormSubmission(request)) {
|
||||
String error = handleParameters(request);
|
||||
map.put("error", error);
|
||||
if (error == null) {
|
||||
map.put("reload", true);
|
||||
}
|
||||
}
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
map.put("internetRadios", settingsService.getAllInternetRadios(true));
|
||||
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given request represents a form submission.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return if the request represents a form submission
|
||||
*/
|
||||
private boolean isFormSubmission(HttpServletRequest request) {
|
||||
return "POST".equals(request.getMethod());
|
||||
}
|
||||
|
||||
private String handleParameters(HttpServletRequest request) {
|
||||
List<InternetRadio> radios = settingsService.getAllInternetRadios(true);
|
||||
for (InternetRadio radio : radios) {
|
||||
Integer id = radio.getId();
|
||||
String streamUrl = getParameter(request, "streamUrl", id);
|
||||
String homepageUrl = getParameter(request, "homepageUrl", id);
|
||||
String name = getParameter(request, "name", id);
|
||||
boolean enabled = getParameter(request, "enabled", id) != null;
|
||||
boolean delete = getParameter(request, "delete", id) != null;
|
||||
|
||||
if (delete) {
|
||||
settingsService.deleteInternetRadio(id);
|
||||
} else {
|
||||
if (name == null) {
|
||||
return "internetradiosettings.noname";
|
||||
}
|
||||
if (streamUrl == null) {
|
||||
return "internetradiosettings.nourl";
|
||||
}
|
||||
settingsService.updateInternetRadio(new InternetRadio(id, name, streamUrl, homepageUrl, enabled, new Date()));
|
||||
}
|
||||
}
|
||||
|
||||
String name = StringUtils.trimToNull(request.getParameter("name"));
|
||||
String streamUrl = StringUtils.trimToNull(request.getParameter("streamUrl"));
|
||||
String homepageUrl = StringUtils.trimToNull(request.getParameter("homepageUrl"));
|
||||
boolean enabled = StringUtils.trimToNull(request.getParameter("enabled")) != null;
|
||||
|
||||
if (name != null || streamUrl != null || homepageUrl != null) {
|
||||
if (name == null) {
|
||||
return "internetradiosettings.noname";
|
||||
}
|
||||
if (streamUrl == null) {
|
||||
return "internetradiosettings.nourl";
|
||||
}
|
||||
settingsService.createInternetRadio(new InternetRadio(name, streamUrl, homepageUrl, enabled, new Date()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getParameter(HttpServletRequest request, String name, Integer id) {
|
||||
return StringUtils.trimToNull(request.getParameter(name + "[" + id + "]"));
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.datatype.DatatypeFactory;
|
||||
import javax.xml.datatype.XMLGregorianCalendar;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.eclipse.persistence.jaxb.JAXBContext;
|
||||
import org.eclipse.persistence.jaxb.MarshallerProperties;
|
||||
import org.jdom.Attribute;
|
||||
import org.jdom.Document;
|
||||
import org.jdom.input.SAXBuilder;
|
||||
import org.libresonic.restapi.Error;
|
||||
import org.libresonic.restapi.ObjectFactory;
|
||||
import org.libresonic.restapi.Response;
|
||||
import org.libresonic.restapi.ResponseStatus;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
import static org.springframework.web.bind.ServletRequestUtils.getStringParameter;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
* @version $Id$
|
||||
*/
|
||||
public class JAXBWriter {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(JAXBWriter.class);
|
||||
|
||||
private final javax.xml.bind.JAXBContext jaxbContext;
|
||||
private final DatatypeFactory datatypeFactory;
|
||||
private final String restProtocolVersion;
|
||||
|
||||
public JAXBWriter() {
|
||||
try {
|
||||
jaxbContext = JAXBContext.newInstance(Response.class);
|
||||
datatypeFactory = DatatypeFactory.newInstance();
|
||||
restProtocolVersion = getRESTProtocolVersion();
|
||||
} catch (Exception x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private Marshaller createXmlMarshaller() throws JAXBException {
|
||||
Marshaller marshaller = jaxbContext.createMarshaller();
|
||||
marshaller.setProperty(Marshaller.JAXB_ENCODING, StringUtil.ENCODING_UTF8);
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
private Marshaller createJsonMarshaller() throws JAXBException {
|
||||
Marshaller marshaller = jaxbContext.createMarshaller();
|
||||
marshaller.setProperty(Marshaller.JAXB_ENCODING, StringUtil.ENCODING_UTF8);
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
|
||||
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
|
||||
marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, true);
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
private String getRESTProtocolVersion() throws Exception {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = StringUtil.class.getResourceAsStream("/libresonic-rest-api.xsd");
|
||||
Document document = new SAXBuilder().build(in);
|
||||
Attribute version = document.getRootElement().getAttribute("version");
|
||||
return version.getValue();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRestProtocolVersion() {
|
||||
return restProtocolVersion;
|
||||
}
|
||||
|
||||
public Response createResponse(boolean ok) {
|
||||
Response response = new ObjectFactory().createResponse();
|
||||
response.setStatus(ok ? ResponseStatus.OK : ResponseStatus.FAILED);
|
||||
response.setVersion(restProtocolVersion);
|
||||
return response;
|
||||
}
|
||||
|
||||
public void writeResponse(HttpServletRequest request, HttpServletResponse httpResponse, Response jaxbResponse) throws Exception {
|
||||
|
||||
String format = getStringParameter(request, "f", "xml");
|
||||
String jsonpCallback = request.getParameter("callback");
|
||||
boolean json = "json".equals(format);
|
||||
boolean jsonp = "jsonp".equals(format) && jsonpCallback != null;
|
||||
Marshaller marshaller;
|
||||
|
||||
if (json) {
|
||||
marshaller = createJsonMarshaller();
|
||||
httpResponse.setContentType("application/json");
|
||||
} else if (jsonp) {
|
||||
marshaller = createJsonMarshaller();
|
||||
httpResponse.setContentType("text/javascript");
|
||||
} else {
|
||||
marshaller = createXmlMarshaller();
|
||||
httpResponse.setContentType("text/xml");
|
||||
}
|
||||
|
||||
httpResponse.setCharacterEncoding(StringUtil.ENCODING_UTF8);
|
||||
|
||||
try {
|
||||
StringWriter writer = new StringWriter();
|
||||
if (jsonp) {
|
||||
writer.append(jsonpCallback).append('(');
|
||||
}
|
||||
marshaller.marshal(new ObjectFactory().createLibresonicResponse(jaxbResponse), writer);
|
||||
if (jsonp) {
|
||||
writer.append(");");
|
||||
}
|
||||
httpResponse.getWriter().append(writer.getBuffer());
|
||||
} catch (Exception x) {
|
||||
LOG.error("Failed to marshal JAXB", x);
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
|
||||
public void writeErrorResponse(HttpServletRequest request, HttpServletResponse response,
|
||||
RESTController.ErrorCode code, String message) throws Exception {
|
||||
Response res = createResponse(false);
|
||||
Error error = new Error();
|
||||
res.setError(error);
|
||||
error.setCode(code.getCode());
|
||||
error.setMessage(message);
|
||||
writeResponse(request, response, res);
|
||||
}
|
||||
|
||||
public XMLGregorianCalendar convertDate(Date date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GregorianCalendar c = new GregorianCalendar();
|
||||
c.setTime(date);
|
||||
return datatypeFactory.newXMLGregorianCalendar(c).normalize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
import org.libresonic.player.domain.InternetRadio;
|
||||
import org.libresonic.player.domain.MediaLibraryStatistics;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.MusicFolderContent;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.MediaScannerService;
|
||||
import org.libresonic.player.service.MusicIndexService;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.util.FileUtil;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller for the left index frame.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class LeftController extends ParameterizableViewController {
|
||||
|
||||
// Update this time if you want to force a refresh in clients.
|
||||
private static final Calendar LAST_COMPATIBILITY_TIME = Calendar.getInstance();
|
||||
static {
|
||||
LAST_COMPATIBILITY_TIME.set(2012, Calendar.MARCH, 6, 0, 0, 0);
|
||||
LAST_COMPATIBILITY_TIME.set(Calendar.MILLISECOND, 0);
|
||||
}
|
||||
|
||||
private MediaScannerService mediaScannerService;
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
private MusicIndexService musicIndexService;
|
||||
private PlayerService playerService;
|
||||
|
||||
/**
|
||||
* Note: This class intentionally does not implement org.springframework.web.servlet.mvc.LastModified
|
||||
* as we don't need browser-side caching of left.jsp. This method is only used by RESTController.
|
||||
*/
|
||||
public long getLastModified(HttpServletRequest request) {
|
||||
saveSelectedMusicFolder(request);
|
||||
|
||||
if (mediaScannerService.isScanning()) {
|
||||
return -1L;
|
||||
}
|
||||
|
||||
long lastModified = LAST_COMPATIBILITY_TIME.getTimeInMillis();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
|
||||
// When was settings last changed?
|
||||
lastModified = Math.max(lastModified, settingsService.getSettingsChanged());
|
||||
|
||||
// When was music folder(s) on disk last changed?
|
||||
List<MusicFolder> allMusicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
MusicFolder selectedMusicFolder = settingsService.getSelectedMusicFolder(username);
|
||||
if (selectedMusicFolder != null) {
|
||||
File file = selectedMusicFolder.getPath();
|
||||
lastModified = Math.max(lastModified, FileUtil.lastModified(file));
|
||||
} else {
|
||||
for (MusicFolder musicFolder : allMusicFolders) {
|
||||
File file = musicFolder.getPath();
|
||||
lastModified = Math.max(lastModified, FileUtil.lastModified(file));
|
||||
}
|
||||
}
|
||||
|
||||
// When was music folder table last changed?
|
||||
for (MusicFolder musicFolder : allMusicFolders) {
|
||||
lastModified = Math.max(lastModified, musicFolder.getChanged().getTime());
|
||||
}
|
||||
|
||||
// When was internet radio table last changed?
|
||||
for (InternetRadio internetRadio : settingsService.getAllInternetRadios()) {
|
||||
lastModified = Math.max(lastModified, internetRadio.getChanged().getTime());
|
||||
}
|
||||
|
||||
// When was user settings last changed?
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
lastModified = Math.max(lastModified, userSettings.getChanged().getTime());
|
||||
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
boolean musicFolderChanged = saveSelectedMusicFolder(request);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
MediaLibraryStatistics statistics = mediaScannerService.getStatistics();
|
||||
Locale locale = RequestContextUtils.getLocale(request);
|
||||
|
||||
boolean refresh = ServletRequestUtils.getBooleanParameter(request, "refresh", false);
|
||||
if (refresh) {
|
||||
settingsService.clearMusicFolderCache();
|
||||
}
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<MusicFolder> allMusicFolders = settingsService.getMusicFoldersForUser(username);
|
||||
MusicFolder selectedMusicFolder = settingsService.getSelectedMusicFolder(username);
|
||||
List<MusicFolder> musicFoldersToUse = selectedMusicFolder == null ? allMusicFolders : Arrays.asList(selectedMusicFolder);
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
MusicFolderContent musicFolderContent = musicIndexService.getMusicFolderContent(musicFoldersToUse, refresh);
|
||||
|
||||
map.put("player", playerService.getPlayer(request, response));
|
||||
map.put("scanning", mediaScannerService.isScanning());
|
||||
map.put("musicFolders", allMusicFolders);
|
||||
map.put("selectedMusicFolder", selectedMusicFolder);
|
||||
map.put("radios", settingsService.getAllInternetRadios());
|
||||
map.put("shortcuts", musicIndexService.getShortcuts(musicFoldersToUse));
|
||||
map.put("partyMode", userSettings.isPartyModeEnabled());
|
||||
map.put("organizeByFolderStructure", settingsService.isOrganizeByFolderStructure());
|
||||
map.put("musicFolderChanged", musicFolderChanged);
|
||||
|
||||
if (statistics != null) {
|
||||
map.put("statistics", statistics);
|
||||
long bytes = statistics.getTotalLengthInBytes();
|
||||
long hours = statistics.getTotalDurationInSeconds() / 3600L;
|
||||
map.put("hours", hours);
|
||||
map.put("bytes", StringUtil.formatBytes(bytes, locale));
|
||||
}
|
||||
|
||||
map.put("indexedArtists", musicFolderContent.getIndexedArtists());
|
||||
map.put("singleSongs", musicFolderContent.getSingleSongs());
|
||||
map.put("indexes", musicFolderContent.getIndexedArtists().keySet());
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean saveSelectedMusicFolder(HttpServletRequest request) {
|
||||
if (request.getParameter("musicFolderId") == null) {
|
||||
return false;
|
||||
}
|
||||
int musicFolderId = Integer.parseInt(request.getParameter("musicFolderId"));
|
||||
|
||||
// Note: UserSettings.setChanged() is intentionally not called. This would break browser caching
|
||||
// of the left frame.
|
||||
UserSettings settings = settingsService.getUserSettings(securityService.getCurrentUsername(request));
|
||||
settings.setSelectedMusicFolderId(musicFolderId);
|
||||
settingsService.updateUserSettings(settings);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setMediaScannerService(MediaScannerService mediaScannerService) {
|
||||
this.mediaScannerService = mediaScannerService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMusicIndexService(MusicIndexService musicIndexService) {
|
||||
this.musicIndexService = musicIndexService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Controller for the lyrics popup.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class LyricsController extends ParameterizableViewController {
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
map.put("artist", request.getParameter("artist"));
|
||||
map.put("song", request.getParameter("song"));
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.PlayQueue;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.service.TranscodingService;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller which produces the M3U playlist.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class M3UController implements Controller {
|
||||
|
||||
private PlayerService playerService;
|
||||
private SettingsService settingsService;
|
||||
private TranscodingService transcodingService;
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
response.setContentType("audio/x-mpegurl");
|
||||
response.setCharacterEncoding(StringUtil.ENCODING_UTF8);
|
||||
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
|
||||
String url = request.getRequestURL().toString();
|
||||
url = url.replaceFirst("play.m3u.*", "stream?");
|
||||
|
||||
// Rewrite URLs in case we're behind a proxy.
|
||||
if (settingsService.isRewriteUrlEnabled()) {
|
||||
String referer = request.getHeader("referer");
|
||||
url = StringUtil.rewriteUrl(url, referer);
|
||||
}
|
||||
|
||||
url = settingsService.rewriteRemoteUrl(url);
|
||||
|
||||
if (player.isExternalWithPlaylist()) {
|
||||
createClientSidePlaylist(response.getWriter(), player, url);
|
||||
} else {
|
||||
createServerSidePlaylist(response.getWriter(), player, url);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void createClientSidePlaylist(PrintWriter out, Player player, String url) throws Exception {
|
||||
out.println("#EXTM3U");
|
||||
List<MediaFile> result;
|
||||
synchronized (player.getPlayQueue()) {
|
||||
result = player.getPlayQueue().getFiles();
|
||||
}
|
||||
for (MediaFile mediaFile : result) {
|
||||
Integer duration = mediaFile.getDurationSeconds();
|
||||
if (duration == null) {
|
||||
duration = -1;
|
||||
}
|
||||
out.println("#EXTINF:" + duration + "," + mediaFile.getArtist() + " - " + mediaFile.getTitle());
|
||||
out.println(url + "player=" + player.getId() + "&id=" + mediaFile.getId() + "&suffix=." + transcodingService.getSuffix(player, mediaFile, null));
|
||||
}
|
||||
}
|
||||
|
||||
private void createServerSidePlaylist(PrintWriter out, Player player, String url) throws IOException {
|
||||
|
||||
url += "player=" + player.getId();
|
||||
|
||||
// Get suffix of current file, e.g., ".mp3".
|
||||
String suffix = getSuffix(player);
|
||||
if (suffix != null) {
|
||||
url += "&suffix=." + suffix;
|
||||
}
|
||||
|
||||
out.println("#EXTM3U");
|
||||
out.println("#EXTINF:-1,Libresonic");
|
||||
out.println(url);
|
||||
}
|
||||
|
||||
private String getSuffix(Player player) {
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
return playQueue.isEmpty() ? null : transcodingService.getSuffix(player, playQueue.getFile(0), null);
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setTranscodingService(TranscodingService transcodingService) {
|
||||
this.transcodingService = transcodingService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import org.libresonic.player.domain.CoverArtScheme;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.MediaFileComparator;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.AdService;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.RatingService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the main page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MainController extends AbstractController {
|
||||
|
||||
private SecurityService securityService;
|
||||
private PlayerService playerService;
|
||||
private SettingsService settingsService;
|
||||
private RatingService ratingService;
|
||||
private MediaFileService mediaFileService;
|
||||
private AdService adService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
List<MediaFile> mediaFiles = getMediaFiles(request);
|
||||
|
||||
if (mediaFiles.isEmpty()) {
|
||||
return new ModelAndView(new RedirectView("notFound.view"));
|
||||
}
|
||||
|
||||
MediaFile dir = mediaFiles.get(0);
|
||||
if (dir.isFile()) {
|
||||
dir = mediaFileService.getParentOf(dir);
|
||||
}
|
||||
|
||||
// Redirect if root directory.
|
||||
if (mediaFileService.isRoot(dir)) {
|
||||
return new ModelAndView(new RedirectView("home.view?"));
|
||||
}
|
||||
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
if (!securityService.isFolderAccessAllowed(dir, username)) {
|
||||
return new ModelAndView(new RedirectView("accessDenied.view"));
|
||||
}
|
||||
|
||||
List<MediaFile> children = mediaFiles.size() == 1 ? mediaFileService.getChildrenOf(dir, true, true, true) : getMultiFolderChildren(mediaFiles);
|
||||
List<MediaFile> files = new ArrayList<MediaFile>();
|
||||
List<MediaFile> subDirs = new ArrayList<MediaFile>();
|
||||
for (MediaFile child : children) {
|
||||
if (child.isFile()) {
|
||||
files.add(child);
|
||||
} else {
|
||||
subDirs.add(child);
|
||||
}
|
||||
}
|
||||
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
|
||||
mediaFileService.populateStarredDate(dir, username);
|
||||
mediaFileService.populateStarredDate(children, username);
|
||||
|
||||
map.put("dir", dir);
|
||||
map.put("files", files);
|
||||
map.put("subDirs", subDirs);
|
||||
map.put("ancestors", getAncestors(dir));
|
||||
map.put("coverArtSizeMedium", CoverArtScheme.MEDIUM.getSize());
|
||||
map.put("coverArtSizeLarge", CoverArtScheme.LARGE.getSize());
|
||||
map.put("player", player);
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("visibility", userSettings.getMainVisibility());
|
||||
map.put("showAlbumYear", settingsService.isSortAlbumsByYear());
|
||||
map.put("showArtistInfo", userSettings.isShowArtistInfoEnabled());
|
||||
map.put("partyMode", userSettings.isPartyModeEnabled());
|
||||
map.put("brand", settingsService.getBrand());
|
||||
map.put("showAd", !settingsService.isLicenseValid() && adService.showAd());
|
||||
map.put("viewAsList", isViewAsList(request, userSettings));
|
||||
if (dir.isAlbum()) {
|
||||
map.put("sieblingAlbums", getSieblingAlbums(dir));
|
||||
map.put("artist", guessArtist(children));
|
||||
map.put("album", guessAlbum(children));
|
||||
}
|
||||
|
||||
try {
|
||||
MediaFile parent = mediaFileService.getParentOf(dir);
|
||||
map.put("parent", parent);
|
||||
map.put("navigateUpAllowed", !mediaFileService.isRoot(parent));
|
||||
} catch (SecurityException x) {
|
||||
// Happens if Podcast directory is outside music folder.
|
||||
}
|
||||
|
||||
Integer userRating = ratingService.getRatingForUser(username, dir);
|
||||
Double averageRating = ratingService.getAverageRating(dir);
|
||||
|
||||
if (userRating == null) {
|
||||
userRating = 0;
|
||||
}
|
||||
|
||||
if (averageRating == null) {
|
||||
averageRating = 0.0D;
|
||||
}
|
||||
|
||||
map.put("userRating", 10 * userRating);
|
||||
map.put("averageRating", Math.round(10.0D * averageRating));
|
||||
map.put("starred", mediaFileService.getMediaFileStarredDate(dir.getId(), username) != null);
|
||||
|
||||
String view;
|
||||
if (isVideoOnly(children)) {
|
||||
view = "videoMain";
|
||||
} else if (dir.isAlbum()) {
|
||||
view = "albumMain";
|
||||
} else {
|
||||
view = "artistMain";
|
||||
}
|
||||
|
||||
ModelAndView result = new ModelAndView(view);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isViewAsList(HttpServletRequest request, UserSettings userSettings) {
|
||||
boolean viewAsList = ServletRequestUtils.getBooleanParameter(request, "viewAsList", userSettings.isViewAsList());
|
||||
if (viewAsList != userSettings.isViewAsList()) {
|
||||
userSettings.setViewAsList(viewAsList);
|
||||
userSettings.setChanged(new Date());
|
||||
settingsService.updateUserSettings(userSettings);
|
||||
}
|
||||
return viewAsList;
|
||||
}
|
||||
|
||||
private boolean isVideoOnly(List<MediaFile> children) {
|
||||
boolean videoFound = false;
|
||||
for (MediaFile child : children) {
|
||||
if (child.isAudio()) {
|
||||
return false;
|
||||
}
|
||||
if (child.isVideo()) {
|
||||
videoFound = true;
|
||||
}
|
||||
}
|
||||
return videoFound;
|
||||
}
|
||||
|
||||
private List<MediaFile> getMediaFiles(HttpServletRequest request) {
|
||||
List<MediaFile> mediaFiles = new ArrayList<MediaFile>();
|
||||
for (String path : ServletRequestUtils.getStringParameters(request, "path")) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(path);
|
||||
if (mediaFile != null) {
|
||||
mediaFiles.add(mediaFile);
|
||||
}
|
||||
}
|
||||
for (int id : ServletRequestUtils.getIntParameters(request, "id")) {
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(id);
|
||||
if (mediaFile != null) {
|
||||
mediaFiles.add(mediaFile);
|
||||
}
|
||||
}
|
||||
return mediaFiles;
|
||||
}
|
||||
|
||||
private String guessArtist(List<MediaFile> children) {
|
||||
for (MediaFile child : children) {
|
||||
if (child.isFile() && child.getArtist() != null) {
|
||||
return child.getArtist();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String guessAlbum(List<MediaFile> children) {
|
||||
for (MediaFile child : children) {
|
||||
if (child.isFile() && child.getArtist() != null) {
|
||||
return child.getAlbumName();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<MediaFile> getMultiFolderChildren(List<MediaFile> mediaFiles) throws IOException {
|
||||
SortedSet<MediaFile> result = new TreeSet<MediaFile>(new MediaFileComparator(settingsService.isSortAlbumsByYear()));
|
||||
for (MediaFile mediaFile : mediaFiles) {
|
||||
if (mediaFile.isFile()) {
|
||||
mediaFile = mediaFileService.getParentOf(mediaFile);
|
||||
}
|
||||
result.addAll(mediaFileService.getChildrenOf(mediaFile, true, true, true));
|
||||
}
|
||||
return new ArrayList<MediaFile>(result);
|
||||
}
|
||||
|
||||
private List<MediaFile> getAncestors(MediaFile dir) throws IOException {
|
||||
LinkedList<MediaFile> result = new LinkedList<MediaFile>();
|
||||
|
||||
try {
|
||||
MediaFile parent = mediaFileService.getParentOf(dir);
|
||||
while (parent != null && !mediaFileService.isRoot(parent)) {
|
||||
result.addFirst(parent);
|
||||
parent = mediaFileService.getParentOf(parent);
|
||||
}
|
||||
} catch (SecurityException x) {
|
||||
// Happens if Podcast directory is outside music folder.
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<MediaFile> getSieblingAlbums(MediaFile dir) {
|
||||
List<MediaFile> result = new ArrayList<MediaFile>();
|
||||
|
||||
MediaFile parent = mediaFileService.getParentOf(dir);
|
||||
if (!mediaFileService.isRoot(parent)) {
|
||||
List<MediaFile> sieblings = mediaFileService.getChildrenOf(parent, false, true, true);
|
||||
for (MediaFile siebling : sieblings) {
|
||||
if (siebling.isAlbum() && !siebling.equals(dir)) {
|
||||
result.add(siebling);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setRatingService(RatingService ratingService) {
|
||||
this.ratingService = ratingService;
|
||||
}
|
||||
|
||||
public void setAdService(AdService adService) {
|
||||
this.adService = adService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller for the "more" page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MoreController extends ParameterizableViewController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
private PlayerService playerService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
|
||||
String uploadDirectory = null;
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(user.getUsername());
|
||||
if (musicFolders.size() > 0) {
|
||||
uploadDirectory = new File(musicFolders.get(0).getPath(), "Incoming").getPath();
|
||||
}
|
||||
|
||||
|
||||
StringBuilder jamstashUrl = new StringBuilder("http://jamstash.com/#/settings?u=" + StringUtil.urlEncode(user.getUsername()) + "&url=");
|
||||
if (settingsService.isUrlRedirectionEnabled()) {
|
||||
jamstashUrl.append(StringUtil.urlEncode(settingsService.getUrlRedirectUrl()));
|
||||
} else {
|
||||
jamstashUrl.append(StringUtil.urlEncode(request.getRequestURL().toString().replaceAll("/more.view.*", "")));
|
||||
}
|
||||
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
map.put("user", user);
|
||||
map.put("uploadDirectory", uploadDirectory);
|
||||
map.put("genres", mediaFileService.getGenres(false));
|
||||
map.put("currentYear", Calendar.getInstance().get(Calendar.YEAR));
|
||||
map.put("musicFolders", musicFolders);
|
||||
map.put("clientSidePlaylist", player.isExternalWithPlaylist() || player.isWeb());
|
||||
map.put("brand", settingsService.getBrand());
|
||||
map.put("jamstashUrl", jamstashUrl);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.domain.Playlist;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.PlaylistService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
import net.tanesha.recaptcha.ReCaptcha;
|
||||
import net.tanesha.recaptcha.ReCaptchaFactory;
|
||||
import net.tanesha.recaptcha.ReCaptchaResponse;
|
||||
|
||||
/**
|
||||
* Multi-controller used for simple pages.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MultiController extends MultiActionController {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MultiController.class);
|
||||
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
private PlaylistService playlistService;
|
||||
|
||||
public ModelAndView login(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
// Auto-login if "user" and "password" parameters are given.
|
||||
String username = request.getParameter("user");
|
||||
String password = request.getParameter("password");
|
||||
if (username != null && password != null) {
|
||||
username = StringUtil.urlEncode(username);
|
||||
password = StringUtil.urlEncode(password);
|
||||
return new ModelAndView(new RedirectView("j_acegi_security_check?j_username=" + username +
|
||||
"&j_password=" + password + "&_acegi_security_remember_me=checked"));
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("logout", request.getParameter("logout") != null);
|
||||
map.put("error", request.getParameter("error") != null);
|
||||
map.put("brand", settingsService.getBrand());
|
||||
map.put("loginMessage", settingsService.getLoginMessage());
|
||||
|
||||
User admin = securityService.getUserByName(User.USERNAME_ADMIN);
|
||||
if (User.USERNAME_ADMIN.equals(admin.getPassword())) {
|
||||
map.put("insecure", true);
|
||||
}
|
||||
|
||||
return new ModelAndView("login", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView recover(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
String usernameOrEmail = StringUtils.trimToNull(request.getParameter("usernameOrEmail"));
|
||||
ReCaptcha captcha = ReCaptchaFactory.newSecureReCaptcha("6LcZ3OMSAAAAANkKMdFdaNopWu9iS03V-nLOuoiH",
|
||||
"6LcZ3OMSAAAAAPaFg89mEzs-Ft0fIu7wxfKtkwmQ", false);
|
||||
boolean showCaptcha = true;
|
||||
|
||||
if (usernameOrEmail != null) {
|
||||
|
||||
map.put("usernameOrEmail", usernameOrEmail);
|
||||
User user = getUserByUsernameOrEmail(usernameOrEmail);
|
||||
String challenge = request.getParameter("recaptcha_challenge_field");
|
||||
String uresponse = request.getParameter("recaptcha_response_field");
|
||||
ReCaptchaResponse captchaResponse = captcha.checkAnswer(request.getRemoteAddr(), challenge, uresponse);
|
||||
|
||||
if (!captchaResponse.isValid()) {
|
||||
map.put("error", "recover.error.invalidcaptcha");
|
||||
} else if (user == null) {
|
||||
map.put("error", "recover.error.usernotfound");
|
||||
} else if (user.getEmail() == null) {
|
||||
map.put("error", "recover.error.noemail");
|
||||
} else {
|
||||
String password = RandomStringUtils.randomAlphanumeric(8);
|
||||
if (emailPassword(password, user.getUsername(), user.getEmail())) {
|
||||
map.put("sentTo", user.getEmail());
|
||||
user.setLdapAuthenticated(false);
|
||||
user.setPassword(password);
|
||||
securityService.updateUser(user);
|
||||
showCaptcha = false;
|
||||
} else {
|
||||
map.put("error", "recover.error.sendfailed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showCaptcha) {
|
||||
map.put("captcha", captcha.createRecaptchaHtml(null, null));
|
||||
}
|
||||
|
||||
return new ModelAndView("recover", "model", map);
|
||||
}
|
||||
|
||||
private boolean emailPassword(String password, String username, String email) {
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
try {
|
||||
HttpConnectionParams.setConnectionTimeout(client.getParams(), 10000);
|
||||
HttpConnectionParams.setSoTimeout(client.getParams(), 10000);
|
||||
HttpPost method = new HttpPost("http://libresonic.org/backend/sendMail.view");
|
||||
|
||||
List<NameValuePair> params = new ArrayList<NameValuePair>();
|
||||
params.add(new BasicNameValuePair("from", "noreply@libresonic.org"));
|
||||
params.add(new BasicNameValuePair("to", email));
|
||||
params.add(new BasicNameValuePair("subject", "Libresonic Password"));
|
||||
params.add(new BasicNameValuePair("text",
|
||||
"Hi there!\n\n" +
|
||||
"You have requested to reset your Libresonic password. Please find your new login details below.\n\n" +
|
||||
"Username: " + username + "\n" +
|
||||
"Password: " + password + "\n\n" +
|
||||
"--\n" +
|
||||
"The Libresonic Team\n" +
|
||||
"libresonic.org"));
|
||||
method.setEntity(new UrlEncodedFormEntity(params, StringUtil.ENCODING_UTF8));
|
||||
client.execute(method);
|
||||
return true;
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Failed to send email.", x);
|
||||
return false;
|
||||
} finally {
|
||||
client.getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private User getUserByUsernameOrEmail(String usernameOrEmail) {
|
||||
if (usernameOrEmail != null) {
|
||||
User user = securityService.getUserByName(usernameOrEmail);
|
||||
if (user != null) {
|
||||
return user;
|
||||
}
|
||||
return securityService.getUserByEmail(usernameOrEmail);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ModelAndView accessDenied(HttpServletRequest request, HttpServletResponse response) {
|
||||
return new ModelAndView("accessDenied");
|
||||
}
|
||||
|
||||
public ModelAndView notFound(HttpServletRequest request, HttpServletResponse response) {
|
||||
return new ModelAndView("notFound");
|
||||
}
|
||||
|
||||
public ModelAndView gettingStarted(HttpServletRequest request, HttpServletResponse response) {
|
||||
updatePortAndContextPath(request);
|
||||
|
||||
if (request.getParameter("hide") != null) {
|
||||
settingsService.setGettingStartedEnabled(false);
|
||||
settingsService.save();
|
||||
return new ModelAndView(new RedirectView("home.view"));
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("runningAsRoot", "root".equals(System.getProperty("user.name")));
|
||||
return new ModelAndView("gettingStarted", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView index(HttpServletRequest request, HttpServletResponse response) {
|
||||
updatePortAndContextPath(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(securityService.getCurrentUsername(request));
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("showRight", userSettings.isShowNowPlayingEnabled() || userSettings.isShowChatEnabled());
|
||||
map.put("autoHidePlayQueue", userSettings.isAutoHidePlayQueue());
|
||||
map.put("showSideBar", userSettings.isShowSideBar());
|
||||
map.put("brand", settingsService.getBrand());
|
||||
return new ModelAndView("index", "model", map);
|
||||
}
|
||||
|
||||
public ModelAndView exportPlaylist(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
Playlist playlist = playlistService.getPlaylist(id);
|
||||
if (!playlistService.isReadAllowed(playlist, securityService.getCurrentUsername(request))) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return null;
|
||||
|
||||
}
|
||||
response.setContentType("application/x-download");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + StringUtil.fileSystemSafe(playlist.getName()) + ".m3u8\"");
|
||||
|
||||
playlistService.exportPlaylist(id, response.getOutputStream());
|
||||
return null;
|
||||
}
|
||||
|
||||
private void updatePortAndContextPath(HttpServletRequest request) {
|
||||
|
||||
int port = Integer.parseInt(System.getProperty("libresonic.port", String.valueOf(request.getLocalPort())));
|
||||
int httpsPort = Integer.parseInt(System.getProperty("libresonic.httpsPort", "0"));
|
||||
|
||||
String contextPath = request.getContextPath().replace("/", "");
|
||||
|
||||
if (settingsService.getPort() != port) {
|
||||
settingsService.setPort(port);
|
||||
settingsService.save();
|
||||
}
|
||||
if (settingsService.getHttpsPort() != httpsPort) {
|
||||
settingsService.setHttpsPort(httpsPort);
|
||||
settingsService.save();
|
||||
}
|
||||
if (!ObjectUtils.equals(settingsService.getUrlRedirectContextPath(), contextPath)) {
|
||||
settingsService.setUrlRedirectContextPath(contextPath);
|
||||
settingsService.save();
|
||||
}
|
||||
}
|
||||
|
||||
public ModelAndView test(HttpServletRequest request, HttpServletResponse response) {
|
||||
return new ModelAndView("test");
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.libresonic.player.command.MusicFolderSettingsCommand;
|
||||
import org.libresonic.player.dao.AlbumDao;
|
||||
import org.libresonic.player.dao.ArtistDao;
|
||||
import org.libresonic.player.dao.MediaFileDao;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.service.MediaScannerService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate the set of music folders.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class MusicFolderSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private MediaScannerService mediaScannerService;
|
||||
private ArtistDao artistDao;
|
||||
private AlbumDao albumDao;
|
||||
private MediaFileDao mediaFileDao;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
MusicFolderSettingsCommand command = new MusicFolderSettingsCommand();
|
||||
|
||||
if (request.getParameter("scanNow") != null) {
|
||||
settingsService.clearMusicFolderCache();
|
||||
mediaScannerService.scanLibrary();
|
||||
}
|
||||
if (request.getParameter("expunge") != null) {
|
||||
expunge();
|
||||
}
|
||||
|
||||
command.setInterval(String.valueOf(settingsService.getIndexCreationInterval()));
|
||||
command.setHour(String.valueOf(settingsService.getIndexCreationHour()));
|
||||
command.setFastCache(settingsService.isFastCacheEnabled());
|
||||
command.setOrganizeByFolderStructure(settingsService.isOrganizeByFolderStructure());
|
||||
command.setScanning(mediaScannerService.isScanning());
|
||||
command.setMusicFolders(wrap(settingsService.getAllMusicFolders(true, true)));
|
||||
command.setNewMusicFolder(new MusicFolderSettingsCommand.MusicFolderInfo());
|
||||
command.setReload(request.getParameter("reload") != null || request.getParameter("scanNow") != null);
|
||||
return command;
|
||||
}
|
||||
|
||||
private void expunge() {
|
||||
artistDao.expunge();
|
||||
albumDao.expunge();
|
||||
mediaFileDao.expunge();
|
||||
}
|
||||
|
||||
private List<MusicFolderSettingsCommand.MusicFolderInfo> wrap(List<MusicFolder> musicFolders) {
|
||||
ArrayList<MusicFolderSettingsCommand.MusicFolderInfo> result = new ArrayList<MusicFolderSettingsCommand.MusicFolderInfo>();
|
||||
for (MusicFolder musicFolder : musicFolders) {
|
||||
result.add(new MusicFolderSettingsCommand.MusicFolderInfo(musicFolder));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView onSubmit(Object comm) throws Exception {
|
||||
MusicFolderSettingsCommand command = (MusicFolderSettingsCommand) comm;
|
||||
|
||||
for (MusicFolderSettingsCommand.MusicFolderInfo musicFolderInfo : command.getMusicFolders()) {
|
||||
if (musicFolderInfo.isDelete()) {
|
||||
settingsService.deleteMusicFolder(musicFolderInfo.getId());
|
||||
} else {
|
||||
MusicFolder musicFolder = musicFolderInfo.toMusicFolder();
|
||||
if (musicFolder != null) {
|
||||
settingsService.updateMusicFolder(musicFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MusicFolder newMusicFolder = command.getNewMusicFolder().toMusicFolder();
|
||||
if (newMusicFolder != null) {
|
||||
settingsService.createMusicFolder(newMusicFolder);
|
||||
}
|
||||
|
||||
settingsService.setIndexCreationInterval(Integer.parseInt(command.getInterval()));
|
||||
settingsService.setIndexCreationHour(Integer.parseInt(command.getHour()));
|
||||
settingsService.setFastCacheEnabled(command.isFastCache());
|
||||
settingsService.setOrganizeByFolderStructure(command.isOrganizeByFolderStructure());
|
||||
settingsService.save();
|
||||
|
||||
mediaScannerService.schedule();
|
||||
return new ModelAndView(new RedirectView(getSuccessView() + ".view?reload"));
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setMediaScannerService(MediaScannerService mediaScannerService) {
|
||||
this.mediaScannerService = mediaScannerService;
|
||||
}
|
||||
|
||||
public void setArtistDao(ArtistDao artistDao) {
|
||||
this.artistDao = artistDao;
|
||||
}
|
||||
|
||||
public void setAlbumDao(AlbumDao albumDao) {
|
||||
this.albumDao = albumDao;
|
||||
}
|
||||
|
||||
public void setMediaFileDao(MediaFileDao mediaFileDao) {
|
||||
this.mediaFileDao = mediaFileDao;
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import org.libresonic.player.command.NetworkSettingsCommand;
|
||||
import org.libresonic.player.domain.UrlRedirectType;
|
||||
import org.libresonic.player.service.NetworkService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to change the network settings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class NetworkSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private NetworkService networkService;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
NetworkSettingsCommand command = new NetworkSettingsCommand();
|
||||
command.setPortForwardingEnabled(settingsService.isPortForwardingEnabled());
|
||||
command.setUrlRedirectionEnabled(settingsService.isUrlRedirectionEnabled());
|
||||
command.setUrlRedirectType(settingsService.getUrlRedirectType().name());
|
||||
command.setUrlRedirectFrom(settingsService.getUrlRedirectFrom());
|
||||
command.setUrlRedirectCustomUrl(settingsService.getUrlRedirectCustomUrl());
|
||||
command.setPort(settingsService.getPort());
|
||||
command.setLicenseInfo(settingsService.getLicenseInfo());
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
protected void doSubmitAction(Object cmd) throws Exception {
|
||||
NetworkSettingsCommand command = (NetworkSettingsCommand) cmd;
|
||||
command.setToast(true);
|
||||
|
||||
settingsService.setPortForwardingEnabled(command.isPortForwardingEnabled());
|
||||
settingsService.setUrlRedirectionEnabled(command.isUrlRedirectionEnabled());
|
||||
settingsService.setUrlRedirectType(UrlRedirectType.valueOf(command.getUrlRedirectType()));
|
||||
settingsService.setUrlRedirectFrom(StringUtils.lowerCase(command.getUrlRedirectFrom()));
|
||||
settingsService.setUrlRedirectCustomUrl(StringUtils.trimToEmpty(command.getUrlRedirectCustomUrl()));
|
||||
|
||||
if (settingsService.getServerId() == null) {
|
||||
Random rand = new Random(System.currentTimeMillis());
|
||||
settingsService.setServerId(String.valueOf(Math.abs(rand.nextLong())));
|
||||
}
|
||||
|
||||
settingsService.save();
|
||||
networkService.initPortForwarding(0);
|
||||
networkService.initUrlRedirection(true);
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setNetworkService(NetworkService networkService) {
|
||||
this.networkService = networkService;
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.TransferStatus;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.StatusService;
|
||||
|
||||
/**
|
||||
* Controller for showing what's currently playing.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class NowPlayingController extends AbstractController {
|
||||
|
||||
private PlayerService playerService;
|
||||
private StatusService statusService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
List<TransferStatus> statuses = statusService.getStreamStatusesForPlayer(player);
|
||||
|
||||
MediaFile current = statuses.isEmpty() ? null : mediaFileService.getMediaFile(statuses.get(0).getFile());
|
||||
MediaFile dir = current == null ? null : mediaFileService.getParentOf(current);
|
||||
|
||||
String url;
|
||||
if (dir != null && !mediaFileService.isRoot(dir)) {
|
||||
url = "main.view?id=" + dir.getId();
|
||||
} else {
|
||||
url = "home.view";
|
||||
}
|
||||
|
||||
return new ModelAndView(new RedirectView(url));
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setStatusService(StatusService statusService) {
|
||||
this.statusService = statusService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.springframework.web.servlet.mvc.*;
|
||||
import org.libresonic.player.service.*;
|
||||
import org.libresonic.player.command.*;
|
||||
import org.libresonic.player.domain.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/**
|
||||
* Controller for the page used to change password.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PasswordSettingsController extends SimpleFormController {
|
||||
|
||||
private SecurityService securityService;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
PasswordSettingsCommand command = new PasswordSettingsCommand();
|
||||
User user = securityService.getCurrentUser(request);
|
||||
command.setUsername(user.getUsername());
|
||||
command.setLdapAuthenticated(user.isLdapAuthenticated());
|
||||
return command;
|
||||
}
|
||||
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
PasswordSettingsCommand command = (PasswordSettingsCommand) comm;
|
||||
User user = securityService.getUserByName(command.getUsername());
|
||||
user.setPassword(command.getPassword());
|
||||
securityService.updateUser(user);
|
||||
|
||||
command.setPassword(null);
|
||||
command.setConfirmPassword(null);
|
||||
command.setToast(true);
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import org.libresonic.player.command.PersonalSettingsCommand;
|
||||
import org.libresonic.player.domain.AlbumListType;
|
||||
import org.libresonic.player.domain.AvatarScheme;
|
||||
import org.libresonic.player.domain.Theme;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate per-user settings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PersonalSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
|
||||
@Override
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
PersonalSettingsCommand command = new PersonalSettingsCommand();
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(user.getUsername());
|
||||
|
||||
command.setUser(user);
|
||||
command.setLocaleIndex("-1");
|
||||
command.setThemeIndex("-1");
|
||||
command.setAlbumLists(AlbumListType.values());
|
||||
command.setAlbumListId(userSettings.getDefaultAlbumList().getId());
|
||||
command.setAvatars(settingsService.getAllSystemAvatars());
|
||||
command.setCustomAvatar(settingsService.getCustomAvatar(user.getUsername()));
|
||||
command.setAvatarId(getAvatarId(userSettings));
|
||||
command.setPartyModeEnabled(userSettings.isPartyModeEnabled());
|
||||
command.setQueueFollowingSongs(userSettings.isQueueFollowingSongs());
|
||||
command.setShowNowPlayingEnabled(userSettings.isShowNowPlayingEnabled());
|
||||
command.setShowChatEnabled(userSettings.isShowChatEnabled());
|
||||
command.setShowArtistInfoEnabled(userSettings.isShowArtistInfoEnabled());
|
||||
command.setNowPlayingAllowed(userSettings.isNowPlayingAllowed());
|
||||
command.setMainVisibility(userSettings.getMainVisibility());
|
||||
command.setPlaylistVisibility(userSettings.getPlaylistVisibility());
|
||||
command.setFinalVersionNotificationEnabled(userSettings.isFinalVersionNotificationEnabled());
|
||||
command.setBetaVersionNotificationEnabled(userSettings.isBetaVersionNotificationEnabled());
|
||||
command.setSongNotificationEnabled(userSettings.isSongNotificationEnabled());
|
||||
command.setAutoHidePlayQueue(userSettings.isAutoHidePlayQueue());
|
||||
command.setLastFmEnabled(userSettings.isLastFmEnabled());
|
||||
command.setLastFmUsername(userSettings.getLastFmUsername());
|
||||
command.setLastFmPassword(userSettings.getLastFmPassword());
|
||||
|
||||
Locale currentLocale = userSettings.getLocale();
|
||||
Locale[] locales = settingsService.getAvailableLocales();
|
||||
String[] localeStrings = new String[locales.length];
|
||||
for (int i = 0; i < locales.length; i++) {
|
||||
localeStrings[i] = locales[i].getDisplayName(locales[i]);
|
||||
if (locales[i].equals(currentLocale)) {
|
||||
command.setLocaleIndex(String.valueOf(i));
|
||||
}
|
||||
}
|
||||
command.setLocales(localeStrings);
|
||||
|
||||
String currentThemeId = userSettings.getThemeId();
|
||||
Theme[] themes = settingsService.getAvailableThemes();
|
||||
command.setThemes(themes);
|
||||
for (int i = 0; i < themes.length; i++) {
|
||||
if (themes[i].getId().equals(currentThemeId)) {
|
||||
command.setThemeIndex(String.valueOf(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
PersonalSettingsCommand command = (PersonalSettingsCommand) comm;
|
||||
|
||||
int localeIndex = Integer.parseInt(command.getLocaleIndex());
|
||||
Locale locale = null;
|
||||
if (localeIndex != -1) {
|
||||
locale = settingsService.getAvailableLocales()[localeIndex];
|
||||
}
|
||||
|
||||
int themeIndex = Integer.parseInt(command.getThemeIndex());
|
||||
String themeId = null;
|
||||
if (themeIndex != -1) {
|
||||
themeId = settingsService.getAvailableThemes()[themeIndex].getId();
|
||||
}
|
||||
|
||||
String username = command.getUser().getUsername();
|
||||
UserSettings settings = settingsService.getUserSettings(username);
|
||||
|
||||
settings.setLocale(locale);
|
||||
settings.setThemeId(themeId);
|
||||
settings.setDefaultAlbumList(AlbumListType.fromId(command.getAlbumListId()));
|
||||
settings.setPartyModeEnabled(command.isPartyModeEnabled());
|
||||
settings.setQueueFollowingSongs(command.isQueueFollowingSongs());
|
||||
settings.setShowNowPlayingEnabled(command.isShowNowPlayingEnabled());
|
||||
settings.setShowChatEnabled(command.isShowChatEnabled());
|
||||
settings.setShowArtistInfoEnabled(command.isShowArtistInfoEnabled());
|
||||
settings.setNowPlayingAllowed(command.isNowPlayingAllowed());
|
||||
settings.setMainVisibility(command.getMainVisibility());
|
||||
settings.setPlaylistVisibility(command.getPlaylistVisibility());
|
||||
settings.setFinalVersionNotificationEnabled(command.isFinalVersionNotificationEnabled());
|
||||
settings.setBetaVersionNotificationEnabled(command.isBetaVersionNotificationEnabled());
|
||||
settings.setSongNotificationEnabled(command.isSongNotificationEnabled());
|
||||
settings.setAutoHidePlayQueue(command.isAutoHidePlayQueue());
|
||||
settings.setLastFmEnabled(command.isLastFmEnabled());
|
||||
settings.setLastFmUsername(command.getLastFmUsername());
|
||||
settings.setSystemAvatarId(getSystemAvatarId(command));
|
||||
settings.setAvatarScheme(getAvatarScheme(command));
|
||||
|
||||
if (StringUtils.isNotBlank(command.getLastFmPassword())) {
|
||||
settings.setLastFmPassword(command.getLastFmPassword());
|
||||
}
|
||||
|
||||
settings.setChanged(new Date());
|
||||
settingsService.updateUserSettings(settings);
|
||||
|
||||
command.setReloadNeeded(true);
|
||||
}
|
||||
|
||||
private int getAvatarId(UserSettings userSettings) {
|
||||
AvatarScheme avatarScheme = userSettings.getAvatarScheme();
|
||||
return avatarScheme == AvatarScheme.SYSTEM ? userSettings.getSystemAvatarId() : avatarScheme.getCode();
|
||||
}
|
||||
|
||||
private AvatarScheme getAvatarScheme(PersonalSettingsCommand command) {
|
||||
if (command.getAvatarId() == AvatarScheme.NONE.getCode()) {
|
||||
return AvatarScheme.NONE;
|
||||
}
|
||||
if (command.getAvatarId() == AvatarScheme.CUSTOM.getCode()) {
|
||||
return AvatarScheme.CUSTOM;
|
||||
}
|
||||
return AvatarScheme.SYSTEM;
|
||||
}
|
||||
|
||||
private Integer getSystemAvatarId(PersonalSettingsCommand command) {
|
||||
int avatarId = command.getAvatarId();
|
||||
if (avatarId == AvatarScheme.NONE.getCode() ||
|
||||
avatarId == AvatarScheme.CUSTOM.getCode()) {
|
||||
return null;
|
||||
}
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the playlist frame.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlayQueueController extends ParameterizableViewController {
|
||||
|
||||
private PlayerService playerService;
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(user.getUsername());
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("user", user);
|
||||
map.put("player", player);
|
||||
map.put("players", playerService.getPlayersForUserAndClientId(user.getUsername(), null));
|
||||
map.put("visibility", userSettings.getPlaylistVisibility());
|
||||
map.put("partyMode", userSettings.isPartyModeEnabled());
|
||||
map.put("notify", userSettings.isSongNotificationEnabled());
|
||||
map.put("autoHide", userSettings.isAutoHidePlayQueue());
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import org.libresonic.player.command.PlayerSettingsCommand;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.PlayerTechnology;
|
||||
import org.libresonic.player.domain.TranscodeScheme;
|
||||
import org.libresonic.player.domain.Transcoding;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.TranscodingService;
|
||||
|
||||
/**
|
||||
* Controller for the player settings page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlayerSettingsController extends SimpleFormController {
|
||||
|
||||
private PlayerService playerService;
|
||||
private SecurityService securityService;
|
||||
private TranscodingService transcodingService;
|
||||
|
||||
@Override
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
|
||||
handleRequestParameters(request);
|
||||
List<Player> players = getPlayers(request);
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
PlayerSettingsCommand command = new PlayerSettingsCommand();
|
||||
Player player = null;
|
||||
String playerId = request.getParameter("id");
|
||||
if (playerId != null) {
|
||||
player = playerService.getPlayerById(playerId);
|
||||
} else if (!players.isEmpty()) {
|
||||
player = players.get(0);
|
||||
}
|
||||
|
||||
if (player != null) {
|
||||
command.setPlayerId(player.getId());
|
||||
command.setName(player.getName());
|
||||
command.setDescription(player.toString());
|
||||
command.setType(player.getType());
|
||||
command.setLastSeen(player.getLastSeen());
|
||||
command.setDynamicIp(player.isDynamicIp());
|
||||
command.setAutoControlEnabled(player.isAutoControlEnabled());
|
||||
command.setTranscodeSchemeName(player.getTranscodeScheme().name());
|
||||
command.setTechnologyName(player.getTechnology().name());
|
||||
command.setAllTranscodings(transcodingService.getAllTranscodings());
|
||||
List<Transcoding> activeTranscodings = transcodingService.getTranscodingsForPlayer(player);
|
||||
int[] activeTranscodingIds = new int[activeTranscodings.size()];
|
||||
for (int i = 0; i < activeTranscodings.size(); i++) {
|
||||
activeTranscodingIds[i] = activeTranscodings.get(i).getId();
|
||||
}
|
||||
command.setActiveTranscodingIds(activeTranscodingIds);
|
||||
}
|
||||
|
||||
command.setTranscodingSupported(transcodingService.isDownsamplingSupported(null));
|
||||
command.setTranscodeDirectory(transcodingService.getTranscodeDirectory().getPath());
|
||||
command.setTranscodeSchemes(TranscodeScheme.values());
|
||||
command.setTechnologies(PlayerTechnology.values());
|
||||
command.setPlayers(players.toArray(new Player[players.size()]));
|
||||
command.setAdmin(user.isAdminRole());
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
PlayerSettingsCommand command = (PlayerSettingsCommand) comm;
|
||||
Player player = playerService.getPlayerById(command.getPlayerId());
|
||||
|
||||
player.setAutoControlEnabled(command.isAutoControlEnabled());
|
||||
player.setDynamicIp(command.isDynamicIp());
|
||||
player.setName(StringUtils.trimToNull(command.getName()));
|
||||
player.setTranscodeScheme(TranscodeScheme.valueOf(command.getTranscodeSchemeName()));
|
||||
player.setTechnology(PlayerTechnology.valueOf(command.getTechnologyName()));
|
||||
|
||||
playerService.updatePlayer(player);
|
||||
transcodingService.setTranscodingsForPlayer(player, command.getActiveTranscodingIds());
|
||||
|
||||
command.setReloadNeeded(true);
|
||||
}
|
||||
|
||||
private List<Player> getPlayers(HttpServletRequest request) {
|
||||
User user = securityService.getCurrentUser(request);
|
||||
String username = user.getUsername();
|
||||
List<Player> players = playerService.getAllPlayers();
|
||||
List<Player> authorizedPlayers = new ArrayList<Player>();
|
||||
|
||||
for (Player player : players) {
|
||||
// Only display authorized players.
|
||||
if (user.isAdminRole() || username.equals(player.getUsername())) {
|
||||
authorizedPlayers.add(player);
|
||||
}
|
||||
}
|
||||
return authorizedPlayers;
|
||||
}
|
||||
|
||||
private void handleRequestParameters(HttpServletRequest request) {
|
||||
if (request.getParameter("delete") != null) {
|
||||
playerService.removePlayerById(request.getParameter("delete"));
|
||||
} else if (request.getParameter("clone") != null) {
|
||||
playerService.clonePlayer(request.getParameter("clone"));
|
||||
}
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setTranscodingService(TranscodingService transcodingService) {
|
||||
this.transcodingService = transcodingService;
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.Playlist;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.PlaylistService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for the playlist page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlaylistController extends ParameterizableViewController {
|
||||
|
||||
private SecurityService securityService;
|
||||
private PlaylistService playlistService;
|
||||
private SettingsService settingsService;
|
||||
private PlayerService playerService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
User user = securityService.getCurrentUser(request);
|
||||
String username = user.getUsername();
|
||||
UserSettings userSettings = settingsService.getUserSettings(username);
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
Playlist playlist = playlistService.getPlaylist(id);
|
||||
if (playlist == null) {
|
||||
return new ModelAndView(new RedirectView("notFound.view"));
|
||||
}
|
||||
|
||||
map.put("playlist", playlist);
|
||||
map.put("user", user);
|
||||
map.put("player", player);
|
||||
map.put("editAllowed", username.equals(playlist.getUsername()) || securityService.isAdmin(username));
|
||||
map.put("partyMode", userSettings.isPartyModeEnabled());
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of Libresonic.
|
||||
*
|
||||
* Libresonic 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.
|
||||
*
|
||||
* Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2014 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.Playlist;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.PlaylistService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the playlists page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlaylistsController extends ParameterizableViewController {
|
||||
|
||||
private SecurityService securityService;
|
||||
private PlaylistService playlistService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
List<Playlist> playlists = playlistService.getReadablePlaylistsForUser(user.getUsername());
|
||||
|
||||
map.put("playlists", playlists);
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of Libresonic.
|
||||
*
|
||||
* Libresonic 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.
|
||||
*
|
||||
* Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2015 (C) Sindre Mehus
|
||||
*/
|
||||
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.service.PodcastService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
|
||||
/**
|
||||
* Controller for the "Podcast channel" page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastChannelController extends ParameterizableViewController {
|
||||
|
||||
private PodcastService podcastService;
|
||||
private SecurityService securityService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
|
||||
int channelId = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("channel", podcastService.getChannel(channelId));
|
||||
map.put("episodes", podcastService.getEpisodes(channelId));
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This file is part of Libresonic.
|
||||
*
|
||||
* Libresonic 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.
|
||||
*
|
||||
* Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2015 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.domain.PodcastChannel;
|
||||
import org.libresonic.player.domain.PodcastEpisode;
|
||||
import org.libresonic.player.service.PodcastService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the "Podcast channels" page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastChannelsController extends ParameterizableViewController {
|
||||
|
||||
private PodcastService podcastService;
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
|
||||
Map<PodcastChannel, List<PodcastEpisode>> channels = new LinkedHashMap<PodcastChannel, List<PodcastEpisode>>();
|
||||
Map<Integer, PodcastChannel> channelMap = new HashMap<Integer, PodcastChannel>();
|
||||
for (PodcastChannel channel : podcastService.getAllChannels()) {
|
||||
channels.put(channel, podcastService.getEpisodes(channel.getId()));
|
||||
channelMap.put(channel.getId(), channel);
|
||||
}
|
||||
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("channels", channels);
|
||||
map.put("channelMap", channelMap);
|
||||
map.put("newestEpisodes", podcastService.getNewestEpisodes(10));
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.Playlist;
|
||||
import org.libresonic.player.service.PlaylistService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller for the page used to generate the Podcast XML file.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastController extends ParameterizableViewController {
|
||||
|
||||
private static final DateFormat RSS_DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
|
||||
private PlaylistService playlistService;
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
String url = request.getRequestURL().toString();
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
List<Playlist> playlists = playlistService.getReadablePlaylistsForUser(username);
|
||||
List<Podcast> podcasts = new ArrayList<Podcast>();
|
||||
|
||||
for (Playlist playlist : playlists) {
|
||||
|
||||
List<MediaFile> songs = playlistService.getFilesInPlaylist(playlist.getId());
|
||||
if (songs.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
long length = 0L;
|
||||
for (MediaFile song : songs) {
|
||||
length += song.getFileSize();
|
||||
}
|
||||
String publishDate = RSS_DATE_FORMAT.format(playlist.getCreated());
|
||||
|
||||
// Resolve content type.
|
||||
String suffix = songs.get(0).getFormat();
|
||||
String type = StringUtil.getMimeType(suffix);
|
||||
|
||||
// Rewrite URLs in case we're behind a proxy.
|
||||
if (settingsService.isRewriteUrlEnabled()) {
|
||||
String referer = request.getHeader("referer");
|
||||
url = StringUtil.rewriteUrl(url, referer);
|
||||
}
|
||||
|
||||
String enclosureUrl = url.replaceFirst("/podcast.*", "/stream?playlist=" + playlist.getId());
|
||||
enclosureUrl = settingsService.rewriteRemoteUrl(enclosureUrl);
|
||||
|
||||
podcasts.add(new Podcast(playlist.getName(), publishDate, enclosureUrl, length, type));
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
map.put("url", url);
|
||||
map.put("podcasts", podcasts);
|
||||
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPlaylistService(PlaylistService playlistService) {
|
||||
this.playlistService = playlistService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains information about a single Podcast.
|
||||
*/
|
||||
public static class Podcast {
|
||||
private String name;
|
||||
private String publishDate;
|
||||
private String enclosureUrl;
|
||||
private long length;
|
||||
private String type;
|
||||
|
||||
public Podcast(String name, String publishDate, String enclosureUrl, long length, String type) {
|
||||
this.name = name;
|
||||
this.publishDate = publishDate;
|
||||
this.enclosureUrl = enclosureUrl;
|
||||
this.length = length;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getPublishDate() {
|
||||
return publishDate;
|
||||
}
|
||||
|
||||
public String getEnclosureUrl() {
|
||||
return enclosureUrl;
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.libresonic.player.domain.PodcastEpisode;
|
||||
import org.libresonic.player.domain.PodcastStatus;
|
||||
import org.libresonic.player.service.PodcastService;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Controller for the "Podcast receiver" page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastReceiverAdminController extends AbstractController {
|
||||
|
||||
private PodcastService podcastService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Integer channelId = ServletRequestUtils.getIntParameter(request, "channelId");
|
||||
|
||||
if (request.getParameter("add") != null) {
|
||||
String url = StringUtils.trim(request.getParameter("add"));
|
||||
podcastService.createChannel(url);
|
||||
return new ModelAndView(new RedirectView("podcastChannels.view"));
|
||||
}
|
||||
if (request.getParameter("downloadEpisode") != null) {
|
||||
download(StringUtil.parseInts(request.getParameter("downloadEpisode")));
|
||||
return new ModelAndView(new RedirectView("podcastChannel.view?id=" + channelId));
|
||||
}
|
||||
if (request.getParameter("deleteChannel") != null) {
|
||||
podcastService.deleteChannel(channelId);
|
||||
return new ModelAndView(new RedirectView("podcastChannels.view"));
|
||||
}
|
||||
if (request.getParameter("deleteEpisode") != null) {
|
||||
for (int episodeId : StringUtil.parseInts(request.getParameter("deleteEpisode"))) {
|
||||
podcastService.deleteEpisode(episodeId, true);
|
||||
}
|
||||
return new ModelAndView(new RedirectView("podcastChannel.view?id=" + channelId));
|
||||
}
|
||||
if (request.getParameter("refresh") != null) {
|
||||
if (channelId != null) {
|
||||
podcastService.refreshChannel(channelId, true);
|
||||
return new ModelAndView(new RedirectView("podcastChannel.view?id=" + channelId));
|
||||
} else {
|
||||
podcastService.refreshAllChannels(true);
|
||||
return new ModelAndView(new RedirectView("podcastChannels.view"));
|
||||
}
|
||||
}
|
||||
|
||||
return new ModelAndView(new RedirectView("podcastChannels.view"));
|
||||
}
|
||||
|
||||
private void download(int[] episodeIds) {
|
||||
for (Integer episodeId : episodeIds) {
|
||||
PodcastEpisode episode = podcastService.getEpisode(episodeId, false);
|
||||
if (episode != null && episode.getUrl() != null &&
|
||||
(episode.getStatus() == PodcastStatus.NEW ||
|
||||
episode.getStatus() == PodcastStatus.ERROR ||
|
||||
episode.getStatus() == PodcastStatus.SKIPPED)) {
|
||||
|
||||
podcastService.downloadEpisode(episode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.service.PodcastService;
|
||||
import org.libresonic.player.command.PodcastSettingsCommand;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Controller for the page used to administrate the Podcast receiver.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private PodcastService podcastService;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
PodcastSettingsCommand command = new PodcastSettingsCommand();
|
||||
|
||||
command.setInterval(String.valueOf(settingsService.getPodcastUpdateInterval()));
|
||||
command.setEpisodeRetentionCount(String.valueOf(settingsService.getPodcastEpisodeRetentionCount()));
|
||||
command.setEpisodeDownloadCount(String.valueOf(settingsService.getPodcastEpisodeDownloadCount()));
|
||||
command.setFolder(settingsService.getPodcastFolder());
|
||||
return command;
|
||||
}
|
||||
|
||||
protected void doSubmitAction(Object comm) throws Exception {
|
||||
PodcastSettingsCommand command = (PodcastSettingsCommand) comm;
|
||||
command.setToast(true);
|
||||
|
||||
settingsService.setPodcastUpdateInterval(Integer.parseInt(command.getInterval()));
|
||||
settingsService.setPodcastEpisodeRetentionCount(Integer.parseInt(command.getEpisodeRetentionCount()));
|
||||
settingsService.setPodcastEpisodeDownloadCount(Integer.parseInt(command.getEpisodeDownloadCount()));
|
||||
settingsService.setPodcastFolder(command.getFolder());
|
||||
settingsService.save();
|
||||
|
||||
podcastService.schedule();
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPodcastService(PodcastService podcastService) {
|
||||
this.podcastService = podcastService;
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import org.libresonic.player.command.PremiumSettingsCommand;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the Subsonic Premium page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PremiumSettingsController extends SimpleFormController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
PremiumSettingsCommand command = new PremiumSettingsCommand();
|
||||
command.setPath(request.getParameter("path"));
|
||||
command.setForceChange(request.getParameter("change") != null);
|
||||
command.setLicenseInfo(settingsService.getLicenseInfo());
|
||||
command.setBrand(settingsService.getBrand());
|
||||
command.setUser(securityService.getCurrentUser(request));
|
||||
return command;
|
||||
}
|
||||
|
||||
protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object com, BindException errors)
|
||||
throws Exception {
|
||||
PremiumSettingsCommand command = (PremiumSettingsCommand) com;
|
||||
Date now = new Date();
|
||||
|
||||
settingsService.setLicenseCode(command.getLicenseCode());
|
||||
settingsService.setLicenseEmail(command.getLicenseInfo().getLicenseEmail());
|
||||
settingsService.setLicenseDate(now);
|
||||
settingsService.save();
|
||||
settingsService.scheduleLicenseValidation();
|
||||
|
||||
// Reflect changes in view. The validator will validate the license asynchronously.
|
||||
command.setLicenseInfo(settingsService.getLicenseInfo());
|
||||
command.setToast(true);
|
||||
|
||||
return new ModelAndView(getSuccessView(), errors.getModel());
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
|
||||
/**
|
||||
* A proxy for external HTTP requests.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ProxyController implements Controller {
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String url = ServletRequestUtils.getRequiredStringParameter(request, "url");
|
||||
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
HttpConnectionParams.setConnectionTimeout(client.getParams(), 15000);
|
||||
HttpConnectionParams.setSoTimeout(client.getParams(), 15000);
|
||||
HttpGet method = new HttpGet(url);
|
||||
|
||||
InputStream in = null;
|
||||
try {
|
||||
HttpResponse resp = client.execute(method);
|
||||
int statusCode = resp.getStatusLine().getStatusCode();
|
||||
if (statusCode != HttpStatus.SC_OK) {
|
||||
response.sendError(statusCode);
|
||||
} else {
|
||||
in = resp.getEntity().getContent();
|
||||
IOUtils.copy(in, response.getOutputStream());
|
||||
}
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
client.getConnectionManager().shutdown();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.PlayQueue;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.RandomSearchCriteria;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SearchService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the creating a random play queue.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class RandomPlayQueueController extends ParameterizableViewController {
|
||||
|
||||
private PlayerService playerService;
|
||||
private List<ReloadFrame> reloadFrames;
|
||||
private SearchService searchService;
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
int size = ServletRequestUtils.getRequiredIntParameter(request, "size");
|
||||
String genre = request.getParameter("genre");
|
||||
if (StringUtils.equalsIgnoreCase("any", genre)) {
|
||||
genre = null;
|
||||
}
|
||||
|
||||
Integer fromYear = null;
|
||||
Integer toYear = null;
|
||||
|
||||
String year = request.getParameter("year");
|
||||
if (!StringUtils.equalsIgnoreCase("any", year)) {
|
||||
String[] tmp = StringUtils.split(year);
|
||||
fromYear = Integer.parseInt(tmp[0]);
|
||||
toYear = Integer.parseInt(tmp[1]);
|
||||
}
|
||||
|
||||
List<MusicFolder> musicFolders = getMusicFolders(request);
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
|
||||
RandomSearchCriteria criteria = new RandomSearchCriteria(size, genre, fromYear, toYear, musicFolders);
|
||||
playQueue.addFiles(false, searchService.getRandomSongs(criteria));
|
||||
|
||||
if (request.getParameter("autoRandom") != null) {
|
||||
playQueue.setRandomSearchCriteria(criteria);
|
||||
}
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("reloadFrames", reloadFrames);
|
||||
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<MusicFolder> getMusicFolders(HttpServletRequest request) throws ServletRequestBindingException {
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
Integer selectedMusicFolderId = ServletRequestUtils.getRequiredIntParameter(request, "musicFolderId");
|
||||
if (selectedMusicFolderId == -1) {
|
||||
selectedMusicFolderId = null;
|
||||
}
|
||||
return settingsService.getMusicFoldersForUser(username, selectedMusicFolderId);
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setReloadFrames(List<ReloadFrame> reloadFrames) {
|
||||
this.reloadFrames = reloadFrames;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
/**
|
||||
* Used in libresonic-servlet.xml to specify frame reloading.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class ReloadFrame {
|
||||
private String frame;
|
||||
private String view;
|
||||
|
||||
public ReloadFrame() {}
|
||||
|
||||
public ReloadFrame(String frame, String view) {
|
||||
this.frame = frame;
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public String getFrame() {
|
||||
return frame;
|
||||
}
|
||||
|
||||
public void setFrame(String frame) {
|
||||
this.frame = frame;
|
||||
}
|
||||
|
||||
public String getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
public void setView(String view) {
|
||||
this.view = view;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.VersionService;
|
||||
|
||||
/**
|
||||
* Controller for the right frame.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class RightController extends ParameterizableViewController {
|
||||
|
||||
private SettingsService settingsService;
|
||||
private SecurityService securityService;
|
||||
private VersionService versionService;
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
ModelAndView result = super.handleRequestInternal(request, response);
|
||||
|
||||
UserSettings userSettings = settingsService.getUserSettings(securityService.getCurrentUsername(request));
|
||||
if (userSettings.isFinalVersionNotificationEnabled() && versionService.isNewFinalVersionAvailable()) {
|
||||
map.put("newVersionAvailable", true);
|
||||
map.put("latestVersion", versionService.getLatestFinalVersion());
|
||||
|
||||
} else if (userSettings.isBetaVersionNotificationEnabled() && versionService.isNewBetaVersionAvailable()) {
|
||||
map.put("newVersionAvailable", true);
|
||||
map.put("latestVersion", versionService.getLatestBetaVersion());
|
||||
}
|
||||
|
||||
map.put("brand", settingsService.getBrand());
|
||||
map.put("showNowPlaying", userSettings.isShowNowPlayingEnabled());
|
||||
map.put("showChat", userSettings.isShowChatEnabled());
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("licenseInfo", settingsService.getLicenseInfo());
|
||||
|
||||
result.addObject("model", map);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setVersionService(VersionService versionService) {
|
||||
this.versionService = versionService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
||||
import org.libresonic.player.command.SearchCommand;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.SearchCriteria;
|
||||
import org.libresonic.player.domain.SearchResult;
|
||||
import org.libresonic.player.domain.User;
|
||||
import org.libresonic.player.domain.UserSettings;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SearchService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the search page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SearchController extends SimpleFormController {
|
||||
|
||||
private static final int MATCH_COUNT = 25;
|
||||
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
private PlayerService playerService;
|
||||
private SearchService searchService;
|
||||
|
||||
@Override
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
return new SearchCommand();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object com, BindException errors)
|
||||
throws Exception {
|
||||
SearchCommand command = (SearchCommand) com;
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
UserSettings userSettings = settingsService.getUserSettings(user.getUsername());
|
||||
command.setUser(user);
|
||||
command.setPartyModeEnabled(userSettings.isPartyModeEnabled());
|
||||
|
||||
List<MusicFolder> musicFolders = settingsService.getMusicFoldersForUser(user.getUsername());
|
||||
String query = StringUtils.trimToNull(command.getQuery());
|
||||
|
||||
if (query != null) {
|
||||
|
||||
SearchCriteria criteria = new SearchCriteria();
|
||||
criteria.setCount(MATCH_COUNT);
|
||||
criteria.setQuery(query);
|
||||
|
||||
SearchResult artists = searchService.search(criteria, musicFolders, SearchService.IndexType.ARTIST);
|
||||
command.setArtists(artists.getMediaFiles());
|
||||
|
||||
SearchResult albums = searchService.search(criteria, musicFolders, SearchService.IndexType.ALBUM);
|
||||
command.setAlbums(albums.getMediaFiles());
|
||||
|
||||
SearchResult songs = searchService.search(criteria, musicFolders, SearchService.IndexType.SONG);
|
||||
command.setSongs(songs.getMediaFiles());
|
||||
|
||||
command.setPlayer(playerService.getPlayer(request, response));
|
||||
}
|
||||
|
||||
return new ModelAndView(getSuccessView(), errors.getModel());
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setPlayerService(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Controller for updating music file metadata.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SetMusicFileInfoController extends AbstractController {
|
||||
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
String action = request.getParameter("action");
|
||||
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(id);
|
||||
|
||||
if ("comment".equals(action)) {
|
||||
mediaFile.setComment(StringUtil.toHtml(request.getParameter("comment")));
|
||||
mediaFileService.updateMediaFile(mediaFile);
|
||||
}
|
||||
|
||||
String url = "main.view?id=" + id;
|
||||
return new ModelAndView(new RedirectView(url));
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.service.MediaFileService;
|
||||
import org.libresonic.player.service.RatingService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
|
||||
/**
|
||||
* Controller for updating music file ratings.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SetRatingController extends AbstractController {
|
||||
|
||||
private RatingService ratingService;
|
||||
private SecurityService securityService;
|
||||
private MediaFileService mediaFileService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
|
||||
Integer rating = ServletRequestUtils.getIntParameter(request, "rating");
|
||||
if (rating == 0) {
|
||||
rating = null;
|
||||
}
|
||||
|
||||
MediaFile mediaFile = mediaFileService.getMediaFile(id);
|
||||
String username = securityService.getCurrentUsername(request);
|
||||
ratingService.setRatingForUser(username, mediaFile, rating);
|
||||
|
||||
return new ModelAndView(new RedirectView("main.view?id=" + id));
|
||||
}
|
||||
|
||||
public void setRatingService(RatingService ratingService) {
|
||||
this.ratingService = ratingService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
This file is part of Libresonic.
|
||||
|
||||
Libresonic 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.
|
||||
|
||||
Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import org.libresonic.player.domain.*;
|
||||
import org.libresonic.player.service.*;
|
||||
import org.springframework.web.servlet.*;
|
||||
import org.springframework.web.servlet.view.*;
|
||||
import org.springframework.web.servlet.mvc.*;
|
||||
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/**
|
||||
* Controller for the main settings page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SettingsController extends AbstractController {
|
||||
|
||||
private SecurityService securityService;
|
||||
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
User user = securityService.getCurrentUser(request);
|
||||
|
||||
// Redirect to music folder settings if admin.
|
||||
String view = user.isAdminRole() ? "musicFolderSettings.view" : "personalSettings.view";
|
||||
|
||||
return new ModelAndView(new RedirectView(view));
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user