diff --git a/interface/build.gradle b/interface/build.gradle index e81fec9..3687b87 100644 --- a/interface/build.gradle +++ b/interface/build.gradle @@ -44,5 +44,5 @@ sourceCompatibility = JavaVersion.VERSION_16 targetCompatibility = JavaVersion.VERSION_16 // Define the main class for the application -mainClassName = defaultPackage + '.player.Interface' +mainClassName = defaultPackage + '.gui.Interface' //mainClassName = defaultPackage + '.localPlayer.Player' diff --git a/interface/src/main/java/edu/regis/universeplayer/ConfigManager.java b/interface/src/main/java/edu/regis/universeplayer/ConfigManager.java new file mode 100644 index 0000000..eb7fc01 --- /dev/null +++ b/interface/src/main/java/edu/regis/universeplayer/ConfigManager.java @@ -0,0 +1,93 @@ +package edu.regis.universeplayer; + +import net.harawata.appdirs.AppDirsFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class ConfigManager +{ + private static final Logger logger = LoggerFactory + .getLogger(ConfigManager.class); + + private static File dataDir; + private static File commDir; + private static File configDir; + /** + * Obtains the data storage directory for the application, creating it if + * needed. + * + * @return The data storage directory. + */ + public static File getDataDir() + { + if (dataDir == null) + { + dataDir = new File(AppDirsFactory.getInstance() + .getUserDataDir("universalmusic", null, null, true)); + if (!dataDir.exists()) + { + if (!dataDir.mkdir()) + { + logger.error("Could not create data directory {}", dataDir); + } + } + } + return dataDir; + } + + /** + * Obtains the configuration directory for the application, creating it if + * needed. + * + * @return The configuration directory. + */ + public static File getConfigDir() + { + if (configDir == null) + { + configDir = new File(AppDirsFactory.getInstance() + .getUserConfigDir("universalmusic", null, null, true)); + if (!configDir.exists()) + { + if (!configDir.mkdir()) + { + logger.error("Could not create configuration directory {}", configDir); + } + } + } + return configDir; + } + + /** + * Obtains the directory for memory mapped files + * + * @return The communications storage directory. + */ + public static File getCommDir() + { + if (commDir == null) + { + commDir = new File(AppDirsFactory.getInstance() + .getSharedDir("universalmusic", null, null), "comm"); + if (!commDir.getParentFile().exists()) + { + if (!commDir.getParentFile().mkdir()) + { + logger.error("Could not create shared directory {}", commDir + .getParent()); + } + } + if (!commDir.exists()) + { + if (!commDir.mkdir()) + { + logger.error("Could not create data directory {}", commDir); + } + } + } + return commDir; + } +} diff --git a/interface/src/main/java/edu/regis/universeplayer/data/DatabaseManager.java b/interface/src/main/java/edu/regis/universeplayer/data/DatabaseManager.java index 983f57d..7c6a8a8 100644 --- a/interface/src/main/java/edu/regis/universeplayer/data/DatabaseManager.java +++ b/interface/src/main/java/edu/regis/universeplayer/data/DatabaseManager.java @@ -4,7 +4,8 @@ package edu.regis.universeplayer.data; -import edu.regis.universeplayer.player.Interface; +import edu.regis.universeplayer.ConfigManager; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,7 +31,8 @@ public class DatabaseManager if (db == null || db.isClosed()) { Class.forName("org.sqlite.JDBC"); - db = DriverManager.getConnection("jdbc:sqlite:" + new File(Interface.getDataDir().getAbsolutePath(), "universalmusic.db").getAbsolutePath()); + db = DriverManager.getConnection("jdbc:sqlite:" + new File(ConfigManager + .getDataDir().getAbsolutePath(), "universalmusic.db").getAbsolutePath()); } warning = db.getWarnings(); while (warning != null) diff --git a/interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java b/interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java index 9ded4c9..884ea7b 100644 --- a/interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java +++ b/interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java @@ -4,8 +4,6 @@ package edu.regis.universeplayer.data; -import edu.regis.universeplayer.player.Interface; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/interface/src/main/java/edu/regis/universeplayer/data/PlaybackEvent.java b/interface/src/main/java/edu/regis/universeplayer/data/PlaybackEvent.java index 2b5014a..93e7c34 100644 --- a/interface/src/main/java/edu/regis/universeplayer/data/PlaybackEvent.java +++ b/interface/src/main/java/edu/regis/universeplayer/data/PlaybackEvent.java @@ -5,8 +5,7 @@ package edu.regis.universeplayer.data; import edu.regis.universeplayer.PlaybackInfo; -import edu.regis.universeplayer.Player; -import edu.regis.universeplayer.localPlayer.LocalPlayer; +import edu.regis.universeplayer.player.Player; import java.util.EventObject; diff --git a/interface/src/main/java/edu/regis/universeplayer/player/AlbumInfo.java b/interface/src/main/java/edu/regis/universeplayer/gui/AlbumInfo.java similarity index 99% rename from interface/src/main/java/edu/regis/universeplayer/player/AlbumInfo.java rename to interface/src/main/java/edu/regis/universeplayer/gui/AlbumInfo.java index 242f69f..987a5f4 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/AlbumInfo.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/AlbumInfo.java @@ -2,7 +2,7 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; import javax.swing.*; import javax.swing.border.LineBorder; diff --git a/interface/src/main/java/edu/regis/universeplayer/player/CollectionList.java b/interface/src/main/java/edu/regis/universeplayer/gui/CollectionList.java similarity index 99% rename from interface/src/main/java/edu/regis/universeplayer/player/CollectionList.java rename to interface/src/main/java/edu/regis/universeplayer/gui/CollectionList.java index bfc41ef..75b369d 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/CollectionList.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/CollectionList.java @@ -2,7 +2,7 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; import java.awt.*; import java.awt.event.FocusAdapter; @@ -14,7 +14,7 @@ import java.util.stream.Collectors; import javax.swing.*; import com.wordpress.tips4java.ScrollablePanel; -import edu.regis.universeplayer.ClickListener; + import edu.regis.universeplayer.data.Album; import edu.regis.universeplayer.data.CollectionType; import edu.regis.universeplayer.data.Song; diff --git a/interface/src/main/java/edu/regis/universeplayer/player/Collections.java b/interface/src/main/java/edu/regis/universeplayer/gui/Collections.java similarity index 97% rename from interface/src/main/java/edu/regis/universeplayer/player/Collections.java rename to interface/src/main/java/edu/regis/universeplayer/gui/Collections.java index 2668d8f..ec1b524 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/Collections.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/Collections.java @@ -2,11 +2,10 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; import java.awt.*; import java.awt.event.*; -import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.Locale; @@ -16,7 +15,6 @@ import javax.swing.*; import edu.regis.universeplayer.data.CollectionType; import edu.regis.universeplayer.data.Song; -import edu.regis.universeplayer.data.SongProvider; /** * This panel links to various song collections the player has set up. diff --git a/interface/src/main/java/edu/regis/universeplayer/player/FocusManager.java b/interface/src/main/java/edu/regis/universeplayer/gui/FocusManager.java similarity index 82% rename from interface/src/main/java/edu/regis/universeplayer/player/FocusManager.java rename to interface/src/main/java/edu/regis/universeplayer/gui/FocusManager.java index 9f43d13..cd7e203 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/FocusManager.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/FocusManager.java @@ -2,7 +2,7 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; import java.awt.*; diff --git a/interface/src/main/java/edu/regis/universeplayer/player/Interface.java b/interface/src/main/java/edu/regis/universeplayer/gui/Interface.java similarity index 73% rename from interface/src/main/java/edu/regis/universeplayer/player/Interface.java rename to interface/src/main/java/edu/regis/universeplayer/gui/Interface.java index b2d4a03..9cbbc3b 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/Interface.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/Interface.java @@ -2,19 +2,17 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; -import edu.regis.universeplayer.Player; +import edu.regis.universeplayer.player.Player; import edu.regis.universeplayer.browser.Browser; -import edu.regis.universeplayer.browser.BrowserPlayer; -import edu.regis.universeplayer.browserCommands.CommandError; +import edu.regis.universeplayer.player.BrowserPlayer; import edu.regis.universeplayer.browserCommands.QueryFuture; import edu.regis.universeplayer.data.InternetSong; import edu.regis.universeplayer.data.Queue; import edu.regis.universeplayer.data.*; -import edu.regis.universeplayer.localPlayer.LocalPlayer; - -import net.harawata.appdirs.AppDirsFactory; +import edu.regis.universeplayer.player.LocalPlayer; +import edu.regis.universeplayer.player.PlayerManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,7 +21,6 @@ import javax.swing.*; import java.awt.*; import java.awt.event.*; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -51,10 +48,6 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL private static Interface INSTANCE; - private static File dataDir; - private static File commDir; - private static File configDir; - public final ActionMap actions = new ActionMap(); /** @@ -82,10 +75,6 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL */ private final PlayerControls controls; - /** - * A link to the browser. - */ - private final ArrayList> players = new ArrayList<>(); private int currentPlayer = -1; public static Interface getInstance() @@ -95,10 +84,8 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL public static void main(String[] args) { - Thread browserThread; Interface inter = null; - Browser browser; - BrowserPlayer browserPlayer; + PlayerManager.getPlayers(); try { /* @@ -112,51 +99,6 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL inter.setSize(700, 500); SongProvider.INSTANCE.addUpdateListener(inter); inter.setVisible(true); - - browser = Browser.createBrowser(); - browserThread = new Thread(browser); - browserThread.start(); - - Player.REGISTERED_PLAYERS.put(LocalSong.class, new LocalPlayer()); - - try - { - inter.players.add(browserPlayer = new BrowserPlayer()); - logger.debug("Sending ping"); - browser.sendObject("ping"); - Runtime.getRuntime() - .addShutdownHook(new Thread(browserPlayer::close)); - Player.REGISTERED_PLAYERS - .put(InternetSong.class, browserPlayer); - -// LinkedList> pingRequests = new LinkedList<>(); -// for (int i = 0; i < 20; i++) -// { -// logger.info("Sending ping"); -// pingRequests.add(browser.sendObject("ping")); -// } -// -// LinkedList> toRemove = new LinkedList<>(); -// while (pingRequests.size() > 0) -// { -// for (Future future : pingRequests) -// { -// if (future.isDone()) -// { -// logger.info("Receiving {}", future.get()); -// toRemove.add(future); -// } -// } -// pingRequests.removeAll(toRemove); -// toRemove.clear(); -// } - } - catch (IOException e) - { - logger.error("Could not open browser background", e); - JOptionPane.showMessageDialog(inter, e, langs - .getString("error.browser.launch"), JOptionPane.ERROR_MESSAGE); - } } catch (Throwable e) { @@ -168,82 +110,6 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL } } - /** - * Obtains the data storage directory for the application, creating it if - * needed. - * - * @return The data storage directory. - */ - public static File getDataDir() - { - if (dataDir == null) - { - dataDir = new File(AppDirsFactory.getInstance() - .getUserDataDir("universalmusic", null, null, true)); - if (!dataDir.exists()) - { - if (!dataDir.mkdir()) - { - logger.error("Could not create data directory {}", dataDir); - } - } - } - return dataDir; - } - - /** - * Obtains the configuration directory for the application, creating it if - * needed. - * - * @return The configuration directory. - */ - public static File getConfigDir() - { - if (configDir == null) - { - configDir = new File(AppDirsFactory.getInstance() - .getUserConfigDir("universalmusic", null, null, true)); - if (!configDir.exists()) - { - if (!configDir.mkdir()) - { - logger.error("Could not create configuration directory {}", configDir); - } - } - } - return configDir; - } - - /** - * Obtains the directory for memory mapped files - * - * @return The communications storage directory. - */ - public static File getCommDir() - { - if (commDir == null) - { - commDir = new File(AppDirsFactory.getInstance() - .getSharedDir("universalmusic", null, null), "comm"); - if (!commDir.getParentFile().exists()) - { - if (!commDir.getParentFile().mkdir()) - { - logger.error("Could not create shared directory {}", commDir - .getParent()); - } - } - if (!commDir.exists()) - { - if (!commDir.mkdir()) - { - logger.error("Could not create data directory {}", commDir); - } - } - } - return commDir; - } - /** * Creates an interface */ @@ -396,49 +262,35 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL @Override protected Void doInBackground() throws Exception { - Interface.this.players.stream() - .filter(p -> p instanceof BrowserPlayer) - .map(p -> (BrowserPlayer) p) - .findFirst() - .ifPresentOrElse(p -> { - logger.debug("Throwing " + - "error"); - QueryFuture command = p - .throwError((false)); - try - { - if (!command - .getConfirmation() - .wasSuccessful()) - { - logger.error("Could not run command", - command.getConfirmation() - .getError()); - JOptionPane - .showMessageDialog(Interface.this, - command.getConfirmation() - .getError(), - command.getConfirmation() - .getMessage(), - JOptionPane.ERROR_MESSAGE); - } - } - catch (ExecutionException | InterruptedException executionException) - { - logger.error("Error" + - " creating " + - "debug " + - "message", executionException); - JOptionPane - .showMessageDialog(Interface.this, executionException - .getMessage(), langs - .getString( - "error.command"), JOptionPane.ERROR_MESSAGE); - } - }, () -> JOptionPane - .showMessageDialog(Interface.this, langs - .getString("error.browser.null"), langs - .getString("error.debug.error"), JOptionPane.ERROR_MESSAGE)); + QueryFuture command = PlayerManager.getPlayers().throwError(false); + try + { + if (!command.getConfirmation().wasSuccessful()) + { + logger.error("Could not run command", + command.getConfirmation() + .getError()); + JOptionPane + .showMessageDialog(Interface.this, + command.getConfirmation() + .getError(), + command.getConfirmation() + .getMessage(), + JOptionPane.ERROR_MESSAGE); + } + } + catch (ExecutionException | InterruptedException executionException) + { + logger.error("Error" + + " creating " + + "debug " + + "message", executionException); + JOptionPane + .showMessageDialog(Interface.this, executionException + .getMessage(), langs + .getString( + "error.command"), JOptionPane.ERROR_MESSAGE); + } return null; } }.execute(); @@ -578,7 +430,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL { if (this.isEnabled()) { - controls.play(); + PlayerManager.getPlayers().play(); } } }); @@ -591,7 +443,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL { if (this.isEnabled()) { - controls.pause(); + PlayerManager.getPlayers().pause(); } } }); @@ -604,7 +456,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL { if (this.isEnabled()) { - controls.togglePlayback(); + PlayerManager.getPlayers().toggle(); } } }); @@ -796,7 +648,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL @Override public void windowClosing(WindowEvent windowEvent) { - this.players.forEach(Player::close); + PlayerManager.getPlayers().shutdownPlayers(); } @Override diff --git a/interface/src/main/java/edu/regis/universeplayer/player/InternetSongDialog.java b/interface/src/main/java/edu/regis/universeplayer/gui/InternetSongDialog.java similarity index 99% rename from interface/src/main/java/edu/regis/universeplayer/player/InternetSongDialog.java rename to interface/src/main/java/edu/regis/universeplayer/gui/InternetSongDialog.java index 66a07a7..2da5401 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/InternetSongDialog.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/InternetSongDialog.java @@ -2,7 +2,7 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; import edu.regis.universeplayer.data.InternetSong; import edu.regis.universeplayer.data.InternetSongProvider; diff --git a/interface/src/main/java/edu/regis/universeplayer/player/LogWindow.java b/interface/src/main/java/edu/regis/universeplayer/gui/LogWindow.java similarity index 96% rename from interface/src/main/java/edu/regis/universeplayer/player/LogWindow.java rename to interface/src/main/java/edu/regis/universeplayer/gui/LogWindow.java index 7b0ecd4..1bbe5fd 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/LogWindow.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/LogWindow.java @@ -1,6 +1,5 @@ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; -import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LogEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -8,9 +7,7 @@ import org.slf4j.LoggerFactory; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; -import java.awt.Rectangle; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; @@ -18,16 +15,10 @@ import java.awt.event.WindowEvent; import java.util.Comparator; import java.util.Formatter; import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Locale; -import java.util.MissingResourceException; -import java.util.Objects; import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.swing.AbstractAction; import javax.swing.Box; diff --git a/interface/src/main/java/edu/regis/universeplayer/player/PlaybackCommand.java b/interface/src/main/java/edu/regis/universeplayer/gui/PlaybackCommand.java similarity index 86% rename from interface/src/main/java/edu/regis/universeplayer/player/PlaybackCommand.java rename to interface/src/main/java/edu/regis/universeplayer/gui/PlaybackCommand.java index b804d54..3bee457 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/PlaybackCommand.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/PlaybackCommand.java @@ -2,7 +2,7 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; /** * Contains possible playback commands that the play can send. diff --git a/interface/src/main/java/edu/regis/universeplayer/player/PlaybackCommandListener.java b/interface/src/main/java/edu/regis/universeplayer/gui/PlaybackCommandListener.java similarity index 93% rename from interface/src/main/java/edu/regis/universeplayer/player/PlaybackCommandListener.java rename to interface/src/main/java/edu/regis/universeplayer/gui/PlaybackCommandListener.java index 5c37963..5149efa 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/PlaybackCommandListener.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/PlaybackCommandListener.java @@ -2,7 +2,7 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; import java.util.EventListener; diff --git a/interface/src/main/java/edu/regis/universeplayer/player/PlayerControls.java b/interface/src/main/java/edu/regis/universeplayer/gui/PlayerControls.java similarity index 58% rename from interface/src/main/java/edu/regis/universeplayer/player/PlayerControls.java rename to interface/src/main/java/edu/regis/universeplayer/gui/PlayerControls.java index 5637978..0da82f2 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/PlayerControls.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/PlayerControls.java @@ -2,18 +2,17 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; -import edu.regis.universeplayer.PlaybackInfo; import edu.regis.universeplayer.PlaybackListener; import edu.regis.universeplayer.PlaybackStatus; -import edu.regis.universeplayer.Player; +import edu.regis.universeplayer.player.Player; import edu.regis.universeplayer.browserCommands.CommandConfirmation; -import edu.regis.universeplayer.browserCommands.CommandReturn; import edu.regis.universeplayer.browserCommands.QueryFuture; import edu.regis.universeplayer.data.PlaybackEvent; import edu.regis.universeplayer.data.Queue; import edu.regis.universeplayer.data.Song; +import edu.regis.universeplayer.player.PlayerManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,12 +45,6 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener, .getBundle("lang.interface", Locale.getDefault()); private final ImageIcon PLAY_ICON, PAUSE_ICON; - /** - * A reference to the song currently playing. - */ - private Song currentSong = null; - private Player currentPlayer = null; - private final JButton playButton; private final JButton nextButton; private final JButton prevButton; @@ -183,34 +176,35 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener, layout.putConstraint(SpringLayout.SOUTH, this, 5, SpringLayout.SOUTH, this.updateProgress); Queue.getInstance().addSongChangeListener(this); + PlayerManager.getPlayers().addPlaybackListener(this); } private void seek(float value) { this.service.execute(() -> { - if (this.currentPlayer != null) + QueryFuture command = PlayerManager.getPlayers().seek(value); + try { - QueryFuture command = this.currentPlayer.seek(value); - try + if (command != null && !command.getConfirmation() + .wasSuccessful()) { - if (!command.getConfirmation().wasSuccessful()) - { - JOptionPane.showMessageDialog(this, - command.getConfirmation().getError(), - command.getConfirmation().getMessage(), - JOptionPane.ERROR_MESSAGE); - logger.error("Could not run command", - command.getConfirmation().getError()); - } - } - catch (ExecutionException | InterruptedException e) - { - logger.error("Could not run command", e); - JOptionPane.showMessageDialog(this, e.getMessage(), langs - .getString( - "error.command"), JOptionPane.ERROR_MESSAGE); + logger.error("Could not run command", + command.getConfirmation().getError()); + JOptionPane.showMessageDialog(this, + command.getConfirmation().getError(), + command.getConfirmation().getMessage(), + JOptionPane.ERROR_MESSAGE); } } + catch (ExecutionException | InterruptedException e) + { + logger.error("Could not run command", + e); + JOptionPane.showMessageDialog(this, + e, + e.getMessage(), + JOptionPane.ERROR_MESSAGE); + } }); this.triggerCommandListeners(PlaybackCommand.SEEK, value); } @@ -220,44 +214,42 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener, this.service.execute(() -> { try { - if (this.currentPlayer != null) + QueryFuture status = + PlayerManager.getPlayers().getStatus(); + CommandConfirmation confirmation = status.getConfirmation(); + if (status.getConfirmation().wasSuccessful()) { - QueryFuture status = - this.currentPlayer.getStatus(); - CommandConfirmation confirmation = status.getConfirmation(); - if (status.getConfirmation().wasSuccessful()) + switch (status.get()) { - switch (status.get()) + case PAUSED -> confirmation = PlayerManager.getPlayers() + .play() + .getConfirmation(); + case STOPPED, EMPTY -> { + if (Queue.getInstance().size() > 0) { - case PAUSED -> confirmation = this.currentPlayer.play() - .getConfirmation(); - case STOPPED, EMPTY -> { - if (Queue.getInstance().size() > 0) + if (Queue.getInstance() + .getCurrentSong() == null) { - if (Queue.getInstance() - .getCurrentSong() == null) - { - Queue.getInstance().skipToSong(0); - } - else - { - confirmation = this.currentPlayer.play() - .getConfirmation(); - } + Queue.getInstance().skipToSong(0); + } + else + { + confirmation = PlayerManager.getPlayers().play() + .getConfirmation(); } } - } } - if (confirmation != null && !confirmation.wasSuccessful()) - { - JOptionPane.showMessageDialog(this, - confirmation.getError(), - confirmation.getMessage(), - JOptionPane.ERROR_MESSAGE); - logger.error("Could not run command", - confirmation.getError()); } } + if (confirmation != null && !confirmation.wasSuccessful()) + { + logger.error("Could not run command", + confirmation.getError()); + JOptionPane.showMessageDialog(this, + confirmation.getError(), + confirmation.getMessage(), + JOptionPane.ERROR_MESSAGE); + } } catch (ExecutionException | InterruptedException e) { @@ -275,29 +267,27 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener, this.service.execute(() -> { try { - if (this.currentPlayer != null) + QueryFuture status = + PlayerManager.getPlayers().getStatus(); + CommandConfirmation confirmation = status.getConfirmation(); + if (status.getConfirmation().wasSuccessful()) { - QueryFuture status = - this.currentPlayer.getStatus(); - CommandConfirmation confirmation = status.getConfirmation(); - if (status.getConfirmation().wasSuccessful()) + switch (status.get()) { - switch (status.get()) - { - case PLAYING -> confirmation = - this.currentPlayer.pause().getConfirmation(); - } - } - if (confirmation != null && !confirmation.wasSuccessful()) - { - JOptionPane.showMessageDialog(this, - confirmation.getError(), - confirmation.getMessage(), - JOptionPane.ERROR_MESSAGE); - logger.error("Could not run command", - confirmation.getError()); + case PLAYING -> confirmation = + PlayerManager.getPlayers().pause() + .getConfirmation(); } } + if (confirmation != null && !confirmation.wasSuccessful()) + { + logger.error("Could not run command", + confirmation.getError()); + JOptionPane.showMessageDialog(this, + confirmation.getError(), + confirmation.getMessage(), + JOptionPane.ERROR_MESSAGE); + } } catch (ExecutionException | InterruptedException e) { @@ -318,46 +308,45 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener, this.service.execute(() -> { try { - if (this.currentPlayer != null) + QueryFuture status = + PlayerManager.getPlayers().getStatus(); + CommandConfirmation confirmation = status.getConfirmation(); + if (status.getConfirmation().wasSuccessful()) { - QueryFuture status = - this.currentPlayer.getStatus(); - CommandConfirmation confirmation = status.getConfirmation(); - if (status.getConfirmation().wasSuccessful()) + switch (status.get()) { - switch (status.get()) + case PAUSED -> confirmation = PlayerManager.getPlayers() + .play() + .getConfirmation(); + case PLAYING -> confirmation = PlayerManager.getPlayers() + .pause() + .getConfirmation(); + case STOPPED, EMPTY -> { + if (Queue.getInstance().size() > 0) { - case PAUSED -> confirmation = this.currentPlayer.play() - .getConfirmation(); - case PLAYING -> confirmation = this.currentPlayer.pause() - .getConfirmation(); - case STOPPED, EMPTY -> { - if (Queue.getInstance().size() > 0) + if (Queue.getInstance() + .getCurrentSong() == null) { - if (Queue.getInstance() - .getCurrentSong() == null) - { - Queue.getInstance().skipToSong(0); - } - else - { - confirmation = this.currentPlayer.play() - .getConfirmation(); - } + Queue.getInstance().skipToSong(0); + } + else + { + confirmation = PlayerManager.getPlayers().play() + .getConfirmation(); } } - } } - if (confirmation != null && !confirmation.wasSuccessful()) - { - JOptionPane.showMessageDialog(this, - confirmation.getError(), - confirmation.getMessage(), - JOptionPane.ERROR_MESSAGE); - logger.error("Could not run command", - confirmation.getError()); } } + if (confirmation != null && !confirmation.wasSuccessful()) + { + JOptionPane.showMessageDialog(this, + confirmation.getError(), + confirmation.getMessage(), + JOptionPane.ERROR_MESSAGE); + logger.error("Could not run command", + confirmation.getError()); + } } catch (ExecutionException | InterruptedException e) { @@ -450,103 +439,67 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener, @Override public void onSongChange(Queue queue) { - if (this.currentSong != null) - { - if (this.currentPlayer != null) + this.service.execute(() -> { + QueryFuture command = PlayerManager.getPlayers().stopSong(); + try { - this.service.execute(() -> { - QueryFuture command = this.currentPlayer.stopSong(); - try - { - if (!command.getConfirmation().wasSuccessful()) - { - JOptionPane.showMessageDialog(this, - command.getConfirmation().getError(), - command.getConfirmation().getMessage(), - JOptionPane.ERROR_MESSAGE); - logger.error("Could not run command", - command.getConfirmation().getError()); - } - } - catch (ExecutionException | InterruptedException e) - { - logger.error("Could not get current playback status", e); - JOptionPane - .showMessageDialog(this, e.getMessage(), langs - .getString( - "error.command"), JOptionPane.ERROR_MESSAGE); - } - }); - } - } - this.currentSong = queue.getCurrentSong(); - this.playButton.setIcon(PLAY_ICON); - if (this.currentSong != null) - { - this.currentPlayer = Player.REGISTERED_PLAYERS - .get(this.currentSong.getClass()); - this.progress.setMaximum((int) (this.currentSong.duration / 1000)); - if (this.currentPlayer != null) - { - if (!this.currentPlayer.hasPlaybackListener(this)) + if (command != null && !command.getConfirmation().wasSuccessful()) { - this.currentPlayer.addPlaybackListener(this); + JOptionPane.showMessageDialog(this, + command.getConfirmation().getError(), + command.getConfirmation().getMessage(), + JOptionPane.ERROR_MESSAGE); + logger.error("Could not run command", + command.getConfirmation().getError()); } - this.service.execute(() -> { - QueryFuture command = - this.currentPlayer.loadSong(this.currentSong); - try + else + { + command = + PlayerManager.getPlayers() + .playSong(queue.getCurrentSong()); + if (!command.getConfirmation().wasSuccessful()) { - if (!command.getConfirmation().wasSuccessful()) - { - JOptionPane.showMessageDialog(this, - command.getConfirmation().getError(), - command.getConfirmation().getMessage(), - JOptionPane.ERROR_MESSAGE); - logger.error("Could not run command", - command.getConfirmation().getError()); - } + JOptionPane.showMessageDialog(this, + command.getConfirmation().getError(), + command.getConfirmation().getMessage(), + JOptionPane.ERROR_MESSAGE); + logger.error("Could not run command", + command.getConfirmation().getError()); } - catch (ExecutionException | InterruptedException e) + else { - logger.error("Could not get current playback status", e); - JOptionPane - .showMessageDialog(this, e.getMessage(), langs - .getString( - "error.command"), JOptionPane.ERROR_MESSAGE); - } - }); + SwingUtilities.invokeLater(() -> { + this.playButton.setIcon(PLAY_ICON); + this.progress.setMaximum((int) (PlayerManager + .getPlayers() + .getCurrentSong().duration / 1000)); + }); -// this.currentPlayer.play(); + } + } } - else + catch (ExecutionException | InterruptedException e) { - logger.error("No logger found for song {}", this.currentSong - .getClass()); + logger.error("Could not get current playback status", e); + JOptionPane + .showMessageDialog(this, e.getMessage(), langs + .getString( + "error.command"), JOptionPane.ERROR_MESSAGE); } - } - else - { - this.currentPlayer = null; - this.progress.setMaximum(0); - } + }); } @Override public void onPlaybackChanged(PlaybackEvent status) { - if (status.getSource() != null && status - .getSource() == this.currentPlayer) + switch (status.getInfo().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.getInfo().getPlayTime()); - this.progress.setMaximum((int) (status.getInfo() - .getSong().duration / 1000)); + 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.getInfo().getPlayTime()); + this.progress.setMaximum((int) (status.getInfo() + .getSong().duration / 1000)); } } diff --git a/interface/src/main/java/edu/regis/universeplayer/player/QueueList.java b/interface/src/main/java/edu/regis/universeplayer/gui/QueueList.java similarity index 99% rename from interface/src/main/java/edu/regis/universeplayer/player/QueueList.java rename to interface/src/main/java/edu/regis/universeplayer/gui/QueueList.java index b279bf7..1fa4c39 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/QueueList.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/QueueList.java @@ -2,7 +2,7 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; import com.wordpress.tips4java.ScrollablePanel; import edu.regis.universeplayer.ClickListener; diff --git a/interface/src/main/java/edu/regis/universeplayer/player/SongDisplayListener.java b/interface/src/main/java/edu/regis/universeplayer/gui/SongDisplayListener.java similarity index 95% rename from interface/src/main/java/edu/regis/universeplayer/player/SongDisplayListener.java rename to interface/src/main/java/edu/regis/universeplayer/gui/SongDisplayListener.java index 7664641..a03bdb6 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/SongDisplayListener.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/SongDisplayListener.java @@ -2,7 +2,7 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; import java.util.Collection; import java.util.EventListener; diff --git a/interface/src/main/java/edu/regis/universeplayer/player/SongList.java b/interface/src/main/java/edu/regis/universeplayer/gui/SongList.java similarity index 99% rename from interface/src/main/java/edu/regis/universeplayer/player/SongList.java rename to interface/src/main/java/edu/regis/universeplayer/gui/SongList.java index c2b329f..9b1ce79 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/SongList.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/SongList.java @@ -2,7 +2,7 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.player; +package edu.regis.universeplayer.gui; import com.wordpress.tips4java.ScrollablePanel; diff --git a/interface/src/main/java/edu/regis/universeplayer/localPlayer/AudioFile.java b/interface/src/main/java/edu/regis/universeplayer/player/AudioFile.java similarity index 97% rename from interface/src/main/java/edu/regis/universeplayer/localPlayer/AudioFile.java rename to interface/src/main/java/edu/regis/universeplayer/player/AudioFile.java index 0fd86f5..7a765d3 100644 --- a/interface/src/main/java/edu/regis/universeplayer/localPlayer/AudioFile.java +++ b/interface/src/main/java/edu/regis/universeplayer/player/AudioFile.java @@ -2,7 +2,7 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.localPlayer; +package edu.regis.universeplayer.player; import java.io.IOException; import java.io.InputStream; diff --git a/interface/src/main/java/edu/regis/universeplayer/browser/BrowserPlayer.java b/interface/src/main/java/edu/regis/universeplayer/player/BrowserPlayer.java similarity index 95% rename from interface/src/main/java/edu/regis/universeplayer/browser/BrowserPlayer.java rename to interface/src/main/java/edu/regis/universeplayer/player/BrowserPlayer.java index be41ccb..e2b0af9 100644 --- a/interface/src/main/java/edu/regis/universeplayer/browser/BrowserPlayer.java +++ b/interface/src/main/java/edu/regis/universeplayer/player/BrowserPlayer.java @@ -2,12 +2,12 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.browser; +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.browser.Browser; import edu.regis.universeplayer.browserCommands.*; import edu.regis.universeplayer.data.InternetSong; import edu.regis.universeplayer.data.PlaybackEvent; @@ -59,6 +59,22 @@ public class BrowserPlayer implements Player, UpdateListener return browserRef; } + public BrowserPlayer() + { + Thread browserThread = new Thread(() -> { + try + { + this.browserRef = Browser.createBrowser(); + this.browserRef.run(); + } + catch (IOException | InterruptedException e) + { + logger.error("Could not initialize browser", e); + } + }); + browserThread.start(); + } + @Override public Song getCurrentSong() { diff --git a/interface/src/main/java/edu/regis/universeplayer/localPlayer/LocalPlayer.java b/interface/src/main/java/edu/regis/universeplayer/player/LocalPlayer.java similarity index 80% rename from interface/src/main/java/edu/regis/universeplayer/localPlayer/LocalPlayer.java rename to interface/src/main/java/edu/regis/universeplayer/player/LocalPlayer.java index 5b1cdb3..aec2459 100644 --- a/interface/src/main/java/edu/regis/universeplayer/localPlayer/LocalPlayer.java +++ b/interface/src/main/java/edu/regis/universeplayer/player/LocalPlayer.java @@ -1,20 +1,22 @@ /* * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer.localPlayer; +package edu.regis.universeplayer.player; 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; + import uk.co.caprica.vlcj.factory.MediaPlayerFactory; import uk.co.caprica.vlcj.media.MediaRef; import uk.co.caprica.vlcj.media.TrackType; @@ -24,6 +26,7 @@ import uk.co.caprica.vlcj.player.base.StatusApi; import uk.co.caprica.vlcj.player.component.AudioPlayerComponent; import javax.swing.*; + import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -39,7 +42,8 @@ import java.util.concurrent.*; */ public class LocalPlayer implements Player, MediaPlayerEventListener { - private static final Logger logger = LoggerFactory.getLogger(LocalPlayer.class); + private static final Logger logger = LoggerFactory + .getLogger(LocalPlayer.class); private final MediaPlayerFactory playerFactory; private final AudioPlayerComponent player; @@ -73,7 +77,8 @@ public class LocalPlayer implements Player, MediaPlayerEventListener } /** - * Saves the current file as a WAVE file somewhere else. This method is meant for testing only. + * Saves the current file as a WAVE file somewhere else. This method is + * meant for testing only. * * @param file - The file to save to. */ @@ -120,21 +125,35 @@ public class LocalPlayer implements Player, MediaPlayerEventListener this.stopSong(); } this.currentSong = song; - this.player.mediaPlayer().media().play(song.file.getAbsolutePath()); - return new NullFuture<>(); + if (!this.player.mediaPlayer().media() + .play(song.file.getAbsolutePath())) + { + return new NullFuture<>(new RuntimeException("Could not play " + + "song " + song)); + } + else + { + return new NullFuture<>(); + } } @Override public QueryFuture play() { - this.player.mediaPlayer().submit((WrappedRunnable) () -> this.player.mediaPlayer().controls().play()); + this.player.mediaPlayer() + .submit((WrappedRunnable) () -> this.player.mediaPlayer() + .controls() + .play()); return new NullFuture<>(); } @Override public QueryFuture pause() { - this.player.mediaPlayer().submit((WrappedRunnable) () -> this.player.mediaPlayer().controls().pause()); + this.player.mediaPlayer() + .submit((WrappedRunnable) () -> this.player.mediaPlayer() + .controls() + .pause()); return new NullFuture<>(); } @@ -154,15 +173,20 @@ public class LocalPlayer implements Player, MediaPlayerEventListener @Override public QueryFuture stopSong() { - this.player.mediaPlayer().submit((WrappedRunnable) () -> this.player.mediaPlayer().controls().stop()); + this.player.mediaPlayer() + .submit((WrappedRunnable) () -> this.player.mediaPlayer() + .controls() + .stop()); return new NullFuture<>(); } @Override public QueryFuture seek(float time) { - this.player.mediaPlayer().submit((WrappedRunnable) () -> this.player.mediaPlayer().controls() - .setTime((long) (time * 1000))); + this.player.mediaPlayer() + .submit((WrappedRunnable) () -> this.player.mediaPlayer() + .controls() + .setTime((long) (time * 1000))); return new NullFuture<>(); } @@ -184,13 +208,15 @@ public class LocalPlayer implements Player, MediaPlayerEventListener @Override public QueryFuture getCurrentTime() { - return new NullFuture<>(this.player.mediaPlayer().status().time() / 1000F); + return new NullFuture<>(this.player.mediaPlayer().status() + .time() / 1000F); } @Override public QueryFuture getLength() { - return new NullFuture<>(this.player.mediaPlayer().status().length() / 1000F); + return new NullFuture<>(this.player.mediaPlayer().status() + .length() / 1000F); } @Override @@ -227,7 +253,8 @@ public class LocalPlayer implements Player, MediaPlayerEventListener * @param file - The file to read * @return An raw stream for the file * @throws FileNotFoundException - Thrown should the file not exist. - * @throws IOException - Thrown should an error occur when reading the file + * @throws IOException - Thrown should an error occur when reading + * the file */ public static AudioFile getAudioStream(File file) throws IOException { @@ -235,12 +262,14 @@ public class LocalPlayer implements Player, MediaPlayerEventListener } /** - * Converts any audio file to a stream containing WAV audio file data (courtesy of FFMPEG). + * Converts any audio file to a stream containing WAV audio file data + * (courtesy of FFMPEG). * * @param file - The file to convert * @return An input stream containing the file data * @throws FileNotFoundException - Thrown should the file not exist. - * @throws IOException - Thrown should an error occur when reading the file + * @throws IOException - Thrown should an error occur when reading + * the file */ protected static Process convertFile(File file) throws FileNotFoundException, IOException { @@ -304,7 +333,8 @@ public class LocalPlayer implements Player, MediaPlayerEventListener /** * The media started playing. *

- * There is no guarantee that a video output has been created at this point. + * There is no guarantee that a video output has been created at this + * point. * * @param mediaPlayer media player that raised the event */ @@ -312,7 +342,10 @@ 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 PlaybackEvent(this, new PlaybackInfo(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))))); } /** @@ -324,15 +357,19 @@ 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 PlaybackEvent(this, new PlaybackInfo(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))))); } /** * Media stopped. *

- * A stopped event may be raised under certain circumstances even if the media player is not playing (e.g. as part - * of the associated media list player sub-item handling). Client applications must therefore be prepared to handle - * such a situation. + * A stopped event may be raised under certain circumstances even if the + * media player is not playing (e.g. as part of the associated media list + * player sub-item handling). Client applications must therefore be prepared + * to handle such a situation. * * @param mediaPlayer media player that raised the event */ @@ -340,7 +377,10 @@ 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 PlaybackEvent(this, new PlaybackInfo(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))))); } /** @@ -374,7 +414,10 @@ 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 PlaybackEvent(this, new PlaybackInfo(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))))); } /** @@ -386,7 +429,9 @@ public class LocalPlayer implements Player, MediaPlayerEventListener @Override public void timeChanged(MediaPlayer mediaPlayer, long newTime) { - SwingUtilities.invokeLater(() -> this.listeners.forEach(playbackListener -> playbackListener.onPlaybackChanged(new PlaybackEvent(this, new PlaybackInfo(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))))); } /** @@ -531,7 +576,8 @@ public class LocalPlayer implements Player, MediaPlayerEventListener * other application) starts/stops playing media. * * @param mediaPlayer media player that raised the event - * @param corked true if corked; otherwise false + * @param corked true if corked; otherwise + * false */ @Override public void corked(MediaPlayer mediaPlayer, boolean corked) @@ -543,7 +589,8 @@ public class LocalPlayer implements Player, MediaPlayerEventListener * The audio was muted/un-muted. * * @param mediaPlayer media player that raised the event - * @param muted true if muted; otherwise false + * @param muted true if muted; otherwise + * false */ @Override public void muted(MediaPlayer mediaPlayer, boolean muted) @@ -609,9 +656,10 @@ public class LocalPlayer implements Player, MediaPlayerEventListener * The event will be fired again if the media is played again after a native * stopped or finished event is received. *

- * Waiting for this event may be more reliable than using {@link #playing(MediaPlayer)} - * or {@link #videoOutput(MediaPlayer, int)} in some cases (logo and marquee - * already mentioned, also setting audio tracks, sub-title tracks and so on). + * Waiting for this event may be more reliable than using {@link + * #playing(MediaPlayer)} or {@link #videoOutput(MediaPlayer, int)} in some + * cases (logo and marquee already mentioned, also setting audio tracks, + * sub-title tracks and so on). * * @param mediaPlayer media player that raised the event */ @@ -692,27 +740,40 @@ public class LocalPlayer implements Player, MediaPlayerEventListener private static class NullFuture implements QueryFuture { private final T value; + private final CommandConfirmation confirmation; NullFuture() { - this(null); + this((T) null); } NullFuture(T returnVal) { this.value = returnVal; + this.confirmation = new CommandConfirmation(); + } + + NullFuture(Throwable error) + { + this(error, null); + } + + NullFuture(Throwable error, T returnVal) + { + this.value = returnVal; + this.confirmation = new CommandConfirmation(error); } @Override public CommandConfirmation getConfirmation() throws CancellationException { - return new CommandConfirmation(); + return this.confirmation; } @Override public CommandConfirmation getConfirmation(long timeout, TimeUnit unit) { - return new CommandConfirmation(); + return this.getConfirmation(); } @Override @@ -742,7 +803,7 @@ public class LocalPlayer implements Player, MediaPlayerEventListener @Override public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return this.value; + return get(); } } diff --git a/interface/src/main/java/edu/regis/universeplayer/Player.java b/interface/src/main/java/edu/regis/universeplayer/player/Player.java similarity index 79% rename from interface/src/main/java/edu/regis/universeplayer/Player.java rename to interface/src/main/java/edu/regis/universeplayer/player/Player.java index 60ee44e..1f54cf4 100644 --- a/interface/src/main/java/edu/regis/universeplayer/Player.java +++ b/interface/src/main/java/edu/regis/universeplayer/player/Player.java @@ -2,8 +2,10 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -package edu.regis.universeplayer; +package edu.regis.universeplayer.player; +import edu.regis.universeplayer.PlaybackListener; +import edu.regis.universeplayer.PlaybackStatus; import edu.regis.universeplayer.browserCommands.QueryFuture; import edu.regis.universeplayer.data.Song; @@ -19,31 +21,6 @@ import java.util.HashMap; */ public interface Player { - HashMap, Player> REGISTERED_PLAYERS = new HashMap<>(); - - /** - * Obtains a song player that can play the provided song. - * - * @param song - The song to play. - * @return A compatible player, or null if none is found. - */ - static Player getCompatiblePlayer(Song song) - { - Player player = REGISTERED_PLAYERS.get(song.getClass()); - if (player == null) - { - for (Class songClass : REGISTERED_PLAYERS.keySet()) - { - if (songClass.isAssignableFrom(song.getClass())) - { - player = REGISTERED_PLAYERS.get(songClass); - break; - } - } - } - return player; - } - /** * Obtains the song currently playing. * diff --git a/interface/src/main/java/edu/regis/universeplayer/player/PlayerManager.java b/interface/src/main/java/edu/regis/universeplayer/player/PlayerManager.java new file mode 100644 index 0000000..f91af9b --- /dev/null +++ b/interface/src/main/java/edu/regis/universeplayer/player/PlayerManager.java @@ -0,0 +1,380 @@ +package edu.regis.universeplayer.player; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.swing.JOptionPane; + +import edu.regis.universeplayer.PlaybackListener; +import edu.regis.universeplayer.PlaybackStatus; +import edu.regis.universeplayer.browserCommands.CommandConfirmation; +import edu.regis.universeplayer.browserCommands.QueryFuture; +import edu.regis.universeplayer.data.InternetSong; +import edu.regis.universeplayer.data.LocalSong; +import edu.regis.universeplayer.data.PlaybackEvent; +import edu.regis.universeplayer.data.Queue; +import edu.regis.universeplayer.data.Song; +import edu.regis.universeplayer.gui.Interface; + +/** + * The PlayerManager serves as the central access point for playing songs of any + * type. + * + * @author William Hubbard + * @version 0.1 + */ +public class PlayerManager implements PlaybackListener +{ + private static final Logger logger = + LoggerFactory.getLogger(PlayerManager.class); + private static PlayerManager INSTANCE; + private LinkedList listeners = new LinkedList<>(); + + public static PlayerManager getPlayers() + { + if (INSTANCE == null) + { + INSTANCE = new PlayerManager(); + } + return INSTANCE; + } + + /** + * Links to other players. + */ + private final HashMap, Player> players = + new HashMap<>(); + + /** + * A reference to the player currently playing a song. + */ + private Player currentPlayer; + + /** + * A reference to the song currently being played. + */ + private Song currentSong; + + public PlayerManager() + { + this.initPlayers(); + } + + /** + * Obtains a song player that can play the provided song. + * + * @param song - The song to play. + * @return A compatible player, or null if none is found. + */ + public Player getCompatiblePlayer(Song song) + { + Player player = this.players.get(song.getClass()); + if (player == null) + { + for (Class songClass : this.players.keySet()) + { + if (songClass.isAssignableFrom(song.getClass())) + { + player = this.players.get(songClass); + break; + } + } + } + return player; + } + + /** + * Initializes the default players. + */ + private void initPlayers() + { + this.initPlayer(LocalSong.class, new LocalPlayer()); + this.initPlayer(InternetSong.class, new BrowserPlayer()); + } + + private void initPlayer(Class songType, + Player player) + { + boolean playerRegistered = this.players.containsValue(player); + this.players.put(songType, player); + if (!playerRegistered) + { + player.addPlaybackListener(this); + } + } + + /** + * Performs the necessary shutdown procedures for all players. + */ + public void shutdownPlayers() + { + this.players.values().forEach(Player::close); + } + + /** + * Obtains the active song player. + * + * @return - The player currently playing a song. + */ + protected Player getCurrentPlayer() + { + return this.currentPlayer; + } + + /** + * Obtains the active song. + * + * @return - The song currently playing. + */ + public Song getCurrentSong() + { + return this.currentSong; + } + + /** + * Creates a QueryFuture response for if no player is found. + * + * @param message - The message to send. + * @return A new future to return. + */ + private QueryFuture getNullPlayer(String message, T returnValue) + { + return new QueryFuture() + { + @Override + public CommandConfirmation getConfirmation() throws CancellationException, ExecutionException, InterruptedException + { + if (returnValue instanceof Throwable) + { + return new CommandConfirmation((Throwable) returnValue); + } + else + { + return new CommandConfirmation(message); + } + } + + @Override + public CommandConfirmation getConfirmation(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException + { + return this.getConfirmation(); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + return false; + } + + @Override + public boolean isCancelled() + { + return false; + } + + @Override + public boolean isDone() + { + return true; + } + + @Override + public T get() throws InterruptedException, ExecutionException + { + return returnValue; + } + + @Override + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException + { + return this.get(); + } + }; + } + + /** + * Creates a QueryFuture response for if no player is found. + * + * @param message - The error to send. + * @return A new future to return. + */ + private QueryFuture getNullPlayer(Throwable message, T returnValue) + { + return new QueryFuture() + { + @Override + public CommandConfirmation getConfirmation() throws CancellationException, ExecutionException, InterruptedException + { + return new CommandConfirmation(message); + } + + @Override + public CommandConfirmation getConfirmation(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException + { + return this.getConfirmation(); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + return false; + } + + @Override + public boolean isCancelled() + { + return false; + } + + @Override + public boolean isDone() + { + return true; + } + + @Override + public T get() throws InterruptedException, ExecutionException + { + return returnValue; + } + + @Override + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException + { + return this.get(); + } + }; + } + + public QueryFuture playSong(Song song) + { + if (this.currentSong != null) + { + this.currentPlayer.stopSong(); + } + this.currentSong = null; + this.currentPlayer = this.getCompatiblePlayer(song); + if (this.currentPlayer == null) + { + throw new IllegalArgumentException("Unknown song type " + song + .getClass()); + } + this.currentSong = song; + return this.currentPlayer.loadSong(song); + } + + public QueryFuture getStatus() + { + if (this.currentPlayer != null) + { + return this.currentPlayer.getStatus(); + } + return this.getNullPlayer("Command successful", PlaybackStatus.EMPTY); + } + + public QueryFuture seek(float time) + { + if (this.currentPlayer != null) + { + return this.currentPlayer.seek(time); + } + return null; + } + + public QueryFuture play() + { + if (this.currentPlayer != null && this.currentSong != null) + { + return this.currentPlayer.play(); + } + return null; + } + + public QueryFuture pause() + { + if (this.currentPlayer != null && this.currentSong != null) + { + return this.currentPlayer.pause(); + } + return null; + } + + public QueryFuture toggle() + { + if (this.currentPlayer != null && this.currentSong != null) + { + return this.currentPlayer.togglePlayback(); + } + return null; + } + + public QueryFuture stopSong() + { + if (this.currentPlayer != null && this.currentSong != null) + { + this.currentSong = null; + return this.currentPlayer.stopSong(); + } + return null; + } + + public void addPlaybackListener(PlaybackListener listener) + { + if (!this.hasPlaybackListener(listener)) + { + this.listeners.add(listener); + } + } + + public boolean hasPlaybackListener(PlaybackListener listener) + { + return this.listeners.contains(listener); + } + + public void removePlaybackListener(PlaybackListener listener) + { + this.listeners.remove(listener); + } + + /** + * Called when playback is changed. + * + * @param status - The playback status. + */ + @Override + public void onPlaybackChanged(PlaybackEvent status) + { + if (status.getSource() == this.currentPlayer) + { + for (PlaybackListener l : this.listeners) + { + l.onPlaybackChanged(status); + } + } + } + + /** + * Sends an error to the browser player. + * @param forward - Whether the error should be thrown in a foreground + * script or a background script. + * @return The command future. + */ + public QueryFuture throwError(boolean forward) + { + BrowserPlayer player = + (BrowserPlayer) this.players.get(InternetSong.class); + if (player == null) + { + return this.getNullPlayer(new NullPointerException( + "No browser found"), null); + } + return player.throwError(forward); + } +} diff --git a/interface/src/test/java/DBTest.java b/interface/src/test/java/DBTest.java index dfaf91a..dda8c7e 100644 --- a/interface/src/test/java/DBTest.java +++ b/interface/src/test/java/DBTest.java @@ -2,7 +2,8 @@ * Copyright (c) 2021 William Hubbard. All Rights Reserved. */ -import edu.regis.universeplayer.player.Interface; +import edu.regis.universeplayer.ConfigManager; + import org.junit.Test; import java.io.File; @@ -13,53 +14,4 @@ import static junit.framework.Assert.assertTrue; public class DBTest { - @Test - public void test() throws ClassNotFoundException, SQLException - { - try - { - Class.forName("org.sqlite.JDBC"); - Connection db; - Statement stat; - ResultSet tableResult; -// db = DriverManager.getConnection("jdbc:sqlite:" + new File(Interface.getDataDir().getAbsolutePath(), "universalmusictest.db").getAbsolutePath()); -// db.setAutoCommit(false); -// stat = db.createStatement(); -// tableResult = stat.executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='INFO';"); -// if (!tableResult.next()) -// { -// System.out.println("Creating song table"); -// /* -// * Create the table -// */ -// stat.executeUpdate("CREATE TABLE INFO" + -// "(DATA INTEGER PRIMARY KEY NOT NULL);"); -// } -// else -// { -// System.out.println("Clearing table"); -// stat.executeUpdate("DELETE FROM INFO;"); -// } -// tableResult.close(); -// stat.executeUpdate("INSERT INTO INFO (DATA) VALUES (1);"); -// stat.close(); -// db.commit(); -// db.close(); - - db = DriverManager.getConnection("jdbc:sqlite:" + new File(Interface.getDataDir().getAbsolutePath(), "universalmusictest.db").getAbsolutePath()); - db.setAutoCommit(false); - stat = db.createStatement(); - tableResult = stat.executeQuery("SELECT (DATA) FROM INFO"); - assertTrue(tableResult.next()); - assertEquals(1, tableResult.getInt("DATA")); - tableResult.close(); - stat.close(); - db.close(); - } - catch (SQLException e) - { - e.printStackTrace(); - throw e; - } - } }