Added better logging and browser support.
We still need to work on the Linux browser a bit.
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@@ -5,10 +5,4 @@
|
|||||||
/*.log
|
/*.log
|
||||||
/*/bin/
|
/*/bin/
|
||||||
/local.properties
|
/local.properties
|
||||||
/browser/profile/extensions/universal_music@regis.edu.xpi
|
/browser/profile/*
|
||||||
/browser/profile/cache2/*
|
|
||||||
/browser/profile/datareporting/*
|
|
||||||
/browser/profile/startupCache/*
|
|
||||||
/browser/profile/sessionstore-backups/*
|
|
||||||
/browser/profile/lock
|
|
||||||
/browser/profile/
|
|
||||||
|
|||||||
@@ -1,29 +1,20 @@
|
|||||||
var port = browser.runtime.connectNative("universal_music")
|
console.log("Hello from Universal Music addon!")
|
||||||
|
/*
|
||||||
/**
|
On startup, connect to the "ping_pong" app.
|
||||||
* Sends a message to the application
|
|
||||||
*/
|
*/
|
||||||
function writeMessage(message)
|
var port = browser.runtime.connectNative("universalmusic");
|
||||||
{
|
|
||||||
port.postMessage(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Sends a message to the application
|
Listen for messages from the app.
|
||||||
*/
|
*/
|
||||||
function onAppMessage(message)
|
port.onMessage.addListener((response) => {
|
||||||
{
|
console.log("Received: " + response);
|
||||||
if ("command" in message)
|
|
||||||
{
|
|
||||||
switch (message["command"])
|
|
||||||
{
|
|
||||||
case "ping":
|
|
||||||
writeMessage({
|
|
||||||
"response": "ping"
|
|
||||||
});
|
});
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
port.onMessage.addListener(onAppMessage);
|
/*
|
||||||
|
On a click on the browser action, send the app a message.
|
||||||
|
*/
|
||||||
|
browser.browserAction.onClicked.addListener(() => {
|
||||||
|
console.log("Sending: ping");
|
||||||
|
port.postMessage("ping");
|
||||||
|
});
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "universal_music",
|
|
||||||
"description": "A link between the Universal Music Player and the web browser.",
|
|
||||||
"path": "${LINK_PATH}",
|
|
||||||
"type": "stdio",
|
|
||||||
"allowed_extensions": ["universal_music@regis.edu"]
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
},
|
},
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
"id": "universal_music@regis.edu",
|
"id": "universalmusic@regis.edu",
|
||||||
"strict_min_version": "55.0"
|
"strict_min_version": "55.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ dependencies {
|
|||||||
implementation 'org.apache.logging.log4j:log4j-core:2.13.3'
|
implementation 'org.apache.logging.log4j:log4j-core:2.13.3'
|
||||||
implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.13.3'
|
implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.13.3'
|
||||||
|
|
||||||
|
implementation 'com.google.code.gson:gson:2.8.7'
|
||||||
|
|
||||||
// Declare the dependency for your favourite test framework you want to use in your tests.
|
// Declare the dependency for your favourite test framework you want to use in your tests.
|
||||||
// TestNG is also supported by the Gradle Test task. Just change the
|
// TestNG is also supported by the Gradle Test task. Just change the
|
||||||
// testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
|
// testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
|
||||||
@@ -40,12 +42,14 @@ abstract class BuildManifest extends DefaultTask {
|
|||||||
@InputDirectory
|
@InputDirectory
|
||||||
final abstract DirectoryProperty inputFile = project.objects.directoryProperty()
|
final abstract DirectoryProperty inputFile = project.objects.directoryProperty()
|
||||||
@OutputFile
|
@OutputFile
|
||||||
final abstract RegularFileProperty outputFile = project.objects.fileProperty().convention(project.layout.buildDirectory.file("manifest.json"))
|
final abstract RegularFileProperty outputFile = project.objects.fileProperty().convention(project.layout.buildDirectory.file("universalmusic.json"))
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
void join() {
|
void join() {
|
||||||
File startupScript
|
File startupScript
|
||||||
Stream<File> files = Arrays.stream(inputFile.get().asFile.listFiles())
|
File binFiles = new File(inputFile.get().asFile, "bin")
|
||||||
|
print binFiles
|
||||||
|
Stream<File> files = Arrays.stream(binFiles.listFiles())
|
||||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||||
startupScript = files.filter(file -> file.getName().endsWith(".bat")).findFirst().get()
|
startupScript = files.filter(file -> file.getName().endsWith(".bat")).findFirst().get()
|
||||||
}
|
}
|
||||||
@@ -58,7 +62,7 @@ abstract class BuildManifest extends DefaultTask {
|
|||||||
outputFile.get().asFile.text = "{\n" +
|
outputFile.get().asFile.text = "{\n" +
|
||||||
" \"name\": \"universalmusic\",\n" +
|
" \"name\": \"universalmusic\",\n" +
|
||||||
" \"description\": \"A link between the Universal Music Player and the web browser.\",\n" +
|
" \"description\": \"A link between the Universal Music Player and the web browser.\",\n" +
|
||||||
" \"path\": \"" + startupScript.getAbsolutePath() + "\",\n" +
|
" \"path\": \"" + startupScript.getAbsolutePath().replace("\\", "\\\\") + "\",\n" +
|
||||||
" \"type\": \"stdio\",\n" +
|
" \"type\": \"stdio\",\n" +
|
||||||
" \"allowed_extensions\": [\"universalmusic@regis.edu\"]\n" +
|
" \"allowed_extensions\": [\"universalmusic@regis.edu\"]\n" +
|
||||||
"}"
|
"}"
|
||||||
@@ -66,50 +70,25 @@ abstract class BuildManifest extends DefaultTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TaskProvider<BuildManifest> buildManifestP = tasks.register("buildManifest", BuildManifest) {
|
TaskProvider<BuildManifest> buildManifestP = tasks.register("buildManifest", BuildManifest) {
|
||||||
inputFile = tasks.named("startScripts", org.gradle.jvm.application.tasks.CreateStartScripts).get().outputDir
|
inputFile = tasks.named("installDist", Sync).get().destinationDir
|
||||||
}
|
}
|
||||||
|
|
||||||
buildManifest.dependsOn(startScripts)
|
buildManifest.dependsOn(installDist)
|
||||||
|
|
||||||
abstract class InstallAddon extends DefaultTask {
|
abstract class InstallAddon extends DefaultTask {
|
||||||
@InputFile
|
@InputFile
|
||||||
final abstract RegularFileProperty inputFile = project.objects.fileProperty()
|
final abstract RegularFileProperty inputFile = project.objects.fileProperty()
|
||||||
@Internal
|
|
||||||
final abstract DirectoryProperty build = project.layout.buildDirectory
|
|
||||||
@InputDirectory
|
|
||||||
final abstract DirectoryProperty libsDir = project.objects.directoryProperty()
|
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
void join() {
|
void join() {
|
||||||
Path lib = libsDir.get().asFile.toPath()
|
|
||||||
Path link = new File(build.get().asFile, "lib").toPath()
|
|
||||||
try {
|
|
||||||
// This takes up more space than I would like, but Windows doesn't allow symbolic links without superuser privliges
|
|
||||||
Files.walk(lib).forEach(file -> {
|
|
||||||
Path fileName = lib.relativize(file)
|
|
||||||
Path dest = link.resolve(fileName)
|
|
||||||
println fileName.toString() + ": " + file.toString() + " to " + dest.toString()
|
|
||||||
if (!Files.exists(dest)) {
|
|
||||||
try {
|
|
||||||
Files.copy(file, dest)
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
throw new RuntimeException("Could not link " + file.toString() + " to " + dest.toString(), e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
throw new RuntimeException("Could not link " + lib.toString() + " to " + link.toString(), e)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||||
Runtime.getRuntime()
|
Process p = Runtime.getRuntime()
|
||||||
.exec("REG ADD HKEY_CURRENT_USER\\SOFTWARE\\Mozilla\\NativeMessagingHosts /v universalmusic /d \""
|
.exec("REG DELETE HKEY_CURRENT_USER\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\universalmusic /f ")
|
||||||
+ inputFile.get().asFile.getAbsolutePath() + "\" ")
|
p.waitFor()
|
||||||
|
p = Runtime.getRuntime()
|
||||||
|
.exec("REG ADD HKEY_CURRENT_USER\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\universalmusic /ve /d \""
|
||||||
|
+ inputFile.get().asFile.getAbsolutePath() + "\" /f ")
|
||||||
|
p.waitFor()
|
||||||
}
|
}
|
||||||
else if (Os.isFamily(Os.FAMILY_MAC)) {
|
else if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||||
Files.copy(inputFile.get().asFile.toPath(), Paths.get(System.getProperty("user.home"), "Library/Application Support/Mozilla/NativeMessagingHosts", inputFile.get().asFile.getName()))
|
Files.copy(inputFile.get().asFile.toPath(), Paths.get(System.getProperty("user.home"), "Library/Application Support/Mozilla/NativeMessagingHosts", inputFile.get().asFile.getName()))
|
||||||
@@ -122,7 +101,6 @@ abstract class InstallAddon extends DefaultTask {
|
|||||||
|
|
||||||
TaskProvider<InstallAddon> installAddonP = tasks.register("installAddon", InstallAddon) {
|
TaskProvider<InstallAddon> installAddonP = tasks.register("installAddon", InstallAddon) {
|
||||||
inputFile = buildManifestP.get().outputFile
|
inputFile = buildManifestP.get().outputFile
|
||||||
libsDir = jar.destinationDirectory
|
|
||||||
}
|
}
|
||||||
|
|
||||||
installAddon.dependsOn(buildManifest)
|
installAddon.dependsOn(buildManifest)
|
||||||
|
|||||||
@@ -4,33 +4,181 @@
|
|||||||
|
|
||||||
package edu.regis.universeplayer.addon;
|
package edu.regis.universeplayer.addon;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public class Main
|
public class Main
|
||||||
{
|
{
|
||||||
private static Logger logger = LoggerFactory.getLogger(Main.class);
|
private static Logger logger = LoggerFactory.getLogger(Main.class);
|
||||||
|
|
||||||
|
private static final Gson gson = new Gson();
|
||||||
|
|
||||||
|
private static BufferedInputStream browserIn;
|
||||||
|
private static BufferedOutputStream browserOut;
|
||||||
|
|
||||||
public static void main(String[] args)
|
public static void main(String[] args)
|
||||||
{
|
{
|
||||||
boolean running = true;
|
boolean running = true;
|
||||||
logger.info("This is an INFO level log message!");
|
int rawLength, messageLength;
|
||||||
logger.error("This is an ERROR level log message!");
|
byte[] header = new byte[4];
|
||||||
logger.warn("This could be problimatic");
|
byte[] message;
|
||||||
logger.info("https://sematext.com/blog/log4j2-tutorial/#toc-log4j-2-configuration-1");
|
JsonElement messageJson;
|
||||||
while (running)
|
JsonPrimitive jsonPrimative;
|
||||||
|
JsonObject jsonObject;
|
||||||
|
ByteBuffer headerReader;
|
||||||
|
|
||||||
|
System.err.printf("Working from directory %s\n", System.getProperty("user.dir"));
|
||||||
|
if (ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN))
|
||||||
{
|
{
|
||||||
logger.info("Waiting1");
|
logger.info("Incoming messages will be Big-endian");
|
||||||
logger.debug("Waiting");
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.info("Incoming messages will be Little-endian");
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Thread.sleep(500);
|
browserIn = new BufferedInputStream(System.in);
|
||||||
}
|
browserOut = new BufferedOutputStream(System.out);
|
||||||
catch (InterruptedException e)
|
while (running)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rawLength = browserIn.read(header);
|
||||||
|
|
||||||
|
if (rawLength <= 0)
|
||||||
{
|
{
|
||||||
logger.trace("Something interrupted us", e);
|
|
||||||
running = false;
|
running = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
headerReader = ByteBuffer.wrap(header, 0, rawLength);
|
||||||
|
/*
|
||||||
|
* The browser outputs in whatever the native endian order,
|
||||||
|
* which may not be what Java does.
|
||||||
|
*/
|
||||||
|
headerReader.order(ByteOrder.nativeOrder());
|
||||||
|
messageLength = headerReader.getInt();
|
||||||
|
logger.debug("Receiving message of {} length", messageLength);
|
||||||
|
message = new byte[messageLength];
|
||||||
|
browserIn.read(message);
|
||||||
|
logger.debug("Unpacking message {} (length {})", new String(message, "UTF-8"), messageLength);
|
||||||
|
|
||||||
|
messageJson = gson.fromJson(new String(message, StandardCharsets.UTF_8), JsonElement.class);
|
||||||
|
logger.debug("Reading JSON element {} (type: {})", messageJson, messageJson.getClass());
|
||||||
|
|
||||||
|
if (messageJson.isJsonPrimitive())
|
||||||
|
{
|
||||||
|
jsonPrimative = ((JsonPrimitive) messageJson);
|
||||||
|
if (jsonPrimative.isBoolean())
|
||||||
|
{
|
||||||
|
handleMessage(jsonPrimative.getAsBoolean());
|
||||||
|
}
|
||||||
|
else if (jsonPrimative.isNumber())
|
||||||
|
{
|
||||||
|
handleMessage(jsonPrimative.getAsNumber());
|
||||||
|
}
|
||||||
|
else if (jsonPrimative.isString())
|
||||||
|
{
|
||||||
|
handleMessage(jsonPrimative.getAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (messageJson.isJsonObject())
|
||||||
|
{
|
||||||
|
jsonObject = ((JsonObject) messageJson);
|
||||||
|
// TODO - Parse the object into Java objects
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.error("Could not create input stream", e);
|
||||||
|
// Forward it to the browser via STDERR
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
browserIn.close();
|
||||||
|
}
|
||||||
|
catch (IOException e1)
|
||||||
|
{
|
||||||
|
logger.error("Could not close browser input", e1);
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
browserOut.close();
|
||||||
|
}
|
||||||
|
catch (IOException e1)
|
||||||
|
{
|
||||||
|
logger.error("Could not close browser output", e1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback for when a message is sent from the browser
|
||||||
|
*
|
||||||
|
* @param message - The message sent.
|
||||||
|
*/
|
||||||
|
private static void handleMessage(Object message)
|
||||||
|
{
|
||||||
|
if (message instanceof String)
|
||||||
|
{
|
||||||
|
if (message.equals("ping"))
|
||||||
|
{
|
||||||
|
sendMessage("pong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback for when a message is sent from the browser
|
||||||
|
*
|
||||||
|
* @param message - The message sent.
|
||||||
|
*/
|
||||||
|
public static void sendMessage(Object message)
|
||||||
|
{
|
||||||
|
String messageJson;
|
||||||
|
byte[] messageData;
|
||||||
|
ByteBuffer headerWriter;
|
||||||
|
|
||||||
|
logger.debug("Sending message {}", message);
|
||||||
|
|
||||||
|
messageJson = gson.toJson(message);
|
||||||
|
messageData = messageJson.getBytes(StandardCharsets.UTF_8);
|
||||||
|
headerWriter = ByteBuffer.allocate(4);
|
||||||
|
/*
|
||||||
|
* The browser outputs in whatever the native endian order,
|
||||||
|
* which may not be what Java does.
|
||||||
|
*/
|
||||||
|
headerWriter.order(ByteOrder.nativeOrder());
|
||||||
|
headerWriter.putInt(messageData.length);
|
||||||
|
logger.debug("Writing message {} {}", headerWriter.array(), messageJson);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
browserOut.write(headerWriter.array());
|
||||||
|
browserOut.write(messageData);
|
||||||
|
browserOut.flush();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.error("Could not write message", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
bin/interface
Executable file → Normal file
0
bin/interface
Executable file → Normal file
0
bin/interface.bat
Executable file → Normal file
0
bin/interface.bat
Executable file → Normal file
@@ -13,7 +13,7 @@ ext.defaultPackage = "edu.regis.universeplayer"
|
|||||||
|
|
||||||
task bundleAddOn(type: Zip)
|
task bundleAddOn(type: Zip)
|
||||||
{
|
{
|
||||||
archiveName = "universal_music@regis.edu.xpi"
|
archiveName = "universalmusic@regis.edu.xpi"
|
||||||
destinationDir = file("./browser/profile/extensions")
|
destinationDir = file("./browser/profile/extensions")
|
||||||
from (files("./add-on"))
|
from (files("./add-on"))
|
||||||
}
|
}
|
||||||
@@ -39,10 +39,10 @@ targetCompatibility = JavaVersion.VERSION_16
|
|||||||
mainClassName = defaultPackage + '.player.Interface'
|
mainClassName = defaultPackage + '.player.Interface'
|
||||||
//mainClassName = defaultPackage + '.localPlayer.Player'
|
//mainClassName = defaultPackage + '.localPlayer.Player'
|
||||||
|
|
||||||
compileJava.dependsOn rootProject.bundleAddOn
|
|
||||||
|
|
||||||
run {
|
run {
|
||||||
systemProperty "java.library.path", file("${project(":player").buildDir}/lib/main/debug").absolutePath
|
systemProperty "java.library.path", file("${project(":player").buildDir}/lib/main/debug").absolutePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileJava.dependsOn rootProject.bundleAddOn
|
||||||
compileJava.dependsOn ':player:linkDebug'
|
compileJava.dependsOn ':player:linkDebug'
|
||||||
|
compileJava.dependsOn ':addonInter:installAddon'
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package edu.regis.universeplayer.data;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This song represents a song found on the local file system.
|
||||||
|
*/
|
||||||
|
public abstract class LocalSong extends Song
|
||||||
|
{
|
||||||
|
public File file;
|
||||||
|
}
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package edu.regis.universeplayer.data;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
public class LocalSongProvider implements SongProvider
|
||||||
|
{
|
||||||
|
private File source;
|
||||||
|
|
||||||
|
private HashMap<File, Song> songs = new HashMap<>();
|
||||||
|
|
||||||
|
private class SongScanner implements Runnable
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scanFolder(File folder)
|
||||||
|
{
|
||||||
|
String type;
|
||||||
|
LinkedList<File> subFolders = new LinkedList<>();
|
||||||
|
for (File file: folder.listFiles())
|
||||||
|
{
|
||||||
|
if (file.isDirectory())
|
||||||
|
{
|
||||||
|
this.scanFolder(file);
|
||||||
|
}
|
||||||
|
if (file.getName().lastIndexOf(".") < file.getName().length() - 1)
|
||||||
|
{
|
||||||
|
type = file.getName().substring(file.getName().lastIndexOf('.') + 1).toLowerCase();
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "mp3":
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalSongProvider(File source)
|
||||||
|
{
|
||||||
|
this.source = source;
|
||||||
|
if (this.source == null || !this.source.isDirectory())
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("File source must be existing directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums within the collection.
|
||||||
|
*
|
||||||
|
* @return A list of albums.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbums()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all songs within the collection.
|
||||||
|
*
|
||||||
|
* @return A list of songs.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Song> getSongs()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all artists.
|
||||||
|
*
|
||||||
|
* @return All artists.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<String> getArtists()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all album artists.
|
||||||
|
*
|
||||||
|
* @return All album artists.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<String> getAlbumArtists()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all genres.
|
||||||
|
*
|
||||||
|
* @return All genres.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<String> getGenres()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all years that have albums.
|
||||||
|
*
|
||||||
|
* @return All years.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Integer> getYears()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all songs from an album.
|
||||||
|
*
|
||||||
|
* @param album - The album to obtain
|
||||||
|
* @return All songs from the requested album, or null if that album is not in the database.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Song> getSongsFromAlbum(Album album)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all songs written by a given artist.
|
||||||
|
*
|
||||||
|
* @param artist - The artist to search for
|
||||||
|
* @return A list of all songs from the specified artist, or null if that artist is not in the
|
||||||
|
* database.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Song> getSongsFromArtist(String artist)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains an album by a specific name.
|
||||||
|
*
|
||||||
|
* @param name - The name to search for.
|
||||||
|
* @return - The first album that matches the given name, or null if that album name is not in
|
||||||
|
* the database.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Album getAlbumByName(String name)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums that were written by a certain artist.
|
||||||
|
*
|
||||||
|
* @param artist - The artist to search for.
|
||||||
|
* @return - The collection on matching albums.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbumsFromArtist(String artist)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums that match a certain genre
|
||||||
|
*
|
||||||
|
* @param genre - The genre to search for.
|
||||||
|
* @return - The collection on matching albums.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbumsFromGenre(String genre)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums that were released a certain year.
|
||||||
|
*
|
||||||
|
* @param year - The year to search for.
|
||||||
|
* @return - The collection on matching albums.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbumsFromYear(int year)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package edu.regis.universeplayer.data;
|
||||||
|
|
||||||
|
import com.mpatric.mp3agic.ID3v1;
|
||||||
|
import com.mpatric.mp3agic.ID3v2;
|
||||||
|
import com.mpatric.mp3agic.InvalidDataException;
|
||||||
|
import com.mpatric.mp3agic.Mp3File;
|
||||||
|
import com.mpatric.mp3agic.UnsupportedTagException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will specifically import an MP3 file.
|
||||||
|
*
|
||||||
|
* @author William Hubbard
|
||||||
|
* @version 0.1
|
||||||
|
*/
|
||||||
|
public class MP3Song extends LocalSong
|
||||||
|
{
|
||||||
|
public MP3Song(File file)
|
||||||
|
{
|
||||||
|
Mp3File metadata;
|
||||||
|
ID3v1 tag1;
|
||||||
|
ID3v2 tag2;
|
||||||
|
this.file = file;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
metadata = new Mp3File(file);
|
||||||
|
this.album = new Album();
|
||||||
|
if (metadata.hasId3v1Tag())
|
||||||
|
{
|
||||||
|
tag1 = metadata.getId3v1Tag();
|
||||||
|
this.title = tag1.getTitle();
|
||||||
|
this.artists = tag1.getArtist().split(";");
|
||||||
|
/**
|
||||||
|
* Trim the artists as needed.
|
||||||
|
*/
|
||||||
|
for (int i = 0, l = this.artists.length; i < l; i++)
|
||||||
|
{
|
||||||
|
this.artists[i] = this.artists[i].trim();
|
||||||
|
}
|
||||||
|
this.trackNum = Integer.parseInt(tag1.getTrack().split("/")[0]);
|
||||||
|
|
||||||
|
this.album.name = tag1.getAlbum();
|
||||||
|
this.album.genres = tag1.getGenreDescription().split(";");
|
||||||
|
for (int i = 0, l = this.album.genres.length; i < l; i++)
|
||||||
|
{
|
||||||
|
this.album.genres[i] = this.album.genres[i].trim();
|
||||||
|
}
|
||||||
|
this.album.year = Integer.parseInt(tag1.getYear());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Make sure that the v2 tag doesn't contain any contradictory information.
|
||||||
|
*/
|
||||||
|
if (metadata.hasId3v2Tag())
|
||||||
|
{
|
||||||
|
tag2 = metadata.getId3v2Tag();
|
||||||
|
if (!tag2.getTitle().isEmpty())
|
||||||
|
{
|
||||||
|
this.title = tag2.getTitle();
|
||||||
|
}
|
||||||
|
if (!tag2.getTitle().isEmpty())
|
||||||
|
{
|
||||||
|
this.artists = tag2.getArtist().split(";");
|
||||||
|
/**
|
||||||
|
* Trim the artists as needed.
|
||||||
|
*/
|
||||||
|
for (int i = 0, l = this.artists.length; i < l; i++)
|
||||||
|
{
|
||||||
|
this.artists[i] = this.artists[i].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!tag2.getTitle().isEmpty())
|
||||||
|
{
|
||||||
|
this.trackNum = Integer.parseInt(tag2.getTrack().split("/")[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tag2.getAlbum().isEmpty())
|
||||||
|
{
|
||||||
|
this.album.name = tag2.getAlbum();
|
||||||
|
}
|
||||||
|
if (!tag2.getAlbumArtist().isEmpty())
|
||||||
|
{
|
||||||
|
this.album.artists = tag2.getAlbumArtist().split(";");
|
||||||
|
/**
|
||||||
|
* Trim the artists as needed.
|
||||||
|
*/
|
||||||
|
for (int i = 0, l = this.album.artists.length; i < l; i++)
|
||||||
|
{
|
||||||
|
this.album.artists[i] = this.album.artists[i].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!tag2.getGenreDescription().isEmpty())
|
||||||
|
{
|
||||||
|
this.album.genres = tag2.getGenreDescription().split(";");
|
||||||
|
for (int i = 0, l = this.album.genres.length; i < l; i++)
|
||||||
|
{
|
||||||
|
this.album.genres[i] = this.album.genres[i].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!tag2.getYear().isEmpty())
|
||||||
|
{
|
||||||
|
this.album.year = Integer.parseInt(tag2.getYear());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Invalid MP3 File", e);
|
||||||
|
}
|
||||||
|
catch (UnsupportedTagException | InvalidDataException e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,8 @@ import edu.regis.universeplayer.browser.Browser;
|
|||||||
import edu.regis.universeplayer.browser.MessageManager;
|
import edu.regis.universeplayer.browser.MessageManager;
|
||||||
import edu.regis.universeplayer.data.CollectionType;
|
import edu.regis.universeplayer.data.CollectionType;
|
||||||
import edu.regis.universeplayer.data.Song;
|
import edu.regis.universeplayer.data.Song;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Interface class serves as the primary GUI that the player interacts with.
|
* The Interface class serves as the primary GUI that the player interacts with.
|
||||||
@@ -30,6 +32,8 @@ import edu.regis.universeplayer.data.Song;
|
|||||||
*/
|
*/
|
||||||
public class Interface extends JFrame implements SongDisplayListener, ComponentListener, WindowListener, PlaybackCommandListener
|
public class Interface extends JFrame implements SongDisplayListener, ComponentListener, WindowListener, PlaybackCommandListener
|
||||||
{
|
{
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(Interface.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to the panel containing links to different collection views.
|
* A reference to the panel containing links to different collection views.
|
||||||
*/
|
*/
|
||||||
@@ -54,15 +58,15 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
|
|
||||||
public static void main(String[] args)
|
public static void main(String[] args)
|
||||||
{
|
{
|
||||||
try
|
/*
|
||||||
{
|
* Add this just in case of a crash or something. It won't work if the
|
||||||
Browser.launchBrowser();
|
* program is forcible terminated by the OS, but it could be helpful
|
||||||
}
|
* otherwise.
|
||||||
catch (IOException e)
|
*/
|
||||||
{
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
e.printStackTrace();
|
Browser.closeBrowser();
|
||||||
JOptionPane.showMessageDialog(null, e, "Could not open browser background", JOptionPane.ERROR_MESSAGE);
|
}));
|
||||||
}
|
logger.info("Starting application");
|
||||||
Interface inter = new Interface();
|
Interface inter = new Interface();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -70,11 +74,24 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
logger.error("Could not open browser communication", e);
|
||||||
JOptionPane.showMessageDialog(null, e, "Could not open browser communication", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(null, e, "Could not open browser communication", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
inter.pack();
|
inter.pack();
|
||||||
inter.setVisible(true);
|
inter.setVisible(true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Launch the browser in the background.
|
||||||
|
*/
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Browser.launchBrowser();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.error("Could not open browser background", e);
|
||||||
|
JOptionPane.showMessageDialog(inter, e, "Could not open browser background", JOptionPane.ERROR_MESSAGE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -168,7 +185,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
@Override
|
@Override
|
||||||
public void windowOpened(WindowEvent windowEvent)
|
public void windowOpened(WindowEvent windowEvent)
|
||||||
{
|
{
|
||||||
|
logger.info("Interface opened");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -183,7 +200,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
logger.error("Could not close browser", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,7 +255,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
logger.error("Could not send message to browser", e);
|
||||||
JOptionPane.showMessageDialog(this, e, "Could not send message to browser", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(this, e, "Could not send message to browser", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user