diff --git a/add-on/background.js b/add-on/background.js index 6fbddc2..31b8cbf 100644 --- a/add-on/background.js +++ b/add-on/background.js @@ -4,13 +4,20 @@ var numTabMessages = 0; /* On startup, connect to the "ping_pong" app. */ -var port = browser.runtime.connectNative("universalmusic"); +var interfacePort = browser.runtime.connectNative("universalmusic"); var tabPort = null; function setupTab(port) { - console.info("Tab " + port.sender.tab.url + " loaded."); + if (port.sender) + { + console.info("Tab " + port.sender.tab.url + " loaded."); + } + else + { + console.info("Tab loaded"); + } tabPort = port; tabPort.onDisconnect.addListener(e => { tabPort = null; @@ -25,9 +32,21 @@ function setupTab(port) browser.tabs.remove(e.sender.tab.id); }); tabPort.onMessage.addListener(message => { - if (typeof message == "object" && message.type == "response") + if (typeof message == "object") { - tabMessages.set(message.num, message.data); + if (message.type == "response") + { + tabMessages.set(message.num, message.data); + } + else if (message.type == "update") + { + returnValue = { + "messageNum": -1, + "message": message.data + } + console.trace("Sending update ", returnValue) + interfacePort.postMessage(returnValue); + } } }); return true; @@ -70,15 +89,20 @@ var listeners = [function (message, returnValue) { } switch (type) { - case "CommandSong": - returnValue = () => browser.tabs.create({url: message.song}).then(setupTab); + case "CommandLoadSong": + returnValue = () => { + if (message.song) + { + browser.tabs.create({url: message.song}); + } + } if (tabPort) { console.log("Replacing tab"); /* * Closes the existing tab first */ - returnValue = browser.tabs.remove(e.sender.tab.id).then(returnValue); + returnValue = browser.tabs.remove(tabPort.sender.tab.id).then(returnValue); } else { @@ -89,7 +113,7 @@ var listeners = [function (message, returnValue) { case "QueryStatus": if (!tabPort) { - returnValue = 4; + returnValue = "EMPTY"; break; } case "QueryTime": @@ -139,6 +163,7 @@ function handleMessage(message) { returnValue = listeners[index](message, returnValue); } + console.debug("Message returned ", returnValue); if (returnValue instanceof Promise) { returnValue.then(resolve).catch(reject); @@ -158,7 +183,7 @@ function handleMessage(message) /* * Listen for messages from the app. */ -port.onMessage.addListener((message) => { +interfacePort.onMessage.addListener((message) => { console.log("Received from interface: ", message); handleMessage(message.message).then((response) => { @@ -175,9 +200,9 @@ port.onMessage.addListener((message) => { } } console.log("Sending ", returnValue) - port.postMessage(returnValue); + interfacePort.postMessage(returnValue); }).catch(error => { - + console.error("Error in evaluating message: ", error) }); }); diff --git a/add-on/foreground.js b/add-on/foreground.js index 74400f1..db6ec11 100644 --- a/add-on/foreground.js +++ b/add-on/foreground.js @@ -34,6 +34,16 @@ function handleMessage(message) } } +function onStatusUpdate(status, time, songData) +{ + sendUpdate({ + type: "edu.regis.universeplayer.PlaybackInfo", + currentSong: songData, + status: status, + playTime: time + }); +} + function sendUpdate(response) { post = data => background.postMessage({ diff --git a/add-on/youtube.js b/add-on/youtube.js index 7dfcd87..0218fd1 100644 --- a/add-on/youtube.js +++ b/add-on/youtube.js @@ -3,26 +3,51 @@ var video; function onload() { - video = document.getElementsByClassName('video-stream html5-main-video')[0] + video = document.getElementsByClassName('video-stream html5-main-video')[0]; + statusUpdate = e => { + return onStatusUpdate(getState(), e.srcElement.currentTime, getSongData()); + }; +// video.addListener("timeupdate", statusUpdate); +// video.addListener("play", statusUpdate); +// video.addListener("pause", statusUpdate); +// video.addListener("ended", statusUpdate); + video.ontimeupdate = statusUpdate; + video.onplay = statusUpdate; + video.onpause = statusUpdate; + video.onended = statusUpdate; +} + +function getSongData() +{ + return { + type: "edu.regis.universeplayer.browser.InternetSong", + location: "window.location.href", + title: getTitle(), + artists: getArtists(), + trackNum: 0, + discNum: 0, + duration: parseInt(getLength() * 1000), + album: null + } } function getState() { if (!video) { - return 4; + return "EMPTY"; } else if (video.ended) { - return 3; + return "FINISHED"; } else if (video.paused) { - return 1; + return "PAUSED"; } else { - return 0; + return "PLAYING"; } } @@ -36,6 +61,16 @@ function getLength() return video.duration; } +function getTitle() +{ + return document.getElementsByTagName("meta").title.content; +} + +function getArtists() +{ + return [document.getElementById("channel-name").getElementsByTagName("a")[0].text]; +} + function play() { if (video != null) diff --git a/addonInter/src/main/java/edu/regis/universeplayer/addon/BrowserLink.java b/addonInter/src/main/java/edu/regis/universeplayer/addon/BrowserLink.java index 36e4291..f9eee2e 100644 --- a/addonInter/src/main/java/edu/regis/universeplayer/addon/BrowserLink.java +++ b/addonInter/src/main/java/edu/regis/universeplayer/addon/BrowserLink.java @@ -219,7 +219,15 @@ public class BrowserLink extends MessageRunner if (clazz != null) { - return gson.fromJson(message, clazz); + try + { + return gson.fromJson(message, clazz); + } + catch (Exception e) + { + logger.error("Could not parse " + message, e); + throw e; + } } else { diff --git a/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java b/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java index 9c38a50..f24e8a3 100644 --- a/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java +++ b/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java @@ -52,6 +52,10 @@ public class Main * Pretty much just forwards any messages to the browser and * returns their value. */ + browserLink.addUpdateListener((update, link) -> { + logger.debug("Sending update {}", update); + interfaceLink.sendUpdate(update); + }); interfaceLink.addListener((providedValue, previousReturn) -> { logger.debug("Forwarding message to browser: {}", providedValue); Object returnValue = browserLink.sendObject(providedValue).get(); diff --git a/addonInter/src/main/resources/log4j2.xml b/addonInter/src/main/resources/log4j2.xml index 35282cb..3e4c214 100644 --- a/addonInter/src/main/resources/log4j2.xml +++ b/addonInter/src/main/resources/log4j2.xml @@ -21,6 +21,6 @@ - + \ No newline at end of file diff --git a/browserCommands/src/main/java/edu/regis/universeplayer/PlaybackInfo.java b/browserCommands/src/main/java/edu/regis/universeplayer/PlaybackInfo.java new file mode 100644 index 0000000..b42c737 --- /dev/null +++ b/browserCommands/src/main/java/edu/regis/universeplayer/PlaybackInfo.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 William Hubbard. All Rights Reserved. + */ + +package edu.regis.universeplayer; + +import edu.regis.universeplayer.PlaybackStatus; +import edu.regis.universeplayer.data.Song; + +import java.io.Serializable; +import java.util.EventObject; + +public class PlaybackInfo implements Serializable +{ + private final Song currentSong; + /** + * The time we are currently at in the song, in seconds. + */ + private final float playTime; + + /** + * The current status of the player. + */ + private final PlaybackStatus status; + + public PlaybackInfo(Song song, float playTime, PlaybackStatus status) + { + this.currentSong = song; + this.playTime = playTime; + this.status = status; + } + + /** + * Obtains the song currently playing. + * + * @return The current song, or null if none is loaded. + */ + public Song getSong() + { + return this.currentSong; + } + + /** + * Obtains the current play time. + * + * @return The play time of the player. This will be zero if the song is + * stopped or not loaded. + */ + public float getPlayTime() + { + return this.playTime; + } + + /** + * Obtains the current status of the player. + * + * @return - The player's playback status. + */ + public PlaybackStatus getStatus() + { + return this.status; + } +} diff --git a/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageHandler.java b/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageHandler.java index da2a521..02ad1ff 100644 --- a/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageHandler.java +++ b/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageHandler.java @@ -9,10 +9,7 @@ import org.slf4j.LoggerFactory; import java.io.*; import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Map; +import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -33,11 +30,12 @@ public class MessageHandler implements Runnable, MessageSerializer private final ExecutorService executor; private final LinkedList listeners = new LinkedList<>(); protected final HashMap> messageResponses = new HashMap<>(); + protected final Queue updates = new LinkedList<>(); /** * Creates a message handler. * - * @param name - The name of the handler. This is used in logging. + * @param name - The name of the handler. This is used in logging. * @param input - The input from our external source. * @param output - The output to the external source. */ @@ -142,6 +140,29 @@ public class MessageHandler implements Runnable, MessageSerializer toRemove.clear(); } } + + /* + * Send in any updates + */ + synchronized (this.updates) + { + if (this.updates.size() > 0) + { + Object update; + while ((update = this.updates.poll()) != null) + { + try + { + messageByte = serializeObject(update); + writeMessage(browserOut, -1, messageByte); + } + catch (IOException e) + { + logger.error("Could not send response message for " + update, e); + } + } + } + } } } catch (Throwable e) @@ -231,6 +252,19 @@ public class MessageHandler implements Runnable, MessageSerializer } } + /** + * Sends an object through the handler as an update not associated with any message. + * + * @param object - The object to send. + */ + public void sendUpdate(Object object) + { + synchronized (this.updates) + { + this.updates.add(object); + } + } + /** * Checks to see if a listener has been added to the list. * diff --git a/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageRunner.java b/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageRunner.java index 2ace761..2f09046 100644 --- a/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageRunner.java +++ b/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/MessageRunner.java @@ -39,6 +39,8 @@ public abstract class MessageRunner implements Runnable, MessageSerializer protected final HashMap sentQueue = new HashMap<>(); protected int messagesSent = 0; + protected final LinkedList listeners = new LinkedList<>(); + /** * Creates a message runner. * @@ -77,6 +79,7 @@ public abstract class MessageRunner implements Runnable, MessageSerializer BufferedOutputStream browserOut = null; MessagePacket packet; + int messageNum = -1; byte[][] returnMessage; ByteBuffer numBuffer = ByteBuffer.allocate(4); @@ -98,18 +101,19 @@ public abstract class MessageRunner implements Runnable, MessageSerializer { try { - packet.returnValue.index = this.messagesSent; - writeMessage(browserOut, this.messagesSent, packet.message); + messageNum = this.messagesSent; + packet.returnValue.index = messageNum; + writeMessage(browserOut, messageNum, packet.message); synchronized (this.sentQueue) { - this.sentQueue.put(this.messagesSent, packet); + this.sentQueue.put(messageNum, packet); this.messagesSent++; } } catch (IOException e) { - logger.error("Could not send message " + this.messagesSent + " " + new String(packet.message, StandardCharsets.UTF_8), e); + logger.error("Could not send message " + messageNum + " " + new String(packet.message, StandardCharsets.UTF_8), e); } } @@ -118,35 +122,44 @@ public abstract class MessageRunner implements Runnable, MessageSerializer */ try { - if (!this.sentQueue.isEmpty()) + if (browserIn.available() > 0) { - if (browserIn.available() > 0) + logger.trace("Reading message"); + /* + * Wait for a response from the browser. + */ + returnMessage = this.readMessage(browserIn); + if (returnMessage == null) { - /* - * Wait for a response from the browser. - */ - returnMessage = this.readMessage(browserIn); - if (returnMessage == null) + logger.info("Connection closed."); + break; + } + else + { + numBuffer.clear(); + numBuffer.put(returnMessage[0]); + numBuffer.clear(); + messageNum = numBuffer.getInt(); + if (messageNum == -1) { - logger.info("Connection closed."); - break; + /* + * Not a response, just a generic update. + */ + Object ob = deserializeObject(returnMessage[1]); + this.triggerUpdateListeners(ob); + logger.debug("Received update {}", ob); } else { - numBuffer.clear(); - numBuffer.put(returnMessage[0]); - numBuffer.clear(); - packet = this.sentQueue.get(numBuffer.getInt()); + packet = this.sentQueue.get(messageNum); if (packet == null) { - numBuffer.clear(); - logger.warn("Received message {} for nonexistant packet", numBuffer.getInt()); + logger.warn("Received message {} for nonexistant packet", messageNum); } else { packet.returnMessage = returnMessage[1]; - numBuffer.clear(); - logger.debug("Reading message {} {}", numBuffer.getInt(), new String(packet.returnMessage, StandardCharsets.UTF_8)); + logger.debug("Reading message {} {}", messageNum, new String(packet.returnMessage, StandardCharsets.UTF_8)); synchronized (this.readLock) { logger.trace("Received message {}, notifying futures.", packet.returnValue.index); @@ -236,6 +249,48 @@ public abstract class MessageRunner implements Runnable, MessageSerializer return future; } + /** + * Adds a listener for when an update not associated with a message comes + * through the runner. + * + * @param listener - The listener to add. + */ + public void addUpdateListener(UpdateListener listener) + { + this.listeners.add(listener); + } + + /** + * Check if an update listener is present. + * + * @param listener - The listener to check for. + * @return Whether the provided listener is present. + */ + public boolean hasUpdateListener(UpdateListener listener) + { + return this.listeners.contains(listener); + } + + /** + * Removes a listener for when an update not associated with a message comes + * through the runner. + * + * @param listener - The listener to remove. + */ + public void removeUpdateListener(UpdateListener listener) + { + this.listeners.remove(listener); + } + + /** + * Triggers update listeners + * @param ob - The update that came through. + */ + protected void triggerUpdateListeners(Object ob) + { + this.listeners.forEach(l -> l.onUpdate(ob, this)); + } + /** * This represents a sent message in storage. */ diff --git a/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/UpdateListener.java b/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/UpdateListener.java new file mode 100644 index 0000000..45752bb --- /dev/null +++ b/browserCommands/src/main/java/edu/regis/universeplayer/browserCommands/UpdateListener.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 William Hubbard. All Rights Reserved. + */ + +package edu.regis.universeplayer.browserCommands; + +import java.util.EventListener; + +/** + * An update listener is called when an update is sent through the message + * runner that is not associated with a message. + * + * @author William Hubbard + * @version 0.1 + */ +public interface UpdateListener extends EventListener +{ + /** + * Called when an update is received from a message runner. + * + * @param object - The received object + * @param runner - The runner that received the object. + */ + void onUpdate(Object object, MessageRunner runner); +} diff --git a/interface/src/main/java/edu/regis/universeplayer/data/Album.java b/browserCommands/src/main/java/edu/regis/universeplayer/data/Album.java similarity index 94% rename from interface/src/main/java/edu/regis/universeplayer/data/Album.java rename to browserCommands/src/main/java/edu/regis/universeplayer/data/Album.java index a78e81a..036db3c 100644 --- a/interface/src/main/java/edu/regis/universeplayer/data/Album.java +++ b/browserCommands/src/main/java/edu/regis/universeplayer/data/Album.java @@ -11,7 +11,7 @@ public class Album implements Comparable public int id; public String name; public String[] artists; - public ImageIcon art; + public transient ImageIcon art; public int year; public String[] genres; public int totalTracks; diff --git a/interface/src/main/java/edu/regis/universeplayer/browser/InternetSong.java b/browserCommands/src/main/java/edu/regis/universeplayer/data/InternetSong.java similarity index 64% rename from interface/src/main/java/edu/regis/universeplayer/browser/InternetSong.java rename to browserCommands/src/main/java/edu/regis/universeplayer/data/InternetSong.java index 2f8d280..91e8903 100644 --- a/interface/src/main/java/edu/regis/universeplayer/browser/InternetSong.java +++ b/browserCommands/src/main/java/edu/regis/universeplayer/data/InternetSong.java @@ -2,9 +2,7 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.browser; - -import edu.regis.universeplayer.data.Song; +package edu.regis.universeplayer.data; import java.net.URL; diff --git a/interface/src/main/java/edu/regis/universeplayer/data/LocalSong.java b/browserCommands/src/main/java/edu/regis/universeplayer/data/LocalSong.java similarity index 100% rename from interface/src/main/java/edu/regis/universeplayer/data/LocalSong.java rename to browserCommands/src/main/java/edu/regis/universeplayer/data/LocalSong.java diff --git a/interface/src/main/java/edu/regis/universeplayer/data/Song.java b/browserCommands/src/main/java/edu/regis/universeplayer/data/Song.java similarity index 94% rename from interface/src/main/java/edu/regis/universeplayer/data/Song.java rename to browserCommands/src/main/java/edu/regis/universeplayer/data/Song.java index 5a3e03f..68a3490 100644 --- a/interface/src/main/java/edu/regis/universeplayer/data/Song.java +++ b/browserCommands/src/main/java/edu/regis/universeplayer/data/Song.java @@ -9,7 +9,7 @@ import java.io.Serializable; /** * Contains data for a song. */ -public abstract class Song implements Comparable, Serializable +public class Song implements Comparable, Serializable { public String title; public String[] artists; diff --git a/interface/src/main/java/edu/regis/universeplayer/PlaybackListener.java b/interface/src/main/java/edu/regis/universeplayer/PlaybackListener.java index c484faf..ee98793 100644 --- a/interface/src/main/java/edu/regis/universeplayer/PlaybackListener.java +++ b/interface/src/main/java/edu/regis/universeplayer/PlaybackListener.java @@ -4,17 +4,16 @@ package edu.regis.universeplayer; -import edu.regis.universeplayer.data.Song; +import edu.regis.universeplayer.data.PlaybackEvent; import java.util.EventListener; -import java.util.EventObject; /** * A playback listener allows a class to listen for events regarding player * updates. * * @author William Hubbard - * @verison 0.1 + * @version 0.1 */ public interface PlaybackListener extends EventListener { @@ -23,64 +22,6 @@ public interface PlaybackListener extends EventListener * * @param status - The playback status. */ - void onPlaybackChanged(PlaybackInfo status); + void onPlaybackChanged(PlaybackEvent status); - class PlaybackInfo extends EventObject - { - private final Song currentSong; - /** - * The time we are currently at in the song, in seconds. - */ - private final float playTime; - - /** - * The current status of the player. - */ - private final PlaybackStatus status; - - public PlaybackInfo(Player player, Song song, float playTime, PlaybackStatus status) - { - super(player); - this.currentSong = song; - this.playTime = playTime; - this.status = status; - } - - @Override - public Player getSource() - { - return (Player) super.getSource(); - } - - /** - * Obtains the song currently playing. - * - * @return The current song, or null if none is loaded. - */ - public Song getSong() - { - return this.currentSong; - } - - /** - * Obtains the current play time. - * - * @return The play time of the player. This will be zero if the song is - * stopped or not loaded. - */ - public float getPlayTime() - { - return this.playTime; - } - - /** - * Obtains the current status of the player. - * - * @return - The player's playback status. - */ - public PlaybackStatus getStatus() - { - return this.status; - } - } } diff --git a/interface/src/main/java/edu/regis/universeplayer/browser/Browser.java b/interface/src/main/java/edu/regis/universeplayer/browser/Browser.java index 649ab93..acb2a92 100644 --- a/interface/src/main/java/edu/regis/universeplayer/browser/Browser.java +++ b/interface/src/main/java/edu/regis/universeplayer/browser/Browser.java @@ -4,20 +4,20 @@ package edu.regis.universeplayer.browser; +import edu.regis.universeplayer.PlaybackInfo; import edu.regis.universeplayer.PlaybackListener; import edu.regis.universeplayer.PlaybackStatus; import edu.regis.universeplayer.Player; import edu.regis.universeplayer.browserCommands.*; +import edu.regis.universeplayer.data.InternetSong; +import edu.regis.universeplayer.data.PlaybackEvent; import edu.regis.universeplayer.data.Song; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; -import java.net.ConnectException; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; +import java.net.*; import java.util.LinkedList; import java.util.Scanner; import java.util.concurrent.*; @@ -227,7 +227,7 @@ public class Browser extends MessageRunner implements Player @Override public void addPlaybackListener(PlaybackListener listener) { - + this.listeners.add(listener); } /** @@ -239,7 +239,7 @@ public class Browser extends MessageRunner implements Player @Override public boolean hasPlaybackListener(PlaybackListener listener) { - return false; + return this.listeners.contains(listener); } /** @@ -250,9 +250,21 @@ public class Browser extends MessageRunner implements Player @Override public void removePlaybackListener(PlaybackListener listener) { - + this.listeners.remove(listener); } - + + @Override + protected void triggerUpdateListeners(Object ob) + { + PlaybackEvent status; + super.triggerUpdateListeners(ob); + if (ob instanceof PlaybackInfo) + { + status = new PlaybackEvent(this, (PlaybackInfo) ob); + this.listeners.forEach(l -> l.onPlaybackChanged(status)); + } + } + @Override public QueryFuture play() { @@ -301,7 +313,15 @@ public class Browser extends MessageRunner implements Player @Override public QueryFuture stopSong() { - return null; + try + { + return new ForwardedFuture(this.sendObject(new CommandLoadSong((URL) null))); + } + catch (IOException e) + { + logger.error("Could not send message", e); + return null; + } } @Override @@ -331,14 +351,14 @@ public class Browser extends MessageRunner implements Player Future future = this.sendObject(new QueryStatus()); return new QueryFuture<>() { - private CommandReturn getVal() throws ExecutionException, InterruptedException + private CommandReturn getVal() throws ExecutionException, InterruptedException { - return ((CommandReturn) future.get()); + return ((CommandReturn) future.get()); } - private CommandReturn getVal(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException + private CommandReturn getVal(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException { - return ((CommandReturn) future.get(timeout, unit)); + return ((CommandReturn) future.get(timeout, unit)); } @Override @@ -374,15 +394,15 @@ public class Browser extends MessageRunner implements Player @Override public PlaybackStatus get() throws InterruptedException, ExecutionException { - Double value = getVal().getReturnValue(); - return PlaybackStatus.values()[value.intValue()]; + String value = getVal().getReturnValue(); + return PlaybackStatus.valueOf(value); } @Override public PlaybackStatus get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - Double value = getVal(timeout, unit).getReturnValue(); - return PlaybackStatus.values()[value.intValue()]; + String value = getVal(timeout, unit).getReturnValue(); + return PlaybackStatus.valueOf(value); } }; } diff --git a/interface/src/main/java/edu/regis/universeplayer/data/InternetSongProvider.java b/interface/src/main/java/edu/regis/universeplayer/data/InternetSongProvider.java index d81653b..c783e3e 100644 --- a/interface/src/main/java/edu/regis/universeplayer/data/InternetSongProvider.java +++ b/interface/src/main/java/edu/regis/universeplayer/data/InternetSongProvider.java @@ -4,7 +4,6 @@ package edu.regis.universeplayer.data; -import edu.regis.universeplayer.browser.InternetSong; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/interface/src/main/java/edu/regis/universeplayer/data/MP3Song.java b/interface/src/main/java/edu/regis/universeplayer/data/MP3Song.java deleted file mode 100644 index 6f78d2e..0000000 --- a/interface/src/main/java/edu/regis/universeplayer/data/MP3Song.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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) - { - } - - } -} diff --git a/interface/src/main/java/edu/regis/universeplayer/data/PlaybackEvent.java b/interface/src/main/java/edu/regis/universeplayer/data/PlaybackEvent.java new file mode 100644 index 0000000..2b5014a --- /dev/null +++ b/interface/src/main/java/edu/regis/universeplayer/data/PlaybackEvent.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 William Hubbard. All Rights Reserved. + */ + +package edu.regis.universeplayer.data; + +import edu.regis.universeplayer.PlaybackInfo; +import edu.regis.universeplayer.Player; +import edu.regis.universeplayer.localPlayer.LocalPlayer; + +import java.util.EventObject; + +public class PlaybackEvent extends EventObject +{ + private final PlaybackInfo info; + + public PlaybackEvent(Player player, PlaybackInfo playbackInfo) + { + super(player); + this.info = playbackInfo; + } + + public PlaybackInfo getInfo() + { + return info; + } +} diff --git a/interface/src/main/java/edu/regis/universeplayer/localPlayer/LocalPlayer.java b/interface/src/main/java/edu/regis/universeplayer/localPlayer/LocalPlayer.java index 0fa7a9a..5b1cdb3 100644 --- a/interface/src/main/java/edu/regis/universeplayer/localPlayer/LocalPlayer.java +++ b/interface/src/main/java/edu/regis/universeplayer/localPlayer/LocalPlayer.java @@ -4,12 +4,14 @@ package edu.regis.universeplayer.localPlayer; import com.intervigil.wave.WaveReader; +import edu.regis.universeplayer.PlaybackInfo; import edu.regis.universeplayer.PlaybackListener; import edu.regis.universeplayer.PlaybackStatus; import edu.regis.universeplayer.Player; import edu.regis.universeplayer.browserCommands.CommandConfirmation; import edu.regis.universeplayer.browserCommands.QueryFuture; import edu.regis.universeplayer.data.LocalSong; +import edu.regis.universeplayer.data.PlaybackEvent; import edu.regis.universeplayer.data.Song; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -310,7 +312,7 @@ public class LocalPlayer implements Player, MediaPlayerEventListener public void playing(MediaPlayer mediaPlayer) { logger.debug("Local player playing."); - SwingUtilities.invokeLater(() -> this.listeners.forEach(playbackListener -> playbackListener.onPlaybackChanged(new PlaybackListener.PlaybackInfo(this, this.currentSong, mediaPlayer.status().time(), PlaybackStatus.PLAYING)))); + SwingUtilities.invokeLater(() -> this.listeners.forEach(playbackListener -> playbackListener.onPlaybackChanged(new PlaybackEvent(this, new PlaybackInfo(this.currentSong, mediaPlayer.status().time(), PlaybackStatus.PLAYING))))); } /** @@ -322,7 +324,7 @@ public class LocalPlayer implements Player, MediaPlayerEventListener public void paused(MediaPlayer mediaPlayer) { logger.debug("Local player paused."); - SwingUtilities.invokeLater(() -> this.listeners.forEach(playbackListener -> playbackListener.onPlaybackChanged(new PlaybackListener.PlaybackInfo(this, this.currentSong, mediaPlayer.status().time(), PlaybackStatus.PAUSED)))); + SwingUtilities.invokeLater(() -> this.listeners.forEach(playbackListener -> playbackListener.onPlaybackChanged(new PlaybackEvent(this, new PlaybackInfo(this.currentSong, mediaPlayer.status().time(), PlaybackStatus.PAUSED))))); } /** @@ -338,7 +340,7 @@ public class LocalPlayer implements Player, MediaPlayerEventListener public void stopped(MediaPlayer mediaPlayer) { logger.debug("Local player stopped prematurely."); - SwingUtilities.invokeLater(() -> this.listeners.forEach(playbackListener -> playbackListener.onPlaybackChanged(new PlaybackListener.PlaybackInfo(this, this.currentSong, mediaPlayer.status().time(), PlaybackStatus.STOPPED)))); + SwingUtilities.invokeLater(() -> this.listeners.forEach(playbackListener -> playbackListener.onPlaybackChanged(new PlaybackEvent(this, new PlaybackInfo(this.currentSong, mediaPlayer.status().time(), PlaybackStatus.STOPPED))))); } /** @@ -372,7 +374,7 @@ public class LocalPlayer implements Player, MediaPlayerEventListener public void finished(MediaPlayer mediaPlayer) { logger.debug("Local player finished."); - SwingUtilities.invokeLater(() -> this.listeners.forEach(playbackListener -> playbackListener.onPlaybackChanged(new PlaybackListener.PlaybackInfo(this, this.currentSong, mediaPlayer.status().time(), PlaybackStatus.FINISHED)))); + SwingUtilities.invokeLater(() -> this.listeners.forEach(playbackListener -> playbackListener.onPlaybackChanged(new PlaybackEvent(this, new PlaybackInfo(this.currentSong, mediaPlayer.status().time(), PlaybackStatus.FINISHED))))); } /** @@ -384,7 +386,7 @@ public class LocalPlayer implements Player, MediaPlayerEventListener @Override public void timeChanged(MediaPlayer mediaPlayer, long newTime) { - SwingUtilities.invokeLater(() -> this.listeners.forEach(playbackListener -> playbackListener.onPlaybackChanged(new PlaybackListener.PlaybackInfo(this, this.currentSong, newTime / 1000F, PlaybackStatus.PLAYING)))); + SwingUtilities.invokeLater(() -> this.listeners.forEach(playbackListener -> playbackListener.onPlaybackChanged(new PlaybackEvent(this, new PlaybackInfo(this.currentSong, newTime / 1000F, PlaybackStatus.PLAYING))))); } /** diff --git a/interface/src/main/java/edu/regis/universeplayer/player/Interface.java b/interface/src/main/java/edu/regis/universeplayer/player/Interface.java index 8411e23..e30cbdf 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/Interface.java +++ b/interface/src/main/java/edu/regis/universeplayer/player/Interface.java @@ -6,7 +6,7 @@ package edu.regis.universeplayer.player; import edu.regis.universeplayer.Player; import edu.regis.universeplayer.browser.Browser; -import edu.regis.universeplayer.browser.InternetSong; +import edu.regis.universeplayer.data.InternetSong; import edu.regis.universeplayer.data.Queue; import edu.regis.universeplayer.data.*; import edu.regis.universeplayer.localPlayer.LocalPlayer; @@ -22,9 +22,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; import java.util.Set; -import java.util.concurrent.Future; /** * The Interface class serves as the primary GUI that the player interacts with. diff --git a/interface/src/main/java/edu/regis/universeplayer/player/InternetSongDialog.java b/interface/src/main/java/edu/regis/universeplayer/player/InternetSongDialog.java index 121f921..f7e2b5a 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/InternetSongDialog.java +++ b/interface/src/main/java/edu/regis/universeplayer/player/InternetSongDialog.java @@ -4,7 +4,7 @@ package edu.regis.universeplayer.player; -import edu.regis.universeplayer.browser.InternetSong; +import edu.regis.universeplayer.data.InternetSong; import edu.regis.universeplayer.data.InternetSongProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/interface/src/main/java/edu/regis/universeplayer/player/PlayerControls.java b/interface/src/main/java/edu/regis/universeplayer/player/PlayerControls.java index 01484d9..169de8e 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/PlayerControls.java +++ b/interface/src/main/java/edu/regis/universeplayer/player/PlayerControls.java @@ -4,9 +4,11 @@ package edu.regis.universeplayer.player; +import edu.regis.universeplayer.PlaybackInfo; import edu.regis.universeplayer.PlaybackListener; import edu.regis.universeplayer.PlaybackStatus; import edu.regis.universeplayer.Player; +import edu.regis.universeplayer.data.PlaybackEvent; import edu.regis.universeplayer.data.Queue; import edu.regis.universeplayer.data.Song; import org.slf4j.Logger; @@ -323,17 +325,18 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener, } @Override - public void onPlaybackChanged(PlaybackInfo status) + public void onPlaybackChanged(PlaybackEvent status) { if (status.getSource() != null && status.getSource() == this.currentPlayer) { - switch (status.getStatus()) + switch (status.getInfo().getStatus()) { case PLAYING -> this.playButton.setIcon(PAUSE_ICON); case FINISHED -> Queue.getInstance().skipNext(); case PAUSED, STOPPED, EMPTY -> this.playButton.setIcon(PLAY_ICON); } - this.progress.setValue((int) status.getPlayTime()); + this.progress.setValue((int) status.getInfo().getPlayTime()); + this.progress.setMaximum((int) (status.getInfo().getSong().duration / 1000)); } } }