Gives the interface the ability to pull information from the YouTube page.
This will make filling in song data much easier.
This commit is contained in:
@@ -16,7 +16,7 @@ logger.pushUpdate = function (message)
|
||||
}
|
||||
};
|
||||
|
||||
logger.log("Hello from Universal Music addon!")
|
||||
console.log("Hello from Universal Music addon!")
|
||||
|
||||
/**
|
||||
* This variable contains a mapping of message IDs to the message promises they correspond to, as
|
||||
@@ -178,11 +178,6 @@ function loadTab(url, pinned)
|
||||
}
|
||||
}
|
||||
}, reject);
|
||||
}).finally(() => {
|
||||
if (!pinned)
|
||||
{
|
||||
browser.tabs.remove(chosen);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
class Album
|
||||
{
|
||||
type = "edu.regis.universeplayer.data.Album";
|
||||
name;
|
||||
artists;
|
||||
year;
|
||||
@@ -10,6 +11,7 @@ class Album
|
||||
|
||||
class Song
|
||||
{
|
||||
type = "edu.regis.universeplayer.data.Song";
|
||||
/**
|
||||
* The name of the song.
|
||||
*/
|
||||
@@ -34,11 +36,12 @@ class Song
|
||||
/**
|
||||
* A reference to the album this song is part of.
|
||||
*/
|
||||
album;
|
||||
album = new Album();
|
||||
}
|
||||
|
||||
class InternetSong extends Song
|
||||
{
|
||||
type = "edu.regis.universeplayer.data.InternetSong";
|
||||
/**
|
||||
* The location of the song.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
let logger = new Logger("foreground");
|
||||
logger.debug("Loading foreground.js");
|
||||
console.debug("Loading foreground.js");
|
||||
let background;
|
||||
|
||||
/**
|
||||
@@ -124,7 +124,7 @@ $(function () {
|
||||
background.onMessage.addListener(message => {
|
||||
let num = message.num;
|
||||
post = data => {
|
||||
logger.log("Sending to interface %o", data);
|
||||
logger.trace("Sending to interface %o", data);
|
||||
background.postMessage({
|
||||
type: "response",
|
||||
num: num,
|
||||
|
||||
@@ -57,19 +57,44 @@ class Logger {
|
||||
passLog(level, message)
|
||||
{
|
||||
let messageData;
|
||||
if (message.length > 1 && typeof message[0] == "string")
|
||||
if (message.length > 1)
|
||||
{
|
||||
if (typeof message[0] == "string")
|
||||
{
|
||||
/*
|
||||
* Format the initial string in a way that SLF4J can understand it.
|
||||
*/
|
||||
message[0] = message[0].replaceAll(/%[a-z]/gi, "{}");
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < message.length; i++)
|
||||
{
|
||||
if (message[i] instanceof Error)
|
||||
{
|
||||
message[i] = {
|
||||
"type": "edu.regis.universeplayer.browserCommands.BrowserError",
|
||||
"name": message[i].name,
|
||||
"message": message[i].message,
|
||||
"fileName": message[i].fileName,
|
||||
"lineNumber": message[i].lineNumber,
|
||||
"columnNumber": message[i].columnNumber,
|
||||
"stack": message[i].stack
|
||||
}
|
||||
}
|
||||
}
|
||||
this.queuedMessages.push(new MessageData(this.name, level, message));
|
||||
message = this.queuedMessages.pop();
|
||||
try
|
||||
{
|
||||
while (message && this.pushUpdate(message))
|
||||
{
|
||||
message = this.queuedMessages.pop();
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.error("Could not post log message", e)
|
||||
}
|
||||
/*
|
||||
* If we popped a message but got here, then a message was not sent. Put it back.
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ logger.pushUpdate = function (message)
|
||||
}
|
||||
};
|
||||
|
||||
ylogger.debug("Loading youtube.js")
|
||||
console.debug("Loading youtube.js")
|
||||
var video;
|
||||
|
||||
function onload()
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package edu.regis.universeplayer.addon;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
|
||||
import edu.regis.universeplayer.browserCommands.BrowserError;
|
||||
|
||||
public class BrowserErrorSerializer implements JsonSerializer<BrowserError>, JsonDeserializer<BrowserError>
|
||||
{
|
||||
@Override
|
||||
public JsonElement serialize(BrowserError src, Type typeOfSrc,
|
||||
JsonSerializationContext context)
|
||||
{
|
||||
JsonObject ob = new JsonObject();
|
||||
ob.addProperty("message", src.getMessage());
|
||||
String stack =
|
||||
Arrays.stream(src.getStackTrace())
|
||||
.map(trace -> trace.getMethodName() + "@" + trace
|
||||
.getFileName() + ":" + trace
|
||||
.getLineNumber() + ":0").reduce("",
|
||||
(s1, s2) -> s1.isEmpty() ? s2 : s1 + "\n" + s2);
|
||||
ob.addProperty("stack", stack);
|
||||
JsonArray suppressed = new JsonArray();
|
||||
for (Throwable throwable : src.getSuppressed())
|
||||
{
|
||||
suppressed.add(context.serialize(throwable));
|
||||
}
|
||||
return ob;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrowserError deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
JsonObject ob = json.getAsJsonObject();
|
||||
return new BrowserError(ob.get("name").getAsString(), ob.get(
|
||||
"message").getAsString(), ob.get("stack").getAsString());
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,7 @@ package edu.regis.universeplayer.addon;
|
||||
|
||||
import com.google.gson.*;
|
||||
|
||||
import edu.regis.universeplayer.browserCommands.CommandConfirmation;
|
||||
import edu.regis.universeplayer.browserCommands.MessageRunner;
|
||||
import edu.regis.universeplayer.browserCommands.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -37,6 +36,8 @@ public class BrowserLink extends MessageRunner
|
||||
GsonBuilder builder = new GsonBuilder();
|
||||
builder.registerTypeAdapter(StackTraceElement.class, new StackTraceElementSerializer());
|
||||
builder.registerTypeAdapter(Throwable.class, new ThrowableSerializer());
|
||||
builder.registerTypeAdapter(BrowserError.class, new BrowserErrorSerializer());
|
||||
builder.registerTypeAdapter(CommandReturn.class, new CommandReturnSerializer());
|
||||
gson = builder.create();
|
||||
}
|
||||
|
||||
@@ -144,6 +145,7 @@ public class BrowserLink extends MessageRunner
|
||||
*/
|
||||
private Object getMessage(JsonElement message) throws IOException
|
||||
{
|
||||
logger.debug("Deserializing {}", message);
|
||||
if (message.isJsonPrimitive())
|
||||
{
|
||||
return getPrimitiveMessage((JsonPrimitive) message);
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
package edu.regis.universeplayer.addon;
|
||||
|
||||
import com.google.gson.*;
|
||||
import edu.regis.universeplayer.browserCommands.CommandConfirmation;
|
||||
import edu.regis.universeplayer.browserCommands.CommandReturn;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CommandReturnSerializer implements JsonSerializer<CommandReturn<?>>, JsonDeserializer<CommandReturn<?>>
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(CommandReturnSerializer.class);
|
||||
|
||||
@Override
|
||||
public CommandReturn<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
logger.debug("Deserializing return value {}", json);
|
||||
CommandReturn<?> returnVal;
|
||||
CommandConfirmation confirmation;
|
||||
Object value = null;
|
||||
JsonObject jsonOb = json.getAsJsonObject();
|
||||
JsonElement jsonVal = jsonOb.get("returnValue");
|
||||
confirmation = context.deserialize(jsonOb.getAsJsonObject("confirmation"), CommandConfirmation.class);
|
||||
try
|
||||
{
|
||||
value = deserializeObject(jsonVal, context);
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
logger.error("Could not deserialize object {}.", json, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
returnVal = new CommandReturn<>(value, confirmation);
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to determine what type of object is provided and deserializes
|
||||
* it.
|
||||
*
|
||||
* @param jsonVal The element to deserialize. If it is an object, a "type"
|
||||
* field will be used to obtain the class.
|
||||
* @param context The deserialization context.
|
||||
* @return The deserialized object.
|
||||
* @throws ClassNotFoundException If the provided "type" field did not
|
||||
* contain a known class.
|
||||
*/
|
||||
private Object deserializeObject(JsonElement jsonVal, JsonDeserializationContext context) throws ClassNotFoundException
|
||||
{
|
||||
Object value;
|
||||
if (jsonVal.isJsonPrimitive())
|
||||
{
|
||||
JsonPrimitive primVal = jsonVal.getAsJsonPrimitive();
|
||||
if (primVal.isString())
|
||||
{
|
||||
value = primVal.getAsString();
|
||||
}
|
||||
else if (primVal.isBoolean())
|
||||
{
|
||||
value = primVal.getAsBoolean();
|
||||
}
|
||||
else if (primVal.isNumber())
|
||||
{
|
||||
value = primVal.getAsNumber();
|
||||
}
|
||||
else
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
else if (jsonVal.isJsonArray())
|
||||
{
|
||||
JsonArray arrVal = jsonVal.getAsJsonArray();
|
||||
if (arrVal.size() > 0)
|
||||
{
|
||||
value = new ArrayList<>();
|
||||
for (JsonElement el : arrVal)
|
||||
{
|
||||
((ArrayList) value).add(deserializeObject(el, context));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
value = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
else if (jsonVal.isJsonObject())
|
||||
{
|
||||
JsonObject obVal = jsonVal.getAsJsonObject();
|
||||
Class type;
|
||||
if (obVal.has("type"))
|
||||
{
|
||||
type = Class.forName(obVal.get("type").getAsString());
|
||||
value = context.deserialize(obVal, type);
|
||||
logger.debug("Deserializing object of type {} {}", obVal.get("type").getAsString(), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = context.deserialize(obVal, Object.class);
|
||||
logger.debug("Deserializing object of type {} {}", value.getClass(), value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
if (!jsonVal.isJsonObject())
|
||||
{
|
||||
logger.debug("Deserializing non-object {}", jsonVal);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(CommandReturn<?> src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
return serializeObject(src, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to determine what type of object is provided and deserializes
|
||||
* it.
|
||||
*
|
||||
* @param value The element to deserialize. If it is an object, a "type"
|
||||
* field will be used to obtain the class.
|
||||
* @param context The serialization context.
|
||||
* @return The serialized object.
|
||||
*/
|
||||
private JsonElement serializeObject(Object value, JsonSerializationContext context)
|
||||
{
|
||||
JsonElement jsonVal;
|
||||
if (value == null)
|
||||
{
|
||||
jsonVal = JsonNull.INSTANCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value.getClass().isArray())
|
||||
{
|
||||
value = Arrays.stream((Object[]) value).collect(Collectors.toList());
|
||||
}
|
||||
if (value instanceof String || value instanceof Number || value instanceof Boolean)
|
||||
{
|
||||
jsonVal = context.serialize(value);
|
||||
}
|
||||
else if (value instanceof Collection)
|
||||
{
|
||||
jsonVal = new JsonArray();
|
||||
for (Object val : (Collection) value)
|
||||
{
|
||||
jsonVal.getAsJsonArray().add(serializeObject(val, context));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonVal = context.serialize(value, value.getClass());
|
||||
jsonVal.getAsJsonObject().addProperty("type", value.getClass().getName());
|
||||
}
|
||||
}
|
||||
return jsonVal;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package edu.regis.universeplayer.addon;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import edu.regis.universeplayer.browserCommands.BrowserError;
|
||||
|
||||
public class GenericSerializer implements JsonSerializer<Object>,
|
||||
JsonDeserializer<Object>
|
||||
{
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(GenericSerializer.class);
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Object src, Type typeOfSrc,
|
||||
JsonSerializationContext context)
|
||||
{
|
||||
JsonObject val = new JsonObject();
|
||||
for (Field field : src.getClass().getFields())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Modifier.isTransient(field.getModifiers()))
|
||||
{
|
||||
val.add(field.getName(), context.serialize(field.get(src)));
|
||||
}
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
logger.error("Could not store field " + field.getName() + " " +
|
||||
"of class " + src.getClass().getName());
|
||||
}
|
||||
}
|
||||
val.addProperty("type", src.getClass().getName());
|
||||
return val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
Class<?> clazz;
|
||||
Field field;
|
||||
Object ob = null;
|
||||
if (json.isJsonObject() && json.getAsJsonObject().has("type"))
|
||||
{
|
||||
JsonObject jsonOb = json.getAsJsonObject();
|
||||
try
|
||||
{
|
||||
clazz = Class.forName(jsonOb.remove("type").getAsString());
|
||||
ob = clazz.getConstructor().newInstance();
|
||||
for (Map.Entry<String, JsonElement> fields: jsonOb.entrySet())
|
||||
{
|
||||
field = clazz.getField(fields.getKey());
|
||||
if (field.getType().isAssignableFrom(String.class))
|
||||
{
|
||||
field.set(ob, fields.getValue().getAsString());
|
||||
}
|
||||
else if (field.getType().isAssignableFrom(int.class))
|
||||
{
|
||||
field.setInt(ob, fields.getValue().getAsInt());
|
||||
}
|
||||
else if (field.getType().isAssignableFrom(double.class))
|
||||
{
|
||||
field.setDouble(ob, fields.getValue().getAsDouble());
|
||||
}
|
||||
else if (field.getType().isAssignableFrom(byte.class))
|
||||
{
|
||||
field.setByte(ob, fields.getValue().getAsByte());
|
||||
}
|
||||
else if (field.getType().isAssignableFrom(String.class))
|
||||
{
|
||||
field.set(ob, fields.getValue().getAsString());
|
||||
}
|
||||
else if (field.getType().isAssignableFrom(boolean.class))
|
||||
{
|
||||
field.setBoolean(ob, fields.getValue().getAsBoolean());
|
||||
}
|
||||
else if (field.getType().isAssignableFrom(float.class))
|
||||
{
|
||||
field.setFloat(ob, fields.getValue().getAsFloat());
|
||||
}
|
||||
else if (field.getType().isAssignableFrom(long.class))
|
||||
{
|
||||
field.setLong(ob, fields.getValue().getAsLong());
|
||||
}
|
||||
else if (field.getType().isAssignableFrom(short.class))
|
||||
{
|
||||
field.setShort(ob, fields.getValue().getAsShort());
|
||||
}
|
||||
else if (field.getType().isAssignableFrom(URL.class))
|
||||
{
|
||||
try
|
||||
{
|
||||
field.set(ob, new URL(fields.getValue().getAsString()));
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
logger.error("Could not create url {}",
|
||||
fields.getValue().getAsString(), e);
|
||||
}
|
||||
}
|
||||
else if (fields.getValue().isJsonObject())
|
||||
{
|
||||
// clazz = fields.getValue().getAsJsonObject().
|
||||
// field.set(ob, context.);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e)
|
||||
{
|
||||
logger.error("Could not deserialize {} ", json, e);
|
||||
}
|
||||
}
|
||||
return ob;
|
||||
}
|
||||
}
|
||||
@@ -140,6 +140,10 @@ public class Browser extends MessageRunner
|
||||
String methodName;
|
||||
Class<?>[] types = null;
|
||||
Object[] params = null;
|
||||
if (log.message.length == 1 && !(log.message[0] instanceof String))
|
||||
{
|
||||
log.message = new Object[] {"{}", log.message[0]};
|
||||
}
|
||||
if (log.message.length == 1)
|
||||
{
|
||||
types = new Class<?>[]{String.class};
|
||||
@@ -185,6 +189,11 @@ public class Browser extends MessageRunner
|
||||
logger.error("Could not process logger message {}", log,
|
||||
e);
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
logger.error("Could not call logger {} with arguments {}", method,
|
||||
params);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (object instanceof LogEvent)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package edu.regis.universeplayer;
|
||||
|
||||
public class AutoGson
|
||||
{
|
||||
}
|
||||
@@ -4,11 +4,13 @@
|
||||
|
||||
package edu.regis.universeplayer.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
public class Album implements Comparable<Album>
|
||||
public class Album implements Comparable<Album>, Serializable
|
||||
{
|
||||
public int id;
|
||||
public String name;
|
||||
@@ -19,6 +21,29 @@ public class Album implements Comparable<Album>
|
||||
public int totalTracks;
|
||||
public int totalDiscs;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof Album album))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return year == album.year && totalTracks == album.totalTracks && totalDiscs == album.totalDiscs && name.equals(album.name) && Arrays.equals(artists, album.artists) && Arrays.equals(genres, album.genres);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int result = Objects.hash(name, year, totalTracks, totalDiscs);
|
||||
result = 31 * result + Arrays.hashCode(artists);
|
||||
result = 31 * result + Arrays.hashCode(genres);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Album o)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,11 @@ import org.slf4j.LoggerFactory;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* An internet song is a specific type of song that is accessed from a webpage.
|
||||
*/
|
||||
public class InternetSong extends Song
|
||||
{
|
||||
private static final Logger logger = LoggerFactory
|
||||
@@ -21,6 +25,30 @@ public class InternetSong extends Song
|
||||
*/
|
||||
public URL location;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof InternetSong that))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(location, that.location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(super.hashCode(), location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Song o)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ package edu.regis.universeplayer.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This song represents a song found on the local file system.
|
||||
@@ -30,6 +31,30 @@ public class LocalSong extends Song
|
||||
*/
|
||||
public long lastMod;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof LocalSong localSong))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return file.equals(localSong.file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(super.hashCode(), file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Song o)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ package edu.regis.universeplayer.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Contains data for a song.
|
||||
@@ -42,6 +43,28 @@ public class Song implements Comparable<Song>, Serializable
|
||||
*/
|
||||
public Album album;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof Song song))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return trackNum == song.trackNum && disc == song.disc && title.equals(song.title) && Arrays.equals(artists, song.artists) && album.equals(song.album);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int result = Objects.hash(title, trackNum, disc, album);
|
||||
result = 31 * result + Arrays.hashCode(artists);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Song o)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
package edu.regis.universeplayer.player;
|
||||
|
||||
import edu.regis.universeplayer.AbstractTask;
|
||||
import edu.regis.universeplayer.NumberPing;
|
||||
import edu.regis.universeplayer.PlaybackInfo;
|
||||
import edu.regis.universeplayer.PlaybackListener;
|
||||
import edu.regis.universeplayer.PlaybackStatus;
|
||||
@@ -32,6 +34,20 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
|
||||
private static final Logger logger = LoggerFactory
|
||||
.getLogger(BrowserPlayer.class);
|
||||
|
||||
/**
|
||||
* The player instance. This should only be used for debugging..
|
||||
*/
|
||||
private static BrowserPlayer INSTANCE;
|
||||
|
||||
/**
|
||||
* Obtains the player instance. This should only be used for debugging.
|
||||
*/
|
||||
public static BrowserPlayer getInstance()
|
||||
{
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private final ForkJoinPool service = new ForkJoinPool();
|
||||
private final LinkedList<PlaybackListener> listeners = new LinkedList<>();
|
||||
private boolean error = false;
|
||||
|
||||
@@ -65,6 +81,7 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
|
||||
|
||||
public BrowserPlayer()
|
||||
{
|
||||
INSTANCE = this;
|
||||
Thread browserThread = new Thread(() -> {
|
||||
try
|
||||
{
|
||||
@@ -87,37 +104,119 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
|
||||
return this.currentSong;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains data on a specified song.
|
||||
*
|
||||
* @param url - The url to get the data from.
|
||||
* @return A confirmation of command success.
|
||||
*/
|
||||
public ForkJoinTask<InternetSong> getSongData(URL url)
|
||||
{
|
||||
return this.service.submit(new AbstractTask<>()
|
||||
{
|
||||
@Override
|
||||
public QueryFuture<Void> loadSong(InternetSong song)
|
||||
protected boolean exec()
|
||||
{
|
||||
try
|
||||
{
|
||||
return new ForwardedFuture(getBrowser()
|
||||
.sendObject(new CommandLoadSong(song.location)));
|
||||
}
|
||||
catch (IOException e)
|
||||
Future<?> command =
|
||||
getBrowser()
|
||||
.sendObject(new QuerySongData(url));
|
||||
CommandReturn<InternetSong> returnOb =
|
||||
(CommandReturn<InternetSong>) command
|
||||
.get();
|
||||
if (!returnOb.getConfirmation().wasSuccessful())
|
||||
{
|
||||
logger.error("Could not send message", e);
|
||||
return null;
|
||||
this.completeExceptionally(returnOb.getConfirmation()
|
||||
.getError());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.complete(returnOb.getReturnValue());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException | ExecutionException e)
|
||||
{
|
||||
this.completeExceptionally(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForkJoinTask<Void> loadSong(InternetSong song)
|
||||
{
|
||||
return this.service.submit(new AbstractTask<>()
|
||||
{
|
||||
@Override
|
||||
protected boolean exec()
|
||||
{
|
||||
try
|
||||
{
|
||||
Future<?> command =
|
||||
getBrowser()
|
||||
.sendObject(new CommandLoadSong(song.location));
|
||||
CommandReturn<Boolean> returnOb = (CommandReturn<Boolean>) command
|
||||
.get();
|
||||
if (!returnOb.getConfirmation().wasSuccessful())
|
||||
{
|
||||
this.completeExceptionally(returnOb.getConfirmation()
|
||||
.getError());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException | ExecutionException e)
|
||||
{
|
||||
this.completeExceptionally(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the browser process to shut down.
|
||||
*/
|
||||
@Override
|
||||
public QueryFuture<Void> close()
|
||||
public ForkJoinTask<Void> close()
|
||||
{
|
||||
return this.service.submit(new AbstractTask<>()
|
||||
{
|
||||
@Override
|
||||
protected boolean exec()
|
||||
{
|
||||
try
|
||||
{
|
||||
return (QueryFuture<Void>) new ForwardedFuture(getBrowser()
|
||||
.sendObject(new CommandQuit()));
|
||||
}
|
||||
catch (IOException e)
|
||||
Future<?> command =
|
||||
getBrowser()
|
||||
.sendObject(new CommandQuit());
|
||||
CommandReturn<Boolean> returnOb = (CommandReturn<Boolean>) command
|
||||
.get();
|
||||
if (!returnOb.getConfirmation().wasSuccessful())
|
||||
{
|
||||
logger.error("Could not send message", e);
|
||||
return null;
|
||||
this.completeExceptionally(returnOb.getConfirmation()
|
||||
.getError());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException | ExecutionException e)
|
||||
{
|
||||
this.completeExceptionally(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,92 +254,201 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryFuture<Void> play()
|
||||
public ForkJoinTask<Void> play()
|
||||
{
|
||||
try
|
||||
return this.service.submit(new AbstractTask<>()
|
||||
{
|
||||
return new ForwardedFuture(getBrowser()
|
||||
.sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PLAY)));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.error("Could not send message", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryFuture<Void> pause()
|
||||
protected boolean exec()
|
||||
{
|
||||
try
|
||||
{
|
||||
return new ForwardedFuture(getBrowser()
|
||||
.sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PAUSE)));
|
||||
}
|
||||
catch (IOException e)
|
||||
Future<?> command =
|
||||
getBrowser()
|
||||
.sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PLAY));
|
||||
CommandReturn<Boolean> returnOb = (CommandReturn<Boolean>) command
|
||||
.get();
|
||||
if (!returnOb.getConfirmation().wasSuccessful())
|
||||
{
|
||||
logger.error("Could not send message", e);
|
||||
return null;
|
||||
this.completeExceptionally(returnOb.getConfirmation()
|
||||
.getError());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryFuture<Void> togglePlayback()
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
QueryFuture<PlaybackStatus> future = this.getStatus();
|
||||
switch (future.get())
|
||||
{
|
||||
case PLAYING -> {
|
||||
return new ForwardedFuture(getBrowser()
|
||||
.sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PAUSE)));
|
||||
return true;
|
||||
}
|
||||
case PAUSED, STOPPED, FINISHED -> {
|
||||
return new ForwardedFuture(getBrowser()
|
||||
.sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PLAY)));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (IOException | InterruptedException | ExecutionException e)
|
||||
{
|
||||
logger.error("Could not send message", e);
|
||||
return null;
|
||||
this.completeExceptionally(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForkJoinTask<Void> pause()
|
||||
{
|
||||
return this.service.submit(new AbstractTask<>()
|
||||
{
|
||||
@Override
|
||||
protected boolean exec()
|
||||
{
|
||||
try
|
||||
{
|
||||
Future<?> command =
|
||||
getBrowser()
|
||||
.sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PAUSE));
|
||||
CommandReturn<Boolean> returnOb = (CommandReturn<Boolean>) command
|
||||
.get();
|
||||
if (!returnOb.getConfirmation().wasSuccessful())
|
||||
{
|
||||
this.completeExceptionally(returnOb.getConfirmation()
|
||||
.getError());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException | ExecutionException e)
|
||||
{
|
||||
this.completeExceptionally(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForkJoinTask<Void> togglePlayback()
|
||||
{
|
||||
return this.service.submit(new AbstractTask<Void>()
|
||||
{
|
||||
@Override
|
||||
protected boolean exec()
|
||||
{
|
||||
try
|
||||
{
|
||||
Future<?> command = getBrowser()
|
||||
.sendObject(new QueryStatus());
|
||||
CommandReturn<String> returnOb =
|
||||
(CommandReturn<String>) command.get();
|
||||
CommandReturn<?> confirmation;
|
||||
if (!returnOb.getConfirmation().wasSuccessful())
|
||||
{
|
||||
this.completeExceptionally(returnOb.getConfirmation()
|
||||
.getError());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (PlaybackStatus
|
||||
.valueOf(returnOb.getReturnValue()))
|
||||
{
|
||||
case PLAYING -> command =
|
||||
getBrowser()
|
||||
.sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PAUSE));
|
||||
case PAUSED, STOPPED, FINISHED -> command = getBrowser()
|
||||
.sendObject(new CommandSetPlayback(CommandSetPlayback.Playback.PLAY));
|
||||
}
|
||||
confirmation =
|
||||
(CommandReturn<?>) command.get();
|
||||
if (!confirmation.getConfirmation().wasSuccessful())
|
||||
{
|
||||
this.completeExceptionally(returnOb
|
||||
.getConfirmation().getError());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException | ExecutionException e)
|
||||
{
|
||||
this.completeExceptionally(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops playback of the current song.
|
||||
*/
|
||||
@Override
|
||||
public QueryFuture<Void> stopSong()
|
||||
public ForkJoinTask<Void> stopSong()
|
||||
{
|
||||
return this.service.submit(new AbstractTask<>()
|
||||
{
|
||||
@Override
|
||||
protected boolean exec()
|
||||
{
|
||||
try
|
||||
{
|
||||
return new ForwardedFuture(getBrowser()
|
||||
.sendObject(new CommandLoadSong((URL) null)));
|
||||
}
|
||||
catch (IOException e)
|
||||
Future<?> command =
|
||||
getBrowser()
|
||||
.sendObject(new CommandLoadSong((URL) null));
|
||||
CommandReturn<Boolean> returnOb = (CommandReturn<Boolean>) command
|
||||
.get();
|
||||
if (!returnOb.getConfirmation().wasSuccessful())
|
||||
{
|
||||
logger.error("Could not send message", e);
|
||||
return null;
|
||||
this.completeExceptionally(returnOb.getConfirmation()
|
||||
.getError());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException | ExecutionException e)
|
||||
{
|
||||
this.completeExceptionally(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryFuture<Void> seek(float time)
|
||||
public ForkJoinTask<Void> seek(float time)
|
||||
{
|
||||
return this.service.submit(new AbstractTask<>()
|
||||
{
|
||||
@Override
|
||||
protected boolean exec()
|
||||
{
|
||||
try
|
||||
{
|
||||
return new ForwardedFuture(getBrowser()
|
||||
.sendObject(new CommandSeek(time)));
|
||||
}
|
||||
catch (IOException e)
|
||||
Future<?> command =
|
||||
getBrowser()
|
||||
.sendObject(new CommandSeek(time));
|
||||
CommandReturn<Boolean> returnOb = (CommandReturn<Boolean>) command
|
||||
.get();
|
||||
if (!returnOb.getConfirmation().wasSuccessful())
|
||||
{
|
||||
logger.error("Could not send message", e);
|
||||
return null;
|
||||
this.completeExceptionally(returnOb.getConfirmation()
|
||||
.getError());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException | ExecutionException e)
|
||||
{
|
||||
this.completeExceptionally(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,84 +457,49 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
|
||||
* @return A future for the request.
|
||||
*/
|
||||
@Override
|
||||
public QueryFuture<PlaybackStatus> getStatus()
|
||||
public ForkJoinTask<PlaybackStatus> getStatus()
|
||||
{
|
||||
return this.service.submit(new AbstractTask<>()
|
||||
{
|
||||
@Override
|
||||
protected boolean exec()
|
||||
{
|
||||
try
|
||||
{
|
||||
Future future = getBrowser().sendObject(new QueryStatus());
|
||||
return new QueryFuture<>()
|
||||
Future<?> command = getBrowser()
|
||||
.sendObject(new QueryStatus());
|
||||
CommandReturn<String> returnOb =
|
||||
(CommandReturn<String>) command.get();
|
||||
if (!returnOb.getConfirmation().wasSuccessful())
|
||||
{
|
||||
|
||||
private CommandReturn<String> getVal() throws ExecutionException, InterruptedException
|
||||
{
|
||||
return ((CommandReturn<String>) future.get());
|
||||
this.completeExceptionally(returnOb.getConfirmation()
|
||||
.getError());
|
||||
return false;
|
||||
}
|
||||
|
||||
private CommandReturn<String> getVal(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException
|
||||
else
|
||||
{
|
||||
return ((CommandReturn<String>) future.get(timeout, unit));
|
||||
this.complete(PlaybackStatus
|
||||
.valueOf(returnOb.getReturnValue()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException | ExecutionException e)
|
||||
{
|
||||
this.completeExceptionally(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfirmation getConfirmation() throws CancellationException, ExecutionException, InterruptedException
|
||||
{
|
||||
return this.getVal().getConfirmation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfirmation getConfirmation(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException
|
||||
{
|
||||
return this.getVal(timeout, unit).getConfirmation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning)
|
||||
{
|
||||
return future.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled()
|
||||
{
|
||||
return future.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone()
|
||||
{
|
||||
return future.isDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlaybackStatus get() throws InterruptedException, ExecutionException
|
||||
{
|
||||
String value = getVal().getReturnValue();
|
||||
return PlaybackStatus.valueOf(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlaybackStatus get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
String value = getVal(timeout, unit).getReturnValue();
|
||||
return PlaybackStatus.valueOf(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.error("Could not send message", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryFuture<Float> getCurrentTime()
|
||||
public ForkJoinTask<Float> getCurrentTime()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryFuture<Float> getLength()
|
||||
public ForkJoinTask<Float> getLength()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -338,18 +511,90 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
|
||||
* script or the background script.
|
||||
* @return The return value containing error details.
|
||||
*/
|
||||
public QueryFuture<Void> throwError(boolean forward)
|
||||
public ForkJoinTask<Void> throwError(boolean forward)
|
||||
{
|
||||
return this.service.submit(new AbstractTask<>()
|
||||
{
|
||||
@Override
|
||||
protected boolean exec()
|
||||
{
|
||||
try
|
||||
{
|
||||
return new ForwardedFuture(getBrowser()
|
||||
.sendObject(new CommandError(forward)));
|
||||
}
|
||||
catch (IOException e)
|
||||
Future<?> command =
|
||||
getBrowser()
|
||||
.sendObject(new CommandError(forward));
|
||||
CommandReturn<Boolean> returnOb = (CommandReturn<Boolean>) command
|
||||
.get();
|
||||
if (!returnOb.getConfirmation().wasSuccessful())
|
||||
{
|
||||
logger.error("Could not send message", e);
|
||||
return null;
|
||||
this.completeExceptionally(returnOb.getConfirmation()
|
||||
.getError());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException | ExecutionException e)
|
||||
{
|
||||
this.completeExceptionally(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings the browser background.
|
||||
*
|
||||
* @param i - The number to send.
|
||||
* @return A future hopefully returning the same value.
|
||||
*/
|
||||
public ForkJoinTask<Double> ping(double i)
|
||||
{
|
||||
return this.ping(null, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings the browser foreground.
|
||||
*
|
||||
* @param url - The url to open.
|
||||
* @param i - The number to send.
|
||||
* @return A future hopefully returning the same value.
|
||||
*/
|
||||
public ForkJoinTask<Double> ping(URL url, double i)
|
||||
{
|
||||
return this.service.submit(new AbstractTask<>()
|
||||
{
|
||||
@Override
|
||||
protected boolean exec()
|
||||
{
|
||||
try
|
||||
{
|
||||
Future<?> command = getBrowser()
|
||||
.sendObject(new NumberPing(url, i));
|
||||
CommandReturn<Number> returnOb =
|
||||
(CommandReturn<Number>) command.get();
|
||||
if (!returnOb.getConfirmation().wasSuccessful())
|
||||
{
|
||||
this.completeExceptionally(returnOb.getConfirmation()
|
||||
.getError());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.complete(returnOb.getReturnValue().doubleValue());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException | ExecutionException e)
|
||||
{
|
||||
this.completeExceptionally(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -363,66 +608,4 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
|
||||
this.listeners.forEach(l -> l.onPlaybackChanged(status));
|
||||
}
|
||||
}
|
||||
|
||||
private class ForwardedFuture<T> implements QueryFuture<T>
|
||||
{
|
||||
private final Future<T> future;
|
||||
|
||||
ForwardedFuture(Future<T> future)
|
||||
{
|
||||
this.future = future;
|
||||
}
|
||||
|
||||
private CommandReturn<T> getVal() throws ExecutionException, InterruptedException
|
||||
{
|
||||
return ((CommandReturn<T>) this.future.get());
|
||||
}
|
||||
|
||||
private CommandReturn<T> getVal(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException
|
||||
{
|
||||
return ((CommandReturn<T>) this.future.get(timeout, unit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfirmation getConfirmation() throws CancellationException, ExecutionException, InterruptedException
|
||||
{
|
||||
return this.getVal().getConfirmation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfirmation getConfirmation(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
return this.getVal(timeout, unit).getConfirmation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning)
|
||||
{
|
||||
return this.future.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled()
|
||||
{
|
||||
return this.future.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone()
|
||||
{
|
||||
return this.future.isDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() throws InterruptedException, ExecutionException
|
||||
{
|
||||
return this.getVal().getReturnValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
return this.getVal(timeout, unit).getReturnValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
87
interface/src/test/java/BrowserTest.java
Normal file
87
interface/src/test/java/BrowserTest.java
Normal file
@@ -0,0 +1,87 @@
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import edu.regis.universeplayer.PlayerEnvironment;
|
||||
import edu.regis.universeplayer.browser.Browser;
|
||||
import edu.regis.universeplayer.browserCommands.QuerySongData;
|
||||
import edu.regis.universeplayer.data.Album;
|
||||
import edu.regis.universeplayer.data.InternetSong;
|
||||
import edu.regis.universeplayer.player.BrowserPlayer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class BrowserTest
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(BrowserTest.class);
|
||||
|
||||
@BeforeClass
|
||||
public static void setupBrowser()
|
||||
{
|
||||
HashMap<String, Object> props = new HashMap<>();
|
||||
props.put("headless", true);
|
||||
PlayerEnvironment.init(props, Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPing()
|
||||
{
|
||||
logger.info("Pinging background");
|
||||
logger.info("Background test 1");
|
||||
assertEquals(20.0,
|
||||
BrowserPlayer.getInstance().ping(20).join(), 0.01);
|
||||
logger.info("Background test 2");
|
||||
assertEquals(43.1,
|
||||
BrowserPlayer.getInstance().ping(43.1).join(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPingForeground() throws MalformedURLException
|
||||
{
|
||||
logger.info("Pinging foreground");
|
||||
logger.info("Forground test 1");
|
||||
assertEquals(20.0,
|
||||
BrowserPlayer.getInstance()
|
||||
.ping(new URL("https://www.youtube.com/watch?v=FtutLA63Cp8"), 20)
|
||||
.join(), 0.01);
|
||||
logger.info("Forground test 2");
|
||||
assertEquals(39.5,
|
||||
BrowserPlayer.getInstance()
|
||||
.ping(new URL("https://www.youtube" +
|
||||
".com/watch?v=FtutLA63Cp8"), 39.5)
|
||||
.join(), 0.01);
|
||||
logger.info("Foreground test 3");
|
||||
assertEquals(5.1,
|
||||
BrowserPlayer.getInstance()
|
||||
.ping(new URL("https://www.youtube.com/watch?v=grMqiZKmUeE"), 5.1)
|
||||
.join(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataQuery() throws IOException
|
||||
{
|
||||
logger.info("Retrieving song data");
|
||||
InternetSong song = new InternetSong();
|
||||
URL url = new URL("https://www.youtube.com/watch?v=cvX4B7GjU6s");
|
||||
song.location = url;
|
||||
song.title = "First Wave";
|
||||
song.artists = new String[]{"Trocadero"};
|
||||
song.duration = 224541;
|
||||
song.album = new Album();
|
||||
song.album.name = "Ghosts That Linger";
|
||||
song.album.year = 2009;
|
||||
song.album.artists = new String[]{"Rooster Teeth Records / Trocadero"};
|
||||
|
||||
assertEquals(song,
|
||||
BrowserPlayer.getInstance()
|
||||
.getSongData(url).join());
|
||||
}
|
||||
}
|
||||
16
interface/src/test/resources/log4j2.xml
Normal file
16
interface/src/test/resources/log4j2.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--
|
||||
~ Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
-->
|
||||
<Configuration packages="edu.regis.universeplayer" status="WARN">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%c:%L %-5level - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="debug">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
Reference in New Issue
Block a user