Added volume controls and threaded library refresh. Setup for callbacks related to database update functions.
This commit is contained in:
parent
6ef92096e9
commit
24070231b6
6
src/main/java/musicplayer/LibraryCallbackInterface.java
Normal file
6
src/main/java/musicplayer/LibraryCallbackInterface.java
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package musicplayer;
|
||||||
|
|
||||||
|
public interface LibraryCallbackInterface {
|
||||||
|
|
||||||
|
void libraryUpdated(boolean successful);
|
||||||
|
}
|
@ -1,9 +1,7 @@
|
|||||||
package musicplayer;
|
package musicplayer;
|
||||||
|
|
||||||
import musicplayer.db.DatabaseManager;
|
|
||||||
import musicplayer.db.Gateway;
|
import musicplayer.db.Gateway;
|
||||||
import musicplayer.model.ExtractedMetadata;
|
import musicplayer.model.ExtractedMetadata;
|
||||||
import musicplayer.model.Song;
|
|
||||||
import org.jaudiotagger.audio.AudioFileIO;
|
import org.jaudiotagger.audio.AudioFileIO;
|
||||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||||
@ -11,52 +9,15 @@ import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
|||||||
import org.jaudiotagger.tag.Tag;
|
import org.jaudiotagger.tag.Tag;
|
||||||
import org.jaudiotagger.tag.TagException;
|
import org.jaudiotagger.tag.TagException;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public final class LibraryUtils {
|
||||||
public class Application {
|
|
||||||
|
|
||||||
static final String musicFileExtensionRegex = ".*\\.(mp3|mp4|flac)";
|
static final String musicFileExtensionRegex = ".*\\.(mp3|mp4|flac)";
|
||||||
|
|
||||||
public Application() {
|
|
||||||
DatabaseManager.init();
|
|
||||||
List<Song> songs = Gateway.listAllSongs().get();
|
|
||||||
boolean running = true;
|
|
||||||
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
|
|
||||||
Player player = new Player(null);
|
|
||||||
while (running) {
|
|
||||||
try {
|
|
||||||
System.out.println("Ready to read");
|
|
||||||
String input = br.readLine();
|
|
||||||
System.out.println(input);
|
|
||||||
switch (input) {
|
|
||||||
case "play":
|
|
||||||
player.playSong(songs.get(0));
|
|
||||||
break;
|
|
||||||
case "stop":
|
|
||||||
player.stop();
|
|
||||||
break;
|
|
||||||
case "pause":
|
|
||||||
player.pause();
|
|
||||||
break;
|
|
||||||
case "resume":
|
|
||||||
player.resume();
|
|
||||||
break;
|
|
||||||
case "quit":
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Walk through all files and directories recursively and index any music files with a correct extension
|
* Walk through all files and directories recursively and index any music files with a correct extension
|
||||||
*
|
*
|
||||||
@ -66,7 +27,7 @@ public class Application {
|
|||||||
try {
|
try {
|
||||||
Files.walk(rootDirectory)
|
Files.walk(rootDirectory)
|
||||||
.filter(f -> f.toString().matches(musicFileExtensionRegex))
|
.filter(f -> f.toString().matches(musicFileExtensionRegex))
|
||||||
.map(Application::autoParse).filter(Optional::isPresent).map(Optional::get).forEach(Gateway::addSong);
|
.map(LibraryUtils::autoParse).filter(Optional::isPresent).map(Optional::get).forEach(Gateway::addSong);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -78,7 +39,7 @@ public class Application {
|
|||||||
* @param targetFile Path to file to extract metadata from.
|
* @param targetFile Path to file to extract metadata from.
|
||||||
* @return Metadata contained in targetFile.
|
* @return Metadata contained in targetFile.
|
||||||
*/
|
*/
|
||||||
public static Optional<ExtractedMetadata> autoParse(Path targetFile) {
|
private static Optional<ExtractedMetadata> autoParse(Path targetFile) {
|
||||||
Tag audioTags = null;
|
Tag audioTags = null;
|
||||||
try {
|
try {
|
||||||
audioTags = AudioFileIO.read(targetFile.toFile()).getTag();
|
audioTags = AudioFileIO.read(targetFile.toFile()).getTag();
|
@ -101,6 +101,21 @@ public class Player {
|
|||||||
return playBin.isPlaying() ? (int) (playBin.getClock().getTime().toSeconds() - playBin.getBaseTime().toSeconds()) : 0;
|
return playBin.isPlaying() ? (int) (playBin.getClock().getTime().toSeconds() - playBin.getBaseTime().toSeconds()) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the player volume.
|
||||||
|
* @param volume New volume in percent.
|
||||||
|
*/
|
||||||
|
public void setVolume(int volume){
|
||||||
|
playBin.setVolumePercent(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Current Player volume in percent.
|
||||||
|
*/
|
||||||
|
public int getVolume(){
|
||||||
|
return playBin.getVolumePercent();
|
||||||
|
}
|
||||||
|
|
||||||
private class InternalThread implements Runnable {
|
private class InternalThread implements Runnable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="musicplayer.PlayerGUI">
|
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="musicplayer.PlayerGUI">
|
||||||
<grid id="27dc6" binding="mainPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
<grid id="27dc6" binding="mainPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
<margin top="0" left="0" bottom="0" right="0"/>
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<xy x="20" y="20" width="500" height="400"/>
|
<xy x="20" y="20" width="500" height="400"/>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<children>
|
<children>
|
||||||
<splitpane id="79ccb">
|
<splitpane id="79ccb">
|
||||||
<constraints>
|
<constraints>
|
||||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
|
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
|
||||||
<preferred-size width="760" height="500"/>
|
<preferred-size width="760" height="500"/>
|
||||||
</grid>
|
</grid>
|
||||||
</constraints>
|
</constraints>
|
||||||
@ -28,7 +28,10 @@
|
|||||||
<children>
|
<children>
|
||||||
<component id="6b9fd" class="javax.swing.JTree" binding="libraryView">
|
<component id="6b9fd" class="javax.swing.JTree" binding="libraryView">
|
||||||
<constraints/>
|
<constraints/>
|
||||||
<properties/>
|
<properties>
|
||||||
|
<rootVisible value="true"/>
|
||||||
|
<showsRootHandles value="false"/>
|
||||||
|
</properties>
|
||||||
</component>
|
</component>
|
||||||
</children>
|
</children>
|
||||||
</scrollpane>
|
</scrollpane>
|
||||||
@ -51,7 +54,7 @@
|
|||||||
</splitpane>
|
</splitpane>
|
||||||
<component id="98c55" class="javax.swing.JSlider" binding="seekBar">
|
<component id="98c55" class="javax.swing.JSlider" binding="seekBar">
|
||||||
<constraints>
|
<constraints>
|
||||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
|
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<properties>
|
<properties>
|
||||||
<inverted value="false"/>
|
<inverted value="false"/>
|
||||||
@ -64,7 +67,7 @@
|
|||||||
</component>
|
</component>
|
||||||
<toolbar id="56df">
|
<toolbar id="56df">
|
||||||
<constraints>
|
<constraints>
|
||||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
|
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
|
||||||
<preferred-size width="-1" height="20"/>
|
<preferred-size width="-1" height="20"/>
|
||||||
</grid>
|
</grid>
|
||||||
</constraints>
|
</constraints>
|
||||||
@ -91,8 +94,23 @@
|
|||||||
<text value="&Next"/>
|
<text value="&Next"/>
|
||||||
</properties>
|
</properties>
|
||||||
</component>
|
</component>
|
||||||
|
<component id="c8ecb" class="javax.swing.JSlider" binding="volumeSlider">
|
||||||
|
<constraints/>
|
||||||
|
<properties>
|
||||||
|
<majorTickSpacing value="20"/>
|
||||||
|
<minorTickSpacing value="10"/>
|
||||||
|
<paintTicks value="true"/>
|
||||||
|
<value value="100"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
</children>
|
</children>
|
||||||
</toolbar>
|
</toolbar>
|
||||||
|
<component id="e1626" class="javax.swing.JMenuBar" binding="menuBar">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
</component>
|
||||||
</children>
|
</children>
|
||||||
</grid>
|
</grid>
|
||||||
</form>
|
</form>
|
||||||
|
@ -12,7 +12,7 @@ import javax.swing.tree.TreeNode;
|
|||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class PlayerGUI implements PlayerCallbackInterface{
|
public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterface{
|
||||||
private JTree libraryView;
|
private JTree libraryView;
|
||||||
private JTable playList;
|
private JTable playList;
|
||||||
private JPanel mainPanel;
|
private JPanel mainPanel;
|
||||||
@ -20,6 +20,8 @@ public class PlayerGUI implements PlayerCallbackInterface{
|
|||||||
private JButton stopButton;
|
private JButton stopButton;
|
||||||
private JButton nextButton;
|
private JButton nextButton;
|
||||||
private JSlider seekBar;
|
private JSlider seekBar;
|
||||||
|
private JSlider volumeSlider;
|
||||||
|
private JMenuBar menuBar;
|
||||||
private Player player = new Player(this);
|
private Player player = new Player(this);
|
||||||
private PlaylistTableModel playlistTableModel = new PlaylistTableModel(new ArrayList<>());
|
private PlaylistTableModel playlistTableModel = new PlaylistTableModel(new ArrayList<>());
|
||||||
|
|
||||||
@ -35,23 +37,12 @@ public class PlayerGUI implements PlayerCallbackInterface{
|
|||||||
|
|
||||||
public PlayerGUI() {
|
public PlayerGUI() {
|
||||||
DatabaseManager.init();
|
DatabaseManager.init();
|
||||||
populateLibrary(new TreeMap<>(Gateway.listAllSongsGroupedByAlbum().get()));
|
|
||||||
libraryView.setRootVisible(false);
|
libraryView.setRootVisible(false);
|
||||||
libraryView.setToggleClickCount(1);
|
libraryView.setToggleClickCount(1);
|
||||||
libraryView.setCellRenderer(new LibraryTreeCellRenderer());
|
libraryView.setCellRenderer(new LibraryTreeCellRenderer());
|
||||||
|
|
||||||
MouseListener mouseListener = new libraryMouseAdapter();
|
|
||||||
|
|
||||||
libraryView.addMouseListener(mouseListener);
|
|
||||||
playList.setModel(playlistTableModel);
|
playList.setModel(playlistTableModel);
|
||||||
playButton.addActionListener(e -> {
|
resetTree();
|
||||||
if (playList.getSelectedColumn() == -1) {
|
volumeSlider.setValue(player.getVolume());
|
||||||
player.playSong(playlistTableModel.getFirst().get());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
stopButton.addActionListener(e -> player.stop());
|
|
||||||
nextButton.addActionListener(e -> playNextSong());
|
|
||||||
|
|
||||||
Thread seekBarUpdater = new Thread(() -> {
|
Thread seekBarUpdater = new Thread(() -> {
|
||||||
boolean running = true;
|
boolean running = true;
|
||||||
while (running) {
|
while (running) {
|
||||||
@ -64,6 +55,20 @@ public class PlayerGUI implements PlayerCallbackInterface{
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
seekBarUpdater.start();
|
seekBarUpdater.start();
|
||||||
|
|
||||||
|
//Action Listeners
|
||||||
|
playButton.addActionListener(e -> {
|
||||||
|
if (playList.getSelectedColumn() == -1) {
|
||||||
|
player.playSong(playlistTableModel.getFirst().get());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
MouseListener mouseListener = new libraryMouseAdapter();
|
||||||
|
libraryView.addMouseListener(mouseListener);
|
||||||
|
stopButton.addActionListener(e -> player.stop());
|
||||||
|
nextButton.addActionListener(e -> playNextSong());
|
||||||
|
volumeSlider.addChangeListener(e -> player.setVolume(((JSlider)e.getSource()).getValue()));
|
||||||
|
|
||||||
|
populateMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -167,6 +172,23 @@ public class PlayerGUI implements PlayerCallbackInterface{
|
|||||||
SwingUtilities.invokeLater(() -> seekBar.setMaximum(seconds));
|
SwingUtilities.invokeLater(() -> seekBar.setMaximum(seconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void refreshLibrary(){
|
||||||
|
DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) libraryView.getModel().getRoot();
|
||||||
|
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("Refreshing Library...");
|
||||||
|
addNodeToTreeModel(parentNode, newNode);
|
||||||
|
Thread populateThread = new Thread(() -> {
|
||||||
|
TreeMap treeMap = new TreeMap<>(Gateway.listAllSongsGroupedByAlbum().get());
|
||||||
|
SwingUtilities.invokeLater(() -> populateLibrary(treeMap));
|
||||||
|
});
|
||||||
|
populateThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void libraryUpdated(boolean successful) {
|
||||||
|
if(successful)
|
||||||
|
populateLibrary(new TreeMap<>(Gateway.listAllSongsGroupedByAlbum().get()));
|
||||||
|
}
|
||||||
|
|
||||||
private class libraryMouseAdapter extends MouseAdapter {
|
private class libraryMouseAdapter extends MouseAdapter {
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed(MouseEvent e) {
|
public void mousePressed(MouseEvent e) {
|
||||||
@ -187,4 +209,25 @@ public class PlayerGUI implements PlayerCallbackInterface{
|
|||||||
} catch (NullPointerException ignored) {}
|
} catch (NullPointerException ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate the top menu bar with items.
|
||||||
|
*/
|
||||||
|
private void populateMenuBar(){
|
||||||
|
|
||||||
|
// Tools menu
|
||||||
|
JMenu tools = new JMenu("Tools");
|
||||||
|
JMenuItem menuItem = new JMenuItem("Refresh Library");
|
||||||
|
menuItem.addActionListener(e -> refreshLibrary());
|
||||||
|
tools.add(menuItem);
|
||||||
|
|
||||||
|
menuItem = new JMenuItem("Update Library");
|
||||||
|
menuItem.setToolTipText("Reindex and refresh library.");
|
||||||
|
|
||||||
|
tools.add(menuItem);
|
||||||
|
|
||||||
|
// Add everything to the menu bar itself
|
||||||
|
menuBar.add(tools);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ public class PlaylistTableModel extends AbstractTableModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addSong(Song song){
|
public void addSong(Song song){
|
||||||
System.out.println("Adding " + song.toString());
|
|
||||||
songList.add(song);
|
songList.add(song);
|
||||||
fireTableDataChanged();
|
fireTableDataChanged();
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ public class ExtractedMetadata {
|
|||||||
public final String songFile;
|
public final String songFile;
|
||||||
public final String trackNumber;
|
public final String trackNumber;
|
||||||
public final byte[] artwork;
|
public final byte[] artwork;
|
||||||
|
public final String discNumber;
|
||||||
|
|
||||||
private static final String albumArtRegex = ".*(Cover|Folder|cover|folder)\\.(jpg|png)";
|
private static final String albumArtRegex = ".*(Cover|Folder|cover|folder)\\.(jpg|png)";
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ public class ExtractedMetadata {
|
|||||||
this.album = audioTags.getFirst(FieldKey.ALBUM);
|
this.album = audioTags.getFirst(FieldKey.ALBUM);
|
||||||
this.artist = audioTags.getFirst(FieldKey.ARTIST);
|
this.artist = audioTags.getFirst(FieldKey.ARTIST);
|
||||||
this.genre = audioTags.getFirst(FieldKey.GENRE);
|
this.genre = audioTags.getFirst(FieldKey.GENRE);
|
||||||
|
this.discNumber = audioTags.getFirst(FieldKey.DISC_NO);
|
||||||
this.songFile = songFile.getAbsolutePath();
|
this.songFile = songFile.getAbsolutePath();
|
||||||
byte[] tmpArt = null;
|
byte[] tmpArt = null;
|
||||||
try {
|
try {
|
||||||
@ -54,16 +56,25 @@ public class ExtractedMetadata {
|
|||||||
artwork = tmpArt;
|
artwork = tmpArt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an Image into its' byte array representation.
|
||||||
|
* @param image Image to convert.
|
||||||
|
* @return image as byte[].
|
||||||
|
*/
|
||||||
private static byte[] convertImageToBytes(Image image){
|
private static byte[] convertImageToBytes(Image image){
|
||||||
int imageScaleToSize = 50;
|
int imageScaleToSize = 50;
|
||||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){
|
||||||
ImageIO.write(convertToBufferedImage(image.getScaledInstance(imageScaleToSize, imageScaleToSize, Image.SCALE_SMOOTH)), "jpg", baos);
|
ImageIO.write(convertToBufferedImage(image.getScaledInstance(imageScaleToSize, imageScaleToSize, Image.SCALE_SMOOTH)), "jpg", baos);
|
||||||
baos.flush();
|
baos.flush();
|
||||||
return baos.toByteArray();
|
return baos.toByteArray();
|
||||||
} catch (IOException ignored) {}
|
} catch (IOException e) { return null; }
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an Image to a BufferedImage.
|
||||||
|
* @param image Source image.
|
||||||
|
* @return image as BufferedImage.
|
||||||
|
*/
|
||||||
private static BufferedImage convertToBufferedImage(Image image) {
|
private static BufferedImage convertToBufferedImage(Image image) {
|
||||||
BufferedImage newImage = new BufferedImage(
|
BufferedImage newImage = new BufferedImage(
|
||||||
image.getWidth(null), image.getHeight(null),
|
image.getWidth(null), image.getHeight(null),
|
||||||
|
Loading…
Reference in New Issue
Block a user