Moves player logic to its own class, away from Interface
In the future, this will make testing and a CLI environment easier.
This commit is contained in:
@@ -44,5 +44,5 @@ sourceCompatibility = JavaVersion.VERSION_16
|
|||||||
targetCompatibility = JavaVersion.VERSION_16
|
targetCompatibility = JavaVersion.VERSION_16
|
||||||
|
|
||||||
// Define the main class for the application
|
// Define the main class for the application
|
||||||
mainClassName = defaultPackage + '.player.Interface'
|
mainClassName = defaultPackage + '.gui.Interface'
|
||||||
//mainClassName = defaultPackage + '.localPlayer.Player'
|
//mainClassName = defaultPackage + '.localPlayer.Player'
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
package edu.regis.universeplayer.data;
|
package edu.regis.universeplayer.data;
|
||||||
|
|
||||||
import edu.regis.universeplayer.player.Interface;
|
import edu.regis.universeplayer.ConfigManager;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -30,7 +31,8 @@ public class DatabaseManager
|
|||||||
if (db == null || db.isClosed())
|
if (db == null || db.isClosed())
|
||||||
{
|
{
|
||||||
Class.forName("org.sqlite.JDBC");
|
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();
|
warning = db.getWarnings();
|
||||||
while (warning != null)
|
while (warning != null)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
package edu.regis.universeplayer.data;
|
package edu.regis.universeplayer.data;
|
||||||
|
|
||||||
import edu.regis.universeplayer.player.Interface;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
package edu.regis.universeplayer.data;
|
package edu.regis.universeplayer.data;
|
||||||
|
|
||||||
import edu.regis.universeplayer.PlaybackInfo;
|
import edu.regis.universeplayer.PlaybackInfo;
|
||||||
import edu.regis.universeplayer.Player;
|
import edu.regis.universeplayer.player.Player;
|
||||||
import edu.regis.universeplayer.localPlayer.LocalPlayer;
|
|
||||||
|
|
||||||
import java.util.EventObject;
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package edu.regis.universeplayer.player;
|
package edu.regis.universeplayer.gui;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.LineBorder;
|
import javax.swing.border.LineBorder;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package edu.regis.universeplayer.player;
|
package edu.regis.universeplayer.gui;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.FocusAdapter;
|
import java.awt.event.FocusAdapter;
|
||||||
@@ -14,7 +14,7 @@ import java.util.stream.Collectors;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import com.wordpress.tips4java.ScrollablePanel;
|
import com.wordpress.tips4java.ScrollablePanel;
|
||||||
import edu.regis.universeplayer.ClickListener;
|
|
||||||
import edu.regis.universeplayer.data.Album;
|
import edu.regis.universeplayer.data.Album;
|
||||||
import edu.regis.universeplayer.data.CollectionType;
|
import edu.regis.universeplayer.data.CollectionType;
|
||||||
import edu.regis.universeplayer.data.Song;
|
import edu.regis.universeplayer.data.Song;
|
||||||
@@ -2,11 +2,10 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package edu.regis.universeplayer.player;
|
package edu.regis.universeplayer.gui;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -16,7 +15,6 @@ import javax.swing.*;
|
|||||||
|
|
||||||
import edu.regis.universeplayer.data.CollectionType;
|
import edu.regis.universeplayer.data.CollectionType;
|
||||||
import edu.regis.universeplayer.data.Song;
|
import edu.regis.universeplayer.data.Song;
|
||||||
import edu.regis.universeplayer.data.SongProvider;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This panel links to various song collections the player has set up.
|
* This panel links to various song collections the player has set up.
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package edu.regis.universeplayer.player;
|
package edu.regis.universeplayer.gui;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
@@ -2,19 +2,17 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* 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.Browser;
|
||||||
import edu.regis.universeplayer.browser.BrowserPlayer;
|
import edu.regis.universeplayer.player.BrowserPlayer;
|
||||||
import edu.regis.universeplayer.browserCommands.CommandError;
|
|
||||||
import edu.regis.universeplayer.browserCommands.QueryFuture;
|
import edu.regis.universeplayer.browserCommands.QueryFuture;
|
||||||
import edu.regis.universeplayer.data.InternetSong;
|
import edu.regis.universeplayer.data.InternetSong;
|
||||||
import edu.regis.universeplayer.data.Queue;
|
import edu.regis.universeplayer.data.Queue;
|
||||||
import edu.regis.universeplayer.data.*;
|
import edu.regis.universeplayer.data.*;
|
||||||
import edu.regis.universeplayer.localPlayer.LocalPlayer;
|
import edu.regis.universeplayer.player.LocalPlayer;
|
||||||
|
import edu.regis.universeplayer.player.PlayerManager;
|
||||||
import net.harawata.appdirs.AppDirsFactory;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -23,7 +21,6 @@ import javax.swing.*;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -51,10 +48,6 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
|
|
||||||
private static Interface INSTANCE;
|
private static Interface INSTANCE;
|
||||||
|
|
||||||
private static File dataDir;
|
|
||||||
private static File commDir;
|
|
||||||
private static File configDir;
|
|
||||||
|
|
||||||
public final ActionMap actions = new ActionMap();
|
public final ActionMap actions = new ActionMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,10 +75,6 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
*/
|
*/
|
||||||
private final PlayerControls controls;
|
private final PlayerControls controls;
|
||||||
|
|
||||||
/**
|
|
||||||
* A link to the browser.
|
|
||||||
*/
|
|
||||||
private final ArrayList<Player<?>> players = new ArrayList<>();
|
|
||||||
private int currentPlayer = -1;
|
private int currentPlayer = -1;
|
||||||
|
|
||||||
public static Interface getInstance()
|
public static Interface getInstance()
|
||||||
@@ -95,10 +84,8 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
|
|
||||||
public static void main(String[] args)
|
public static void main(String[] args)
|
||||||
{
|
{
|
||||||
Thread browserThread;
|
|
||||||
Interface inter = null;
|
Interface inter = null;
|
||||||
Browser browser;
|
PlayerManager.getPlayers();
|
||||||
BrowserPlayer browserPlayer;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -112,51 +99,6 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
inter.setSize(700, 500);
|
inter.setSize(700, 500);
|
||||||
SongProvider.INSTANCE.addUpdateListener(inter);
|
SongProvider.INSTANCE.addUpdateListener(inter);
|
||||||
inter.setVisible(true);
|
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<Future<Object>> pingRequests = new LinkedList<>();
|
|
||||||
// for (int i = 0; i < 20; i++)
|
|
||||||
// {
|
|
||||||
// logger.info("Sending ping");
|
|
||||||
// pingRequests.add(browser.sendObject("ping"));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// LinkedList<Future<Object>> toRemove = new LinkedList<>();
|
|
||||||
// while (pingRequests.size() > 0)
|
|
||||||
// {
|
|
||||||
// for (Future<Object> 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)
|
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
|
* Creates an interface
|
||||||
*/
|
*/
|
||||||
@@ -396,49 +262,35 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
@Override
|
@Override
|
||||||
protected Void doInBackground() throws Exception
|
protected Void doInBackground() throws Exception
|
||||||
{
|
{
|
||||||
Interface.this.players.stream()
|
QueryFuture<Void> command = PlayerManager.getPlayers().throwError(false);
|
||||||
.filter(p -> p instanceof BrowserPlayer)
|
try
|
||||||
.map(p -> (BrowserPlayer) p)
|
{
|
||||||
.findFirst()
|
if (!command.getConfirmation().wasSuccessful())
|
||||||
.ifPresentOrElse(p -> {
|
{
|
||||||
logger.debug("Throwing " +
|
logger.error("Could not run command",
|
||||||
"error");
|
command.getConfirmation()
|
||||||
QueryFuture<Void> command = p
|
.getError());
|
||||||
.throwError((false));
|
JOptionPane
|
||||||
try
|
.showMessageDialog(Interface.this,
|
||||||
{
|
command.getConfirmation()
|
||||||
if (!command
|
.getError(),
|
||||||
.getConfirmation()
|
command.getConfirmation()
|
||||||
.wasSuccessful())
|
.getMessage(),
|
||||||
{
|
JOptionPane.ERROR_MESSAGE);
|
||||||
logger.error("Could not run command",
|
}
|
||||||
command.getConfirmation()
|
}
|
||||||
.getError());
|
catch (ExecutionException | InterruptedException executionException)
|
||||||
JOptionPane
|
{
|
||||||
.showMessageDialog(Interface.this,
|
logger.error("Error" +
|
||||||
command.getConfirmation()
|
" creating " +
|
||||||
.getError(),
|
"debug " +
|
||||||
command.getConfirmation()
|
"message", executionException);
|
||||||
.getMessage(),
|
JOptionPane
|
||||||
JOptionPane.ERROR_MESSAGE);
|
.showMessageDialog(Interface.this, executionException
|
||||||
}
|
.getMessage(), langs
|
||||||
}
|
.getString(
|
||||||
catch (ExecutionException | InterruptedException executionException)
|
"error.command"), JOptionPane.ERROR_MESSAGE);
|
||||||
{
|
}
|
||||||
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));
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
@@ -578,7 +430,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
{
|
{
|
||||||
if (this.isEnabled())
|
if (this.isEnabled())
|
||||||
{
|
{
|
||||||
controls.play();
|
PlayerManager.getPlayers().play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -591,7 +443,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
{
|
{
|
||||||
if (this.isEnabled())
|
if (this.isEnabled())
|
||||||
{
|
{
|
||||||
controls.pause();
|
PlayerManager.getPlayers().pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -604,7 +456,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
{
|
{
|
||||||
if (this.isEnabled())
|
if (this.isEnabled())
|
||||||
{
|
{
|
||||||
controls.togglePlayback();
|
PlayerManager.getPlayers().toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -796,7 +648,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
@Override
|
@Override
|
||||||
public void windowClosing(WindowEvent windowEvent)
|
public void windowClosing(WindowEvent windowEvent)
|
||||||
{
|
{
|
||||||
this.players.forEach(Player::close);
|
PlayerManager.getPlayers().shutdownPlayers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* 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.InternetSong;
|
||||||
import edu.regis.universeplayer.data.InternetSongProvider;
|
import edu.regis.universeplayer.data.InternetSongProvider;
|
||||||
@@ -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.apache.logging.log4j.core.LogEvent;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -8,9 +7,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.FlowLayout;
|
import java.awt.FlowLayout;
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.WindowAdapter;
|
import java.awt.event.WindowAdapter;
|
||||||
@@ -18,16 +15,10 @@ import java.awt.event.WindowEvent;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Formatter;
|
import java.util.Formatter;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.MissingResourceException;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* 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.
|
* Contains possible playback commands that the play can send.
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package edu.regis.universeplayer.player;
|
package edu.regis.universeplayer.gui;
|
||||||
|
|
||||||
import java.util.EventListener;
|
import java.util.EventListener;
|
||||||
|
|
||||||
@@ -2,18 +2,17 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* 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.PlaybackListener;
|
||||||
import edu.regis.universeplayer.PlaybackStatus;
|
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.CommandConfirmation;
|
||||||
import edu.regis.universeplayer.browserCommands.CommandReturn;
|
|
||||||
import edu.regis.universeplayer.browserCommands.QueryFuture;
|
import edu.regis.universeplayer.browserCommands.QueryFuture;
|
||||||
import edu.regis.universeplayer.data.PlaybackEvent;
|
import edu.regis.universeplayer.data.PlaybackEvent;
|
||||||
import edu.regis.universeplayer.data.Queue;
|
import edu.regis.universeplayer.data.Queue;
|
||||||
import edu.regis.universeplayer.data.Song;
|
import edu.regis.universeplayer.data.Song;
|
||||||
|
import edu.regis.universeplayer.player.PlayerManager;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -46,12 +45,6 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
|
|||||||
.getBundle("lang.interface", Locale.getDefault());
|
.getBundle("lang.interface", Locale.getDefault());
|
||||||
private final ImageIcon PLAY_ICON, PAUSE_ICON;
|
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 playButton;
|
||||||
private final JButton nextButton;
|
private final JButton nextButton;
|
||||||
private final JButton prevButton;
|
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);
|
layout.putConstraint(SpringLayout.SOUTH, this, 5, SpringLayout.SOUTH, this.updateProgress);
|
||||||
|
|
||||||
Queue.getInstance().addSongChangeListener(this);
|
Queue.getInstance().addSongChangeListener(this);
|
||||||
|
PlayerManager.getPlayers().addPlaybackListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void seek(float value)
|
private void seek(float value)
|
||||||
{
|
{
|
||||||
this.service.execute(() -> {
|
this.service.execute(() -> {
|
||||||
if (this.currentPlayer != null)
|
QueryFuture<Void> command = PlayerManager.getPlayers().seek(value);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
QueryFuture<Void> command = this.currentPlayer.seek(value);
|
if (command != null && !command.getConfirmation()
|
||||||
try
|
.wasSuccessful())
|
||||||
{
|
{
|
||||||
if (!command.getConfirmation().wasSuccessful())
|
logger.error("Could not run command",
|
||||||
{
|
command.getConfirmation().getError());
|
||||||
JOptionPane.showMessageDialog(this,
|
JOptionPane.showMessageDialog(this,
|
||||||
command.getConfirmation().getError(),
|
command.getConfirmation().getError(),
|
||||||
command.getConfirmation().getMessage(),
|
command.getConfirmation().getMessage(),
|
||||||
JOptionPane.ERROR_MESSAGE);
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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);
|
this.triggerCommandListeners(PlaybackCommand.SEEK, value);
|
||||||
}
|
}
|
||||||
@@ -220,44 +214,42 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
|
|||||||
this.service.execute(() -> {
|
this.service.execute(() -> {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (this.currentPlayer != null)
|
QueryFuture<PlaybackStatus> status =
|
||||||
|
PlayerManager.getPlayers().getStatus();
|
||||||
|
CommandConfirmation confirmation = status.getConfirmation();
|
||||||
|
if (status.getConfirmation().wasSuccessful())
|
||||||
{
|
{
|
||||||
QueryFuture<PlaybackStatus> status =
|
switch (status.get())
|
||||||
this.currentPlayer.getStatus();
|
|
||||||
CommandConfirmation confirmation = status.getConfirmation();
|
|
||||||
if (status.getConfirmation().wasSuccessful())
|
|
||||||
{
|
{
|
||||||
switch (status.get())
|
case PAUSED -> confirmation = PlayerManager.getPlayers()
|
||||||
|
.play()
|
||||||
|
.getConfirmation();
|
||||||
|
case STOPPED, EMPTY -> {
|
||||||
|
if (Queue.getInstance().size() > 0)
|
||||||
{
|
{
|
||||||
case PAUSED -> confirmation = this.currentPlayer.play()
|
if (Queue.getInstance()
|
||||||
.getConfirmation();
|
.getCurrentSong() == null)
|
||||||
case STOPPED, EMPTY -> {
|
|
||||||
if (Queue.getInstance().size() > 0)
|
|
||||||
{
|
{
|
||||||
if (Queue.getInstance()
|
Queue.getInstance().skipToSong(0);
|
||||||
.getCurrentSong() == null)
|
}
|
||||||
{
|
else
|
||||||
Queue.getInstance().skipToSong(0);
|
{
|
||||||
}
|
confirmation = PlayerManager.getPlayers().play()
|
||||||
else
|
.getConfirmation();
|
||||||
{
|
|
||||||
confirmation = this.currentPlayer.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)
|
catch (ExecutionException | InterruptedException e)
|
||||||
{
|
{
|
||||||
@@ -275,29 +267,27 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
|
|||||||
this.service.execute(() -> {
|
this.service.execute(() -> {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (this.currentPlayer != null)
|
QueryFuture<PlaybackStatus> status =
|
||||||
|
PlayerManager.getPlayers().getStatus();
|
||||||
|
CommandConfirmation confirmation = status.getConfirmation();
|
||||||
|
if (status.getConfirmation().wasSuccessful())
|
||||||
{
|
{
|
||||||
QueryFuture<PlaybackStatus> status =
|
switch (status.get())
|
||||||
this.currentPlayer.getStatus();
|
|
||||||
CommandConfirmation confirmation = status.getConfirmation();
|
|
||||||
if (status.getConfirmation().wasSuccessful())
|
|
||||||
{
|
{
|
||||||
switch (status.get())
|
case PLAYING -> confirmation =
|
||||||
{
|
PlayerManager.getPlayers().pause()
|
||||||
case PLAYING -> confirmation =
|
.getConfirmation();
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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)
|
catch (ExecutionException | InterruptedException e)
|
||||||
{
|
{
|
||||||
@@ -318,46 +308,45 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
|
|||||||
this.service.execute(() -> {
|
this.service.execute(() -> {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (this.currentPlayer != null)
|
QueryFuture<PlaybackStatus> status =
|
||||||
|
PlayerManager.getPlayers().getStatus();
|
||||||
|
CommandConfirmation confirmation = status.getConfirmation();
|
||||||
|
if (status.getConfirmation().wasSuccessful())
|
||||||
{
|
{
|
||||||
QueryFuture<PlaybackStatus> status =
|
switch (status.get())
|
||||||
this.currentPlayer.getStatus();
|
|
||||||
CommandConfirmation confirmation = status.getConfirmation();
|
|
||||||
if (status.getConfirmation().wasSuccessful())
|
|
||||||
{
|
{
|
||||||
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()
|
if (Queue.getInstance()
|
||||||
.getConfirmation();
|
.getCurrentSong() == null)
|
||||||
case PLAYING -> confirmation = this.currentPlayer.pause()
|
|
||||||
.getConfirmation();
|
|
||||||
case STOPPED, EMPTY -> {
|
|
||||||
if (Queue.getInstance().size() > 0)
|
|
||||||
{
|
{
|
||||||
if (Queue.getInstance()
|
Queue.getInstance().skipToSong(0);
|
||||||
.getCurrentSong() == null)
|
}
|
||||||
{
|
else
|
||||||
Queue.getInstance().skipToSong(0);
|
{
|
||||||
}
|
confirmation = PlayerManager.getPlayers().play()
|
||||||
else
|
.getConfirmation();
|
||||||
{
|
|
||||||
confirmation = this.currentPlayer.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)
|
catch (ExecutionException | InterruptedException e)
|
||||||
{
|
{
|
||||||
@@ -450,103 +439,67 @@ public class PlayerControls extends JPanel implements Queue.SongChangeListener,
|
|||||||
@Override
|
@Override
|
||||||
public void onSongChange(Queue queue)
|
public void onSongChange(Queue queue)
|
||||||
{
|
{
|
||||||
if (this.currentSong != null)
|
this.service.execute(() -> {
|
||||||
{
|
QueryFuture<Void> command = PlayerManager.getPlayers().stopSong();
|
||||||
if (this.currentPlayer != null)
|
try
|
||||||
{
|
{
|
||||||
this.service.execute(() -> {
|
if (command != null && !command.getConfirmation().wasSuccessful())
|
||||||
QueryFuture<Void> 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))
|
|
||||||
{
|
{
|
||||||
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(() -> {
|
else
|
||||||
QueryFuture<Void> command =
|
{
|
||||||
this.currentPlayer.loadSong(this.currentSong);
|
command =
|
||||||
try
|
PlayerManager.getPlayers()
|
||||||
|
.playSong(queue.getCurrentSong());
|
||||||
|
if (!command.getConfirmation().wasSuccessful())
|
||||||
{
|
{
|
||||||
if (!command.getConfirmation().wasSuccessful())
|
JOptionPane.showMessageDialog(this,
|
||||||
{
|
command.getConfirmation().getError(),
|
||||||
JOptionPane.showMessageDialog(this,
|
command.getConfirmation().getMessage(),
|
||||||
command.getConfirmation().getError(),
|
JOptionPane.ERROR_MESSAGE);
|
||||||
command.getConfirmation().getMessage(),
|
logger.error("Could not run command",
|
||||||
JOptionPane.ERROR_MESSAGE);
|
command.getConfirmation().getError());
|
||||||
logger.error("Could not run command",
|
|
||||||
command.getConfirmation().getError());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (ExecutionException | InterruptedException e)
|
else
|
||||||
{
|
{
|
||||||
logger.error("Could not get current playback status", e);
|
SwingUtilities.invokeLater(() -> {
|
||||||
JOptionPane
|
this.playButton.setIcon(PLAY_ICON);
|
||||||
.showMessageDialog(this, e.getMessage(), langs
|
this.progress.setMaximum((int) (PlayerManager
|
||||||
.getString(
|
.getPlayers()
|
||||||
"error.command"), JOptionPane.ERROR_MESSAGE);
|
.getCurrentSong().duration / 1000));
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// this.currentPlayer.play();
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
catch (ExecutionException | InterruptedException e)
|
||||||
{
|
{
|
||||||
logger.error("No logger found for song {}", this.currentSong
|
logger.error("Could not get current playback status", e);
|
||||||
.getClass());
|
JOptionPane
|
||||||
|
.showMessageDialog(this, e.getMessage(), langs
|
||||||
|
.getString(
|
||||||
|
"error.command"), JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
else
|
|
||||||
{
|
|
||||||
this.currentPlayer = null;
|
|
||||||
this.progress.setMaximum(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlaybackChanged(PlaybackEvent status)
|
public void onPlaybackChanged(PlaybackEvent status)
|
||||||
{
|
{
|
||||||
if (status.getSource() != null && status
|
switch (status.getInfo().getStatus())
|
||||||
.getSource() == this.currentPlayer)
|
|
||||||
{
|
{
|
||||||
switch (status.getInfo().getStatus())
|
case PLAYING -> this.playButton.setIcon(PAUSE_ICON);
|
||||||
{
|
case FINISHED -> Queue.getInstance().skipNext();
|
||||||
case PLAYING -> this.playButton.setIcon(PAUSE_ICON);
|
case PAUSED, STOPPED, EMPTY -> this.playButton.setIcon(PLAY_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));
|
|
||||||
}
|
}
|
||||||
|
this.progress.setValue((int) status.getInfo().getPlayTime());
|
||||||
|
this.progress.setMaximum((int) (status.getInfo()
|
||||||
|
.getSong().duration / 1000));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package edu.regis.universeplayer.player;
|
package edu.regis.universeplayer.gui;
|
||||||
|
|
||||||
import com.wordpress.tips4java.ScrollablePanel;
|
import com.wordpress.tips4java.ScrollablePanel;
|
||||||
import edu.regis.universeplayer.ClickListener;
|
import edu.regis.universeplayer.ClickListener;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* 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.Collection;
|
||||||
import java.util.EventListener;
|
import java.util.EventListener;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package edu.regis.universeplayer.player;
|
package edu.regis.universeplayer.gui;
|
||||||
|
|
||||||
import com.wordpress.tips4java.ScrollablePanel;
|
import com.wordpress.tips4java.ScrollablePanel;
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* 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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -2,12 +2,12 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* 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.PlaybackInfo;
|
||||||
import edu.regis.universeplayer.PlaybackListener;
|
import edu.regis.universeplayer.PlaybackListener;
|
||||||
import edu.regis.universeplayer.PlaybackStatus;
|
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.browserCommands.*;
|
||||||
import edu.regis.universeplayer.data.InternetSong;
|
import edu.regis.universeplayer.data.InternetSong;
|
||||||
import edu.regis.universeplayer.data.PlaybackEvent;
|
import edu.regis.universeplayer.data.PlaybackEvent;
|
||||||
@@ -59,6 +59,22 @@ public class BrowserPlayer implements Player<InternetSong>, UpdateListener
|
|||||||
return browserRef;
|
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
|
@Override
|
||||||
public Song getCurrentSong()
|
public Song getCurrentSong()
|
||||||
{
|
{
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
package edu.regis.universeplayer.localPlayer;
|
package edu.regis.universeplayer.player;
|
||||||
|
|
||||||
import com.intervigil.wave.WaveReader;
|
import com.intervigil.wave.WaveReader;
|
||||||
|
|
||||||
import edu.regis.universeplayer.PlaybackInfo;
|
import edu.regis.universeplayer.PlaybackInfo;
|
||||||
import edu.regis.universeplayer.PlaybackListener;
|
import edu.regis.universeplayer.PlaybackListener;
|
||||||
import edu.regis.universeplayer.PlaybackStatus;
|
import edu.regis.universeplayer.PlaybackStatus;
|
||||||
import edu.regis.universeplayer.Player;
|
|
||||||
import edu.regis.universeplayer.browserCommands.CommandConfirmation;
|
import edu.regis.universeplayer.browserCommands.CommandConfirmation;
|
||||||
import edu.regis.universeplayer.browserCommands.QueryFuture;
|
import edu.regis.universeplayer.browserCommands.QueryFuture;
|
||||||
import edu.regis.universeplayer.data.LocalSong;
|
import edu.regis.universeplayer.data.LocalSong;
|
||||||
import edu.regis.universeplayer.data.PlaybackEvent;
|
import edu.regis.universeplayer.data.PlaybackEvent;
|
||||||
import edu.regis.universeplayer.data.Song;
|
import edu.regis.universeplayer.data.Song;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import uk.co.caprica.vlcj.factory.MediaPlayerFactory;
|
import uk.co.caprica.vlcj.factory.MediaPlayerFactory;
|
||||||
import uk.co.caprica.vlcj.media.MediaRef;
|
import uk.co.caprica.vlcj.media.MediaRef;
|
||||||
import uk.co.caprica.vlcj.media.TrackType;
|
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 uk.co.caprica.vlcj.player.component.AudioPlayerComponent;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@@ -39,7 +42,8 @@ import java.util.concurrent.*;
|
|||||||
*/
|
*/
|
||||||
public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
public class LocalPlayer implements Player<LocalSong>, 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 MediaPlayerFactory playerFactory;
|
||||||
private final AudioPlayerComponent player;
|
private final AudioPlayerComponent player;
|
||||||
@@ -73,7 +77,8 @@ public class LocalPlayer implements Player<LocalSong>, 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.
|
* @param file - The file to save to.
|
||||||
*/
|
*/
|
||||||
@@ -120,21 +125,35 @@ public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
|||||||
this.stopSong();
|
this.stopSong();
|
||||||
}
|
}
|
||||||
this.currentSong = song;
|
this.currentSong = song;
|
||||||
this.player.mediaPlayer().media().play(song.file.getAbsolutePath());
|
if (!this.player.mediaPlayer().media()
|
||||||
return new NullFuture<>();
|
.play(song.file.getAbsolutePath()))
|
||||||
|
{
|
||||||
|
return new NullFuture<>(new RuntimeException("Could not play " +
|
||||||
|
"song " + song));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new NullFuture<>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QueryFuture<Void> play()
|
public QueryFuture<Void> play()
|
||||||
{
|
{
|
||||||
this.player.mediaPlayer().submit((WrappedRunnable) () -> this.player.mediaPlayer().controls().play());
|
this.player.mediaPlayer()
|
||||||
|
.submit((WrappedRunnable) () -> this.player.mediaPlayer()
|
||||||
|
.controls()
|
||||||
|
.play());
|
||||||
return new NullFuture<>();
|
return new NullFuture<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QueryFuture<Void> pause()
|
public QueryFuture<Void> pause()
|
||||||
{
|
{
|
||||||
this.player.mediaPlayer().submit((WrappedRunnable) () -> this.player.mediaPlayer().controls().pause());
|
this.player.mediaPlayer()
|
||||||
|
.submit((WrappedRunnable) () -> this.player.mediaPlayer()
|
||||||
|
.controls()
|
||||||
|
.pause());
|
||||||
return new NullFuture<>();
|
return new NullFuture<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,15 +173,20 @@ public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
|||||||
@Override
|
@Override
|
||||||
public QueryFuture<Void> stopSong()
|
public QueryFuture<Void> stopSong()
|
||||||
{
|
{
|
||||||
this.player.mediaPlayer().submit((WrappedRunnable) () -> this.player.mediaPlayer().controls().stop());
|
this.player.mediaPlayer()
|
||||||
|
.submit((WrappedRunnable) () -> this.player.mediaPlayer()
|
||||||
|
.controls()
|
||||||
|
.stop());
|
||||||
return new NullFuture<>();
|
return new NullFuture<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QueryFuture<Void> seek(float time)
|
public QueryFuture<Void> seek(float time)
|
||||||
{
|
{
|
||||||
this.player.mediaPlayer().submit((WrappedRunnable) () -> this.player.mediaPlayer().controls()
|
this.player.mediaPlayer()
|
||||||
.setTime((long) (time * 1000)));
|
.submit((WrappedRunnable) () -> this.player.mediaPlayer()
|
||||||
|
.controls()
|
||||||
|
.setTime((long) (time * 1000)));
|
||||||
return new NullFuture<>();
|
return new NullFuture<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,13 +208,15 @@ public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
|||||||
@Override
|
@Override
|
||||||
public QueryFuture<Float> getCurrentTime()
|
public QueryFuture<Float> getCurrentTime()
|
||||||
{
|
{
|
||||||
return new NullFuture<>(this.player.mediaPlayer().status().time() / 1000F);
|
return new NullFuture<>(this.player.mediaPlayer().status()
|
||||||
|
.time() / 1000F);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QueryFuture<Float> getLength()
|
public QueryFuture<Float> getLength()
|
||||||
{
|
{
|
||||||
return new NullFuture<>(this.player.mediaPlayer().status().length() / 1000F);
|
return new NullFuture<>(this.player.mediaPlayer().status()
|
||||||
|
.length() / 1000F);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -227,7 +253,8 @@ public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
|||||||
* @param file - The file to read
|
* @param file - The file to read
|
||||||
* @return An raw stream for the file
|
* @return An raw stream for the file
|
||||||
* @throws FileNotFoundException - Thrown should the file not exist.
|
* @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
|
public static AudioFile getAudioStream(File file) throws IOException
|
||||||
{
|
{
|
||||||
@@ -235,12 +262,14 @@ public class LocalPlayer implements Player<LocalSong>, 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
|
* @param file - The file to convert
|
||||||
* @return An input stream containing the file data
|
* @return An input stream containing the file data
|
||||||
* @throws FileNotFoundException - Thrown should the file not exist.
|
* @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
|
protected static Process convertFile(File file) throws FileNotFoundException, IOException
|
||||||
{
|
{
|
||||||
@@ -304,7 +333,8 @@ public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
|||||||
/**
|
/**
|
||||||
* The media started playing.
|
* The media started playing.
|
||||||
* <p>
|
* <p>
|
||||||
* 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
|
* @param mediaPlayer media player that raised the event
|
||||||
*/
|
*/
|
||||||
@@ -312,7 +342,10 @@ public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
|||||||
public void playing(MediaPlayer mediaPlayer)
|
public void playing(MediaPlayer mediaPlayer)
|
||||||
{
|
{
|
||||||
logger.debug("Local player playing.");
|
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<LocalSong>, MediaPlayerEventListener
|
|||||||
public void paused(MediaPlayer mediaPlayer)
|
public void paused(MediaPlayer mediaPlayer)
|
||||||
{
|
{
|
||||||
logger.debug("Local player paused.");
|
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.
|
* Media stopped.
|
||||||
* <p>
|
* <p>
|
||||||
* A stopped event may be raised under certain circumstances even if the media player is not playing (e.g. as part
|
* A stopped event may be raised under certain circumstances even if the
|
||||||
* of the associated media list player sub-item handling). Client applications must therefore be prepared to handle
|
* media player is not playing (e.g. as part of the associated media list
|
||||||
* such a situation.
|
* player sub-item handling). Client applications must therefore be prepared
|
||||||
|
* to handle such a situation.
|
||||||
*
|
*
|
||||||
* @param mediaPlayer media player that raised the event
|
* @param mediaPlayer media player that raised the event
|
||||||
*/
|
*/
|
||||||
@@ -340,7 +377,10 @@ public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
|||||||
public void stopped(MediaPlayer mediaPlayer)
|
public void stopped(MediaPlayer mediaPlayer)
|
||||||
{
|
{
|
||||||
logger.debug("Local player stopped prematurely.");
|
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<LocalSong>, MediaPlayerEventListener
|
|||||||
public void finished(MediaPlayer mediaPlayer)
|
public void finished(MediaPlayer mediaPlayer)
|
||||||
{
|
{
|
||||||
logger.debug("Local player finished.");
|
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<LocalSong>, MediaPlayerEventListener
|
|||||||
@Override
|
@Override
|
||||||
public void timeChanged(MediaPlayer mediaPlayer, long newTime)
|
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<LocalSong>, MediaPlayerEventListener
|
|||||||
* other application) starts/stops playing media.
|
* other application) starts/stops playing media.
|
||||||
*
|
*
|
||||||
* @param mediaPlayer media player that raised the event
|
* @param mediaPlayer media player that raised the event
|
||||||
* @param corked <code>true</code> if corked; otherwise <code>false</code>
|
* @param corked <code>true</code> if corked; otherwise
|
||||||
|
* <code>false</code>
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void corked(MediaPlayer mediaPlayer, boolean corked)
|
public void corked(MediaPlayer mediaPlayer, boolean corked)
|
||||||
@@ -543,7 +589,8 @@ public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
|||||||
* The audio was muted/un-muted.
|
* The audio was muted/un-muted.
|
||||||
*
|
*
|
||||||
* @param mediaPlayer media player that raised the event
|
* @param mediaPlayer media player that raised the event
|
||||||
* @param muted <code>true</code> if muted; otherwise <code>false</code>
|
* @param muted <code>true</code> if muted; otherwise
|
||||||
|
* <code>false</code>
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void muted(MediaPlayer mediaPlayer, boolean muted)
|
public void muted(MediaPlayer mediaPlayer, boolean muted)
|
||||||
@@ -609,9 +656,10 @@ public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
|||||||
* The event will be fired again if the media is played again after a native
|
* The event will be fired again if the media is played again after a native
|
||||||
* stopped or finished event is received.
|
* stopped or finished event is received.
|
||||||
* <p>
|
* <p>
|
||||||
* Waiting for this event may be more reliable than using {@link #playing(MediaPlayer)}
|
* Waiting for this event may be more reliable than using {@link
|
||||||
* or {@link #videoOutput(MediaPlayer, int)} in some cases (logo and marquee
|
* #playing(MediaPlayer)} or {@link #videoOutput(MediaPlayer, int)} in some
|
||||||
* already mentioned, also setting audio tracks, sub-title tracks and so on).
|
* cases (logo and marquee already mentioned, also setting audio tracks,
|
||||||
|
* sub-title tracks and so on).
|
||||||
*
|
*
|
||||||
* @param mediaPlayer media player that raised the event
|
* @param mediaPlayer media player that raised the event
|
||||||
*/
|
*/
|
||||||
@@ -692,27 +740,40 @@ public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
|||||||
private static class NullFuture<T> implements QueryFuture<T>
|
private static class NullFuture<T> implements QueryFuture<T>
|
||||||
{
|
{
|
||||||
private final T value;
|
private final T value;
|
||||||
|
private final CommandConfirmation confirmation;
|
||||||
|
|
||||||
NullFuture()
|
NullFuture()
|
||||||
{
|
{
|
||||||
this(null);
|
this((T) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
NullFuture(T returnVal)
|
NullFuture(T returnVal)
|
||||||
{
|
{
|
||||||
this.value = 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
|
@Override
|
||||||
public CommandConfirmation getConfirmation() throws CancellationException
|
public CommandConfirmation getConfirmation() throws CancellationException
|
||||||
{
|
{
|
||||||
return new CommandConfirmation();
|
return this.confirmation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandConfirmation getConfirmation(long timeout, TimeUnit unit)
|
public CommandConfirmation getConfirmation(long timeout, TimeUnit unit)
|
||||||
{
|
{
|
||||||
return new CommandConfirmation();
|
return this.getConfirmation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -742,7 +803,7 @@ public class LocalPlayer implements Player<LocalSong>, MediaPlayerEventListener
|
|||||||
@Override
|
@Override
|
||||||
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
||||||
{
|
{
|
||||||
return this.value;
|
return get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2,8 +2,10 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* 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.browserCommands.QueryFuture;
|
||||||
import edu.regis.universeplayer.data.Song;
|
import edu.regis.universeplayer.data.Song;
|
||||||
|
|
||||||
@@ -19,31 +21,6 @@ import java.util.HashMap;
|
|||||||
*/
|
*/
|
||||||
public interface Player<T extends Song>
|
public interface Player<T extends Song>
|
||||||
{
|
{
|
||||||
HashMap<Class<? extends Song>, 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<? extends Song> songClass : REGISTERED_PLAYERS.keySet())
|
|
||||||
{
|
|
||||||
if (songClass.isAssignableFrom(song.getClass()))
|
|
||||||
{
|
|
||||||
player = REGISTERED_PLAYERS.get(songClass);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains the song currently playing.
|
* Obtains the song currently playing.
|
||||||
*
|
*
|
||||||
@@ -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<PlaybackListener> listeners = new LinkedList<>();
|
||||||
|
|
||||||
|
public static PlayerManager getPlayers()
|
||||||
|
{
|
||||||
|
if (INSTANCE == null)
|
||||||
|
{
|
||||||
|
INSTANCE = new PlayerManager();
|
||||||
|
}
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Links to other players.
|
||||||
|
*/
|
||||||
|
private final HashMap<Class<? extends Song>, 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<? extends Song> 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 <T extends Song> void initPlayer(Class<T> songType,
|
||||||
|
Player<T> 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 <T> QueryFuture<T> getNullPlayer(String message, T returnValue)
|
||||||
|
{
|
||||||
|
return new QueryFuture<T>()
|
||||||
|
{
|
||||||
|
@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 <T> QueryFuture<T> getNullPlayer(Throwable message, T returnValue)
|
||||||
|
{
|
||||||
|
return new QueryFuture<T>()
|
||||||
|
{
|
||||||
|
@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<Void> 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<PlaybackStatus> getStatus()
|
||||||
|
{
|
||||||
|
if (this.currentPlayer != null)
|
||||||
|
{
|
||||||
|
return this.currentPlayer.getStatus();
|
||||||
|
}
|
||||||
|
return this.getNullPlayer("Command successful", PlaybackStatus.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryFuture<Void> seek(float time)
|
||||||
|
{
|
||||||
|
if (this.currentPlayer != null)
|
||||||
|
{
|
||||||
|
return this.currentPlayer.seek(time);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryFuture<Void> play()
|
||||||
|
{
|
||||||
|
if (this.currentPlayer != null && this.currentSong != null)
|
||||||
|
{
|
||||||
|
return this.currentPlayer.play();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryFuture<Void> pause()
|
||||||
|
{
|
||||||
|
if (this.currentPlayer != null && this.currentSong != null)
|
||||||
|
{
|
||||||
|
return this.currentPlayer.pause();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryFuture<Void> toggle()
|
||||||
|
{
|
||||||
|
if (this.currentPlayer != null && this.currentSong != null)
|
||||||
|
{
|
||||||
|
return this.currentPlayer.togglePlayback();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryFuture<Void> 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<Void> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import edu.regis.universeplayer.player.Interface;
|
import edu.regis.universeplayer.ConfigManager;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -13,53 +14,4 @@ import static junit.framework.Assert.assertTrue;
|
|||||||
|
|
||||||
public class DBTest
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user