Refactored some code to more sensible locations.
Made IDatabase abstract rather than an interface and self referential for implemented methods.
This commit is contained in:
parent
12a9af2ba3
commit
82d393d3b5
12
pom.xml
12
pom.xml
@ -134,9 +134,15 @@
|
|||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>slf4j-simple</artifactId>
|
<artifactId>log4j-core</artifactId>
|
||||||
<version>1.7.21</version>
|
<version>2.6.2</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-slf4j-impl</artifactId>
|
||||||
|
<version>2.6.2</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -4,7 +4,7 @@ import musicplayer.model.Song;
|
|||||||
|
|
||||||
public class StartPlayingException extends Exception {
|
public class StartPlayingException extends Exception {
|
||||||
|
|
||||||
private Song song;
|
private final Song song;
|
||||||
|
|
||||||
public StartPlayingException(Song song){
|
public StartPlayingException(Song song){
|
||||||
this.song = song;
|
this.song = song;
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package musicplayer.callbacks;
|
|
||||||
|
|
||||||
public interface LibraryCallbackInterface {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this after performing library updates so the front-end knows that updating has completed.
|
|
||||||
* @param message Error message.
|
|
||||||
*/
|
|
||||||
void libraryUpdated(String message);
|
|
||||||
/**
|
|
||||||
* Call this after performing library updates so the front-end knows that updating has completed.
|
|
||||||
*/
|
|
||||||
void libraryUpdated();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the current display with the file/folder currently being parsed.
|
|
||||||
* @param name Data about the file/folder to be shown to the user.
|
|
||||||
*/
|
|
||||||
void currentlyUpdating(String name);
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ package musicplayer.db;
|
|||||||
|
|
||||||
import musicplayer.util.ConfigManager;
|
import musicplayer.util.ConfigManager;
|
||||||
import musicplayer.model.*;
|
import musicplayer.model.*;
|
||||||
import org.hibernate.Criteria;
|
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.SessionFactory;
|
import org.hibernate.SessionFactory;
|
||||||
import org.hibernate.cfg.Configuration;
|
import org.hibernate.cfg.Configuration;
|
||||||
@ -20,7 +19,7 @@ import java.util.Properties;
|
|||||||
/**
|
/**
|
||||||
* Class for managing connection to Hibernate.
|
* Class for managing connection to Hibernate.
|
||||||
*/
|
*/
|
||||||
public class HibernateDatabase implements IDatabase{
|
public class HibernateDatabase extends IDatabase{
|
||||||
|
|
||||||
private SessionFactory sessionFactory;
|
private SessionFactory sessionFactory;
|
||||||
|
|
||||||
|
@ -1,16 +1,48 @@
|
|||||||
package musicplayer.db;
|
package musicplayer.db;
|
||||||
|
|
||||||
|
import musicplayer.library.ILibrary;
|
||||||
import musicplayer.model.*;
|
import musicplayer.model.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public interface IDatabase {
|
public abstract class IDatabase {
|
||||||
Optional<Album> getOneAlbum(String name);
|
abstract public Optional<Album> getOneAlbum(String name);
|
||||||
Optional<Artist> getOneArtist(String name);
|
abstract public Optional<Artist> getOneArtist(String name);
|
||||||
Optional<Song> getOneSong(ExtractedMetadata metadata);
|
abstract public Optional<Song> getOneSong(ExtractedMetadata metadata);
|
||||||
void batchDeleteNot(Long[] validIds);
|
abstract public void batchDeleteNot(Long[] validIds);
|
||||||
<T extends IDBType> Optional<List<T>> listAllT(Class<T> typeClass);
|
abstract public <T extends IDBType> Optional<List<T>> listAllT(Class<T> typeClass);
|
||||||
Song addSong(ExtractedMetadata metadata);
|
abstract public Song addSong(ExtractedMetadata metadata);
|
||||||
void addSong(Song song);
|
abstract public void addSong(Song song);
|
||||||
|
|
||||||
|
|
||||||
|
private static String musicFileExtensionRegex = "(?iu).*\\.(mp3|mp4|flac|ogg)";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all songs contained with all paths in rootDirectory to the database.
|
||||||
|
* @param rootDirectory Folder(s) containing music files to index.
|
||||||
|
*/
|
||||||
|
public void processSongsWithCallback(ILibrary library, List<Path> rootDirectory){
|
||||||
|
rootDirectory.forEach(dir -> {
|
||||||
|
try {
|
||||||
|
Set<Long> seenIds = new HashSet<>();
|
||||||
|
Files.walk(dir)
|
||||||
|
.filter(f -> f.toString().matches(musicFileExtensionRegex)).map(ExtractedMetadata::autoParse)
|
||||||
|
.forEach(x -> x.ifPresent(i -> {
|
||||||
|
Song song = addSong(i);
|
||||||
|
seenIds.add(song.getId());
|
||||||
|
library.currentlyUpdating(i.toString());
|
||||||
|
}));
|
||||||
|
batchDeleteNot(seenIds.toArray(new Long[seenIds.size()]));
|
||||||
|
} catch (IOException e) {
|
||||||
|
library.libraryUpdateFailed(e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
library.libraryUpdated();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,33 @@
|
|||||||
package musicplayer.library;
|
package musicplayer.library;
|
||||||
|
|
||||||
import musicplayer.callbacks.LibraryCallbackInterface;
|
|
||||||
import musicplayer.db.IDatabase;
|
import musicplayer.db.IDatabase;
|
||||||
import musicplayer.model.ExtractedMetadata;
|
|
||||||
import musicplayer.model.HasSongs;
|
import musicplayer.model.HasSongs;
|
||||||
import musicplayer.model.Song;
|
|
||||||
import org.jaudiotagger.audio.AudioFileIO;
|
|
||||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
|
||||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
|
||||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
|
||||||
import org.jaudiotagger.tag.Tag;
|
|
||||||
import org.jaudiotagger.tag.TagException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
public abstract class ILibrary {
|
||||||
import java.nio.file.Files;
|
IDatabase database;
|
||||||
import java.nio.file.Path;
|
abstract public void showSongs();
|
||||||
import java.util.HashSet;
|
abstract public <T extends HasSongs & Comparable<T>> void showGroupedSongs(Class<T> grouping);
|
||||||
import java.util.List;
|
abstract public void updateLibrary();
|
||||||
import java.util.Optional;
|
abstract public void refreshLibrary();
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public interface ILibrary {
|
public ILibrary(IDatabase database){
|
||||||
void showSongs();
|
this.database = database;
|
||||||
<T extends HasSongs & Comparable<T>> void showGroupedSongs(Class<T> grouping);
|
|
||||||
void updateLibrary();
|
|
||||||
void refreshLibrary();
|
|
||||||
|
|
||||||
String musicFileExtensionRegex = "(?iu).*\\.(mp3|mp4|flac|ogg)";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add all songs contained with all paths in rootDirectory to the database.
|
|
||||||
* @param rootDirectory Folder(s) containing music files to index.
|
|
||||||
* @param callbackInterface Object to send callback data to, set to null if the application doesn't require update data.
|
|
||||||
*/
|
|
||||||
static void processSongsWithCallback(IDatabase database, List<Path> rootDirectory, LibraryCallbackInterface callbackInterface){
|
|
||||||
boolean callbacksEnabled = callbackInterface != null;
|
|
||||||
rootDirectory.forEach(dir -> {
|
|
||||||
try {
|
|
||||||
Set<Long> seenIds = new HashSet<>();
|
|
||||||
Files.walk(dir)
|
|
||||||
.filter(f -> f.toString().matches(musicFileExtensionRegex)).map(ILibrary::autoParse)
|
|
||||||
.forEach(x -> x.ifPresent(i -> {
|
|
||||||
Song song = database.addSong(i);
|
|
||||||
seenIds.add(song.getId());
|
|
||||||
if (callbacksEnabled)
|
|
||||||
callbackInterface.currentlyUpdating(i.toString());
|
|
||||||
}));
|
|
||||||
database.batchDeleteNot(seenIds.toArray(new Long[seenIds.size()]));
|
|
||||||
} catch (IOException e) {
|
|
||||||
if(callbacksEnabled)
|
|
||||||
callbackInterface.libraryUpdated(e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if(callbacksEnabled)
|
|
||||||
callbackInterface.libraryUpdated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract music metadata from the target file.
|
* The library was updated but there was an error (partial update may have occurred).
|
||||||
*
|
* @param message Error message.
|
||||||
* @param targetFile Path to file to extract metadata from.
|
|
||||||
* @return Metadata contained in targetFile.
|
|
||||||
*/
|
*/
|
||||||
static Optional<ExtractedMetadata> autoParse(Path targetFile) {
|
abstract public void libraryUpdateFailed(String message);
|
||||||
Tag audioTags = null;
|
|
||||||
try {
|
/**
|
||||||
audioTags = AudioFileIO.read(targetFile.toFile()).getTag();
|
* Library updated successfully.
|
||||||
} catch (CannotReadException | IOException | ReadOnlyFileException | TagException | InvalidAudioFrameException ignored) {
|
*/
|
||||||
}
|
abstract public void libraryUpdated();
|
||||||
return audioTags == null ? Optional.empty() : Optional.of(new ExtractedMetadata(audioTags, targetFile.toFile()));
|
|
||||||
}
|
/**
|
||||||
|
* Show data on the current update item being indexed.
|
||||||
|
* @param name Data about the file/folder to be shown to the user.
|
||||||
|
*/
|
||||||
|
abstract public void currentlyUpdating(String name);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package musicplayer.library;
|
|||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import musicplayer.util.ConfigManager;
|
import musicplayer.util.ConfigManager;
|
||||||
import musicplayer.callbacks.LibraryCallbackInterface;
|
|
||||||
import musicplayer.db.IDatabase;
|
import musicplayer.db.IDatabase;
|
||||||
import musicplayer.model.Album;
|
import musicplayer.model.Album;
|
||||||
import musicplayer.model.Artist;
|
import musicplayer.model.Artist;
|
||||||
@ -31,10 +30,9 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInterface{
|
public class JTreeLibrary extends ILibrary{
|
||||||
|
|
||||||
private IDatabase database;
|
private final IPlaylist playlist;
|
||||||
private IPlaylist playlist;
|
|
||||||
static final DefaultMutableTreeNode updatingNode = new DefaultMutableTreeNode();
|
static final DefaultMutableTreeNode updatingNode = new DefaultMutableTreeNode();
|
||||||
private final AtomicBoolean libraryUpdating = new AtomicBoolean(false);
|
private final AtomicBoolean libraryUpdating = new AtomicBoolean(false);
|
||||||
private final JComboBox<String> libraryDisplayType = new JComboBox<>();
|
private final JComboBox<String> libraryDisplayType = new JComboBox<>();
|
||||||
@ -44,6 +42,8 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt
|
|||||||
private final static Map<Long, ImageIcon> albumArt = new ConcurrentHashMap<>();
|
private final static Map<Long, ImageIcon> albumArt = new ConcurrentHashMap<>();
|
||||||
private ExecutorService albumArtCollectorPool;
|
private ExecutorService albumArtCollectorPool;
|
||||||
|
|
||||||
|
private final JPanel libraryPanel = new JPanel();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Map of display types for the library paired code to populate the library with data in the correct format.
|
* @return Map of display types for the library paired code to populate the library with data in the correct format.
|
||||||
*/
|
*/
|
||||||
@ -57,16 +57,16 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public JTreeLibrary(IDatabase database, IPlaylist playlist){
|
public JTreeLibrary(IDatabase database, IPlaylist playlist){
|
||||||
this.database = database;
|
super(database);
|
||||||
this.playlist = playlist;
|
this.playlist = playlist;
|
||||||
this.setLayout(new BorderLayout(0, 0));
|
libraryPanel.setLayout(new BorderLayout(0, 0));
|
||||||
libraryTree.setRootVisible(false);
|
libraryTree.setRootVisible(false);
|
||||||
libraryTree.setToggleClickCount(1);
|
libraryTree.setToggleClickCount(1);
|
||||||
libraryTree.setCellRenderer(new LibraryTreeCellRenderer());
|
libraryTree.setCellRenderer(new LibraryTreeCellRenderer());
|
||||||
libraryTree.addMouseListener(new LibraryMouseAdapter());
|
libraryTree.addMouseListener(new LibraryMouseAdapter());
|
||||||
libraryTree.setScrollsOnExpand(false);
|
libraryTree.setScrollsOnExpand(false);
|
||||||
final JScrollPane libraryPane = new JScrollPane(libraryTree);
|
final JScrollPane libraryPane = new JScrollPane(libraryTree);
|
||||||
this.add(libraryPane, BorderLayout.CENTER);
|
libraryPanel.add(libraryPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
libraryDisplayVariants.keySet().forEach(libraryDisplayType::addItem);
|
libraryDisplayVariants.keySet().forEach(libraryDisplayType::addItem);
|
||||||
libraryDisplayType.setSelectedIndex(ConfigManager.getLastDisplayTypeIndex());
|
libraryDisplayType.setSelectedIndex(ConfigManager.getLastDisplayTypeIndex());
|
||||||
@ -83,10 +83,17 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt
|
|||||||
otherContainer.setLayout(new BoxLayout(otherContainer, BoxLayout.PAGE_AXIS));
|
otherContainer.setLayout(new BoxLayout(otherContainer, BoxLayout.PAGE_AXIS));
|
||||||
otherContainer.add(libraryDisplayType);
|
otherContainer.add(libraryDisplayType);
|
||||||
otherContainer.add(librarySearchField);
|
otherContainer.add(librarySearchField);
|
||||||
this.add(otherContainer, BorderLayout.NORTH);
|
libraryPanel.add(otherContainer, BorderLayout.NORTH);
|
||||||
getAlbumArt();
|
getAlbumArt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return JPanel showing this library.
|
||||||
|
*/
|
||||||
|
public JPanel getLibraryPanel(){
|
||||||
|
return libraryPanel;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an item to libraryView.
|
* Add an item to libraryView.
|
||||||
*
|
*
|
||||||
@ -170,7 +177,7 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt
|
|||||||
addNodeToTreeModel(model, parentNode, updatingNode);
|
addNodeToTreeModel(model, parentNode, updatingNode);
|
||||||
libraryTree.setModel(model);
|
libraryTree.setModel(model);
|
||||||
List<Path> dirs = ConfigManager.getLibraryDirectories().stream().map(File::toPath).collect(Collectors.toList());
|
List<Path> dirs = ConfigManager.getLibraryDirectories().stream().map(File::toPath).collect(Collectors.toList());
|
||||||
Thread updaterThread = new Thread(() -> ILibrary.processSongsWithCallback(database, dirs, this), "updater");
|
Thread updaterThread = new Thread(() -> database.processSongsWithCallback(this, dirs), "updater");
|
||||||
updaterThread.start();
|
updaterThread.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,8 +212,8 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt
|
|||||||
* @param message Error message.
|
* @param message Error message.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void libraryUpdated(String message) {
|
public void libraryUpdateFailed(String message) {
|
||||||
JOptionPane.showMessageDialog(this, message, "Update Error", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(libraryPanel, message, "Update Error", JOptionPane.ERROR_MESSAGE);
|
||||||
libraryUpdated();
|
libraryUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ public class Album implements Comparable<Album>, IDBType, HasSongs {
|
|||||||
* Get album art for a given album.
|
* Get album art for a given album.
|
||||||
* @return Album art.
|
* @return Album art.
|
||||||
*/
|
*/
|
||||||
|
@Transient
|
||||||
public Optional<ImageIcon> getAlbumArt(){
|
public Optional<ImageIcon> getAlbumArt(){
|
||||||
Tag audioTags;
|
Tag audioTags;
|
||||||
File targetFile = songs.iterator().next().getSongFile();
|
File targetFile = songs.iterator().next().getSongFile();
|
||||||
|
@ -1,16 +1,24 @@
|
|||||||
package musicplayer.model;
|
package musicplayer.model;
|
||||||
|
|
||||||
|
import org.jaudiotagger.audio.AudioFileIO;
|
||||||
|
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||||
import org.jaudiotagger.tag.FieldKey;
|
import org.jaudiotagger.tag.FieldKey;
|
||||||
import org.jaudiotagger.tag.Tag;
|
import org.jaudiotagger.tag.Tag;
|
||||||
|
import org.jaudiotagger.tag.TagException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal class representing metadata extracted from a music file.
|
* Internal class representing metadata extracted from a music file.
|
||||||
*/
|
*/
|
||||||
public class ExtractedMetadata {
|
public class ExtractedMetadata {
|
||||||
private final Tag audioTags;
|
private final Tag audioTags;
|
||||||
private String songFile;
|
private final String songFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param audioTags jaudiotagger tag data.
|
* @param audioTags jaudiotagger tag data.
|
||||||
@ -37,4 +45,19 @@ public class ExtractedMetadata {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() { return getTitle()+ " - " + getArtist(); }
|
public String toString() { return getTitle()+ " - " + getArtist(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract music metadata from the target file.
|
||||||
|
*
|
||||||
|
* @param targetFile Path to file to extract metadata from.
|
||||||
|
* @return Metadata contained in targetFile.
|
||||||
|
*/
|
||||||
|
public static Optional<ExtractedMetadata> autoParse(Path targetFile) {
|
||||||
|
Tag audioTags = null;
|
||||||
|
try {
|
||||||
|
audioTags = AudioFileIO.read(targetFile.toFile()).getTag();
|
||||||
|
} catch (CannotReadException | IOException | ReadOnlyFileException | TagException | InvalidAudioFrameException ignored) {
|
||||||
|
}
|
||||||
|
return audioTags == null ? Optional.empty() : Optional.of(new ExtractedMetadata(audioTags, targetFile.toFile()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ public class GStreamerPlayer implements IPlayer{
|
|||||||
if (this.thread != null) {
|
if (this.thread != null) {
|
||||||
playBin.stop();
|
playBin.stop();
|
||||||
internalThread.stop();
|
internalThread.stop();
|
||||||
thread = null;
|
thread.interrupt();
|
||||||
resetSeek();
|
resetSeek();
|
||||||
playlist.setStopped();
|
playlist.setStopped();
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,8 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public class VLCPlayer implements IPlayer {
|
public class VLCPlayer implements IPlayer {
|
||||||
|
|
||||||
private ArrayList<String> libvlcArgs = new ArrayList<>(Collections.singletonList("--vout=dummy"));
|
private final ArrayList<String> libvlcArgs = new ArrayList<>(Collections.singletonList("--vout=dummy"));
|
||||||
private MediaPlayer mediaPlayer;
|
private final MediaPlayer mediaPlayer;
|
||||||
|
|
||||||
private Song currentSong;
|
private Song currentSong;
|
||||||
private final PlayerCallbackInterface callbackInterface;
|
private final PlayerCallbackInterface callbackInterface;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package musicplayer.playlist;
|
package musicplayer.playlist;
|
||||||
|
|
||||||
import musicplayer.library.ILibrary;
|
|
||||||
import musicplayer.model.Album;
|
import musicplayer.model.Album;
|
||||||
import musicplayer.model.Artist;
|
import musicplayer.model.Artist;
|
||||||
|
import musicplayer.model.ExtractedMetadata;
|
||||||
import musicplayer.model.Song;
|
import musicplayer.model.Song;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@ -38,15 +38,15 @@ public interface IPlaylist {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
static void writePlaylistToFile(List<Song> playlist, File targetFile) throws IOException {
|
static void writePlaylistToFile(List<Song> playlist, File targetFile) throws IOException {
|
||||||
if(!targetFile.exists())
|
if(targetFile.exists() || targetFile.createNewFile()) {
|
||||||
targetFile.createNewFile();
|
|
||||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile), "utf-8"))) {
|
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile), "utf-8"))) {
|
||||||
writer.write("#EXTM3U\n");
|
writer.write("#EXTM3U\n");
|
||||||
for(Song song : playlist){
|
for (Song song : playlist) {
|
||||||
writer.write(song.getM3UFormatString());
|
writer.write(song.getM3UFormatString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read songs in from an m3u playlist.
|
* Read songs in from an m3u playlist.
|
||||||
@ -67,7 +67,7 @@ public interface IPlaylist {
|
|||||||
songPaths.set(i, Paths.get(targetFile.getParent(), songPaths.get(i).toString()));
|
songPaths.set(i, Paths.get(targetFile.getParent(), songPaths.get(i).toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
songPaths.stream().map(ILibrary::autoParse).filter(Optional::isPresent).map(Optional::get)
|
songPaths.stream().map(ExtractedMetadata::autoParse).filter(Optional::isPresent).map(Optional::get)
|
||||||
.forEach(x -> result.add(new Song(x.getTrackNumber(), x.getDiscNumber(), x.getTitle(), new Artist(x.getArtist()), new Album(x.getAlbum()), x.getGenre(), x.getSongFile())));
|
.forEach(x -> result.add(new Song(x.getTrackNumber(), x.getDiscNumber(), x.getTitle(), new Artist(x.getArtist()), new Album(x.getAlbum()), x.getGenre(), x.getSongFile())));
|
||||||
}
|
}
|
||||||
} catch (IOException ignored) { }
|
} catch (IOException ignored) { }
|
||||||
|
@ -133,7 +133,7 @@ public class JTablePlaylist extends JScrollPane implements IPlaylist {
|
|||||||
|
|
||||||
private class PlaylistTableModel extends AbstractTableModel {
|
private class PlaylistTableModel extends AbstractTableModel {
|
||||||
|
|
||||||
private List<Song> songList = new ArrayList<>();
|
private final List<Song> songList = new ArrayList<>();
|
||||||
private int playingRow = -1; // -1 means no song is currently playing.
|
private int playingRow = -1; // -1 means no song is currently playing.
|
||||||
|
|
||||||
public PlaylistTableModel(){ }
|
public PlaylistTableModel(){ }
|
||||||
|
@ -7,8 +7,8 @@ import musicplayer.util.Icons;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
public class ControlBar extends JPanel {
|
class ControlBar extends JPanel {
|
||||||
IPlayer player;
|
private final IPlayer player;
|
||||||
|
|
||||||
public ControlBar(IPlayer player){
|
public ControlBar(IPlayer player){
|
||||||
this.player = player;
|
this.player = player;
|
||||||
|
@ -4,6 +4,7 @@ import com.google.inject.Inject;
|
|||||||
import musicplayer.StartPlayingException;
|
import musicplayer.StartPlayingException;
|
||||||
import musicplayer.callbacks.PlayerCallbackInterface;
|
import musicplayer.callbacks.PlayerCallbackInterface;
|
||||||
import musicplayer.library.ILibrary;
|
import musicplayer.library.ILibrary;
|
||||||
|
import musicplayer.library.JTreeLibrary;
|
||||||
import musicplayer.player.IPlayer;
|
import musicplayer.player.IPlayer;
|
||||||
import musicplayer.playlist.IPlaylist;
|
import musicplayer.playlist.IPlaylist;
|
||||||
import musicplayer.util.ConfigManager;
|
import musicplayer.util.ConfigManager;
|
||||||
@ -115,7 +116,7 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
|
|||||||
libraryAndPlaylistPane.setDividerLocation(240);
|
libraryAndPlaylistPane.setDividerLocation(240);
|
||||||
libraryAndPlaylistPane.setRightComponent((JScrollPane) playlist);
|
libraryAndPlaylistPane.setRightComponent((JScrollPane) playlist);
|
||||||
this.add(libraryAndPlaylistPane, BorderLayout.CENTER);
|
this.add(libraryAndPlaylistPane, BorderLayout.CENTER);
|
||||||
libraryAndPlaylistPane.setLeftComponent((JPanel) library);
|
libraryAndPlaylistPane.setLeftComponent(((JTreeLibrary)library).getLibraryPanel());
|
||||||
final JPanel controlPane = new JPanel();
|
final JPanel controlPane = new JPanel();
|
||||||
controlPane.setLayout(new BorderLayout(0, 0));
|
controlPane.setLayout(new BorderLayout(0, 0));
|
||||||
this.add(controlPane, BorderLayout.SOUTH);
|
this.add(controlPane, BorderLayout.SOUTH);
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package musicplayer.db;
|
package musicplayer.db;
|
||||||
|
|
||||||
import musicplayer.model.Album;
|
import musicplayer.library.ILibrary;
|
||||||
import musicplayer.model.Artist;
|
import musicplayer.model.*;
|
||||||
import musicplayer.model.ExtractedMetadata;
|
|
||||||
import musicplayer.model.Song;
|
|
||||||
import org.jaudiotagger.tag.FieldKey;
|
import org.jaudiotagger.tag.FieldKey;
|
||||||
import org.jaudiotagger.tag.Tag;
|
import org.jaudiotagger.tag.Tag;
|
||||||
import org.jaudiotagger.tag.id3.ID3v23Tag;
|
import org.jaudiotagger.tag.id3.ID3v23Tag;
|
||||||
@ -11,6 +9,8 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@ -216,4 +216,47 @@ public class HibernateDatabaseTest {
|
|||||||
assertTrue(db.contains(song2));
|
assertTrue(db.contains(song2));
|
||||||
assertFalse(db.contains(song3));
|
assertFalse(db.contains(song3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcessSongsWithCallback() throws Exception {
|
||||||
|
Path dir = new File(HibernateDatabaseTest.class.getResource("/sample.mp3").getFile()).getParentFile().toPath();
|
||||||
|
List<Path> dirList = new ArrayList<>();
|
||||||
|
dirList.add(dir);
|
||||||
|
FakeLibrary library = new FakeLibrary(database);
|
||||||
|
database.processSongsWithCallback(library, dirList);
|
||||||
|
assertEquals(1, database.listAllT(Song.class).get().size());
|
||||||
|
assertTrue(library.success);
|
||||||
|
assertFalse(library.failed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FakeLibrary extends ILibrary{
|
||||||
|
|
||||||
|
public boolean success = false;
|
||||||
|
public boolean failed = false;
|
||||||
|
|
||||||
|
public FakeLibrary(IDatabase database) {
|
||||||
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showSongs() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends HasSongs & Comparable<T>> void showGroupedSongs(Class<T> grouping) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateLibrary() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refreshLibrary() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void libraryUpdateFailed(String message) { failed = true;}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void libraryUpdated() { success = true;}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void currentlyUpdating(String name) {}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,79 +0,0 @@
|
|||||||
package musicplayer.library;
|
|
||||||
|
|
||||||
import musicplayer.callbacks.LibraryCallbackInterface;
|
|
||||||
import musicplayer.db.HibernateDatabase;
|
|
||||||
import musicplayer.db.IDatabase;
|
|
||||||
import musicplayer.model.ExtractedMetadata;
|
|
||||||
import musicplayer.model.Song;
|
|
||||||
import org.jaudiotagger.audio.AudioFileIO;
|
|
||||||
import org.jaudiotagger.tag.FieldKey;
|
|
||||||
import org.jaudiotagger.tag.Tag;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
public class ILibraryTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAutoParse() throws Exception {
|
|
||||||
File inputFile = new File(ILibraryTest.class.getResource("/sample.mp3").getFile());
|
|
||||||
Optional<ExtractedMetadata> data = ILibrary.autoParse(inputFile.toPath());
|
|
||||||
Tag audioTags = AudioFileIO.read(inputFile).getTag();
|
|
||||||
assertTrue(data.isPresent());
|
|
||||||
ExtractedMetadata internal = data.get();
|
|
||||||
assertEquals(audioTags.getFirst(FieldKey.ARTIST), internal.getArtist());
|
|
||||||
assertEquals(audioTags.getFirst(FieldKey.GENRE), internal.getGenre());
|
|
||||||
assertEquals(audioTags.getFirst(FieldKey.TITLE), internal.getTitle());
|
|
||||||
assertEquals(audioTags.getFirst(FieldKey.ALBUM), internal.getAlbum());
|
|
||||||
assertEquals(audioTags.getFirst(FieldKey.TRACK), internal.getTrackNumber());
|
|
||||||
assertEquals(audioTags.getFirst(FieldKey.DISC_NO), internal.getDiscNumber());
|
|
||||||
assertEquals(inputFile.getPath(), internal.getSongFile());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAutoParseInvalidFile() throws Exception {
|
|
||||||
File inputFile = new File("");
|
|
||||||
Optional<ExtractedMetadata> data = ILibrary.autoParse(inputFile.toPath());
|
|
||||||
assertFalse(data.isPresent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testProcessSongsWithCallback() throws Exception {
|
|
||||||
CallbackInterface callbackInterface = new CallbackInterface();
|
|
||||||
Path dir = new File(ILibraryTest.class.getResource("/sample.mp3").getFile()).getParentFile().toPath();
|
|
||||||
List<Path> dirList = new ArrayList<>();
|
|
||||||
dirList.add(dir);
|
|
||||||
IDatabase database = new HibernateDatabase(true);
|
|
||||||
ILibrary.processSongsWithCallback(database, dirList, callbackInterface);
|
|
||||||
assertEquals(1, database.listAllT(Song.class).get().size());
|
|
||||||
assertTrue(callbackInterface.status);
|
|
||||||
assertFalse(callbackInterface.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CallbackInterface implements LibraryCallbackInterface{
|
|
||||||
public boolean status = false;
|
|
||||||
public String update = "";
|
|
||||||
public boolean error = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void libraryUpdated(String message) {
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void libraryUpdated() {
|
|
||||||
status = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void currentlyUpdating(String name) {
|
|
||||||
update = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,6 +4,7 @@ import musicplayer.db.HibernateDatabase;
|
|||||||
import musicplayer.db.IDatabase;
|
import musicplayer.db.IDatabase;
|
||||||
import musicplayer.model.Album;
|
import musicplayer.model.Album;
|
||||||
import musicplayer.model.Artist;
|
import musicplayer.model.Artist;
|
||||||
|
import musicplayer.model.ExtractedMetadata;
|
||||||
import musicplayer.model.Song;
|
import musicplayer.model.Song;
|
||||||
import musicplayer.playlist.IPlaylist;
|
import musicplayer.playlist.IPlaylist;
|
||||||
import musicplayer.playlist.JTablePlaylist;
|
import musicplayer.playlist.JTablePlaylist;
|
||||||
@ -12,18 +13,21 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import javax.swing.tree.DefaultMutableTreeNode;
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class JTreeLibraryTest {
|
public class JTreeLibraryTest {
|
||||||
|
|
||||||
JTreeLibrary library;
|
private JTreeLibrary library;
|
||||||
Song sampleSong = new Song(ILibrary.autoParse(
|
private final Song sampleSong = new Song(ExtractedMetadata.autoParse(
|
||||||
new File(JTreeLibraryTest.class.getResource("/sample.mp3").getFile()).toPath()).get());
|
new File(JTreeLibraryTest.class.getResource("/sample.mp3").getFile()).toPath()).get());
|
||||||
IDatabase database;
|
private IDatabase database;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() {
|
||||||
database = new HibernateDatabase(true);
|
database = new HibernateDatabase(true);
|
||||||
IPlaylist playlist = new JTablePlaylist();
|
IPlaylist playlist = new JTablePlaylist();
|
||||||
library = new JTreeLibrary(database, playlist);
|
library = new JTreeLibrary(database, playlist);
|
||||||
@ -85,6 +89,6 @@ public class JTreeLibraryTest {
|
|||||||
public void testCurrentlyUpdating() throws Exception {
|
public void testCurrentlyUpdating() throws Exception {
|
||||||
String updateText = "Test String";
|
String updateText = "Test String";
|
||||||
library.currentlyUpdating("Test String");
|
library.currentlyUpdating("Test String");
|
||||||
assertEquals(updateText, library.updatingNode.getUserObject());
|
assertEquals(updateText, JTreeLibrary.updatingNode.getUserObject());
|
||||||
}
|
}
|
||||||
}
|
}
|
38
src/test/java/musicplayer/model/ExtractedMetadataTest.java
Normal file
38
src/test/java/musicplayer/model/ExtractedMetadataTest.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package musicplayer.model;
|
||||||
|
|
||||||
|
import org.jaudiotagger.audio.AudioFileIO;
|
||||||
|
import org.jaudiotagger.tag.FieldKey;
|
||||||
|
import org.jaudiotagger.tag.Tag;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class ExtractedMetadataTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAutoParse() throws Exception {
|
||||||
|
File inputFile = new File(ExtractedMetadataTest.class.getResource("/sample.mp3").getFile());
|
||||||
|
Optional<ExtractedMetadata> data = ExtractedMetadata.autoParse(inputFile.toPath());
|
||||||
|
Tag audioTags = AudioFileIO.read(inputFile).getTag();
|
||||||
|
assertTrue(data.isPresent());
|
||||||
|
ExtractedMetadata internal = data.get();
|
||||||
|
assertEquals(audioTags.getFirst(FieldKey.ARTIST), internal.getArtist());
|
||||||
|
assertEquals(audioTags.getFirst(FieldKey.GENRE), internal.getGenre());
|
||||||
|
assertEquals(audioTags.getFirst(FieldKey.TITLE), internal.getTitle());
|
||||||
|
assertEquals(audioTags.getFirst(FieldKey.ALBUM), internal.getAlbum());
|
||||||
|
assertEquals(audioTags.getFirst(FieldKey.TRACK), internal.getTrackNumber());
|
||||||
|
assertEquals(audioTags.getFirst(FieldKey.DISC_NO), internal.getDiscNumber());
|
||||||
|
assertEquals(inputFile.getPath(), internal.getSongFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAutoParseInvalidFile() throws Exception {
|
||||||
|
File inputFile = new File("");
|
||||||
|
Optional<ExtractedMetadata> data = ExtractedMetadata.autoParse(inputFile.toPath());
|
||||||
|
assertFalse(data.isPresent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
|||||||
package musicplayer.player;
|
package musicplayer.player;
|
||||||
|
|
||||||
import musicplayer.library.ILibrary;
|
import musicplayer.library.ILibrary;
|
||||||
|
import musicplayer.model.ExtractedMetadata;
|
||||||
import musicplayer.model.Song;
|
import musicplayer.model.Song;
|
||||||
import musicplayer.playlist.IPlaylist;
|
import musicplayer.playlist.IPlaylist;
|
||||||
import musicplayer.playlist.JTablePlaylist;
|
import musicplayer.playlist.JTablePlaylist;
|
||||||
@ -15,13 +16,13 @@ import static org.junit.Assert.*;
|
|||||||
|
|
||||||
public class GStreamerPlayerTest {
|
public class GStreamerPlayerTest {
|
||||||
|
|
||||||
GStreamerPlayer player;
|
private GStreamerPlayer player;
|
||||||
IPlaylist playlist;
|
private IPlaylist playlist;
|
||||||
Song sampleSong = new Song(ILibrary.autoParse(
|
private final Song sampleSong = new Song(ExtractedMetadata.autoParse(
|
||||||
new File(GStreamerPlayerTest.class.getResource("/sample.mp3").getFile()).toPath()).get());
|
new File(GStreamerPlayerTest.class.getResource("/sample.mp3").getFile()).toPath()).get());
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() {
|
||||||
playlist = new JTablePlaylist();
|
playlist = new JTablePlaylist();
|
||||||
player = new GStreamerPlayer(null, playlist, true);
|
player = new GStreamerPlayer(null, playlist, true);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package musicplayer.player;
|
package musicplayer.player;
|
||||||
|
|
||||||
import musicplayer.library.ILibrary;
|
import musicplayer.library.ILibrary;
|
||||||
|
import musicplayer.model.ExtractedMetadata;
|
||||||
import musicplayer.model.Song;
|
import musicplayer.model.Song;
|
||||||
import musicplayer.playlist.IPlaylist;
|
import musicplayer.playlist.IPlaylist;
|
||||||
import musicplayer.playlist.JTablePlaylist;
|
import musicplayer.playlist.JTablePlaylist;
|
||||||
import musicplayer.util.ConfigManager;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@ -15,13 +15,13 @@ import static org.junit.Assert.*;
|
|||||||
|
|
||||||
public class VLCPlayerTest {
|
public class VLCPlayerTest {
|
||||||
|
|
||||||
VLCPlayer player;
|
private VLCPlayer player;
|
||||||
IPlaylist playlist;
|
private IPlaylist playlist;
|
||||||
Song sampleSong = new Song(ILibrary.autoParse(
|
private final Song sampleSong = new Song(ExtractedMetadata.autoParse(
|
||||||
new File(VLCPlayerTest.class.getResource("/sample.mp3").getFile()).toPath()).get());
|
new File(VLCPlayerTest.class.getResource("/sample.mp3").getFile()).toPath()).get());
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() {
|
||||||
playlist = new JTablePlaylist();
|
playlist = new JTablePlaylist();
|
||||||
player = new VLCPlayer(null, playlist, true);
|
player = new VLCPlayer(null, playlist, true);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package musicplayer.playlist;
|
package musicplayer.playlist;
|
||||||
|
|
||||||
import musicplayer.library.ILibrary;
|
import musicplayer.model.ExtractedMetadata;
|
||||||
import musicplayer.model.Song;
|
import musicplayer.model.Song;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -19,12 +19,13 @@ public class IPlaylistTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteAndReadPlaylist() throws Exception {
|
public void testWriteAndReadPlaylist() throws Exception {
|
||||||
Song sampleSong = new Song(ILibrary.autoParse(
|
Song sampleSong = new Song(ExtractedMetadata.autoParse(
|
||||||
new File(IPlaylistTest.class.getResource("/sample.mp3").getFile()).toPath()).get());
|
new File(IPlaylistTest.class.getResource("/sample.mp3").getFile()).toPath()).get());
|
||||||
File playlistFile = folder.newFile();
|
File playlistFile = folder.newFile();
|
||||||
List<Song> playlist = new ArrayList<>();
|
List<Song> playlist = new ArrayList<>();
|
||||||
playlist.add(sampleSong);
|
playlist.add(sampleSong);
|
||||||
IPlaylist.writePlaylistToFile(playlist, playlistFile);
|
IPlaylist.writePlaylistToFile(playlist, playlistFile);
|
||||||
|
assertTrue(playlistFile.exists());
|
||||||
List<Song> result = IPlaylist.readPlaylistFromFile(playlistFile);
|
List<Song> result = IPlaylist.readPlaylistFromFile(playlistFile);
|
||||||
assertEquals(sampleSong, result.get(0));
|
assertEquals(sampleSong, result.get(0));
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@ import static org.junit.Assert.*;
|
|||||||
|
|
||||||
public class JTablePlaylistTest {
|
public class JTablePlaylistTest {
|
||||||
|
|
||||||
JTablePlaylist playlist;
|
private JTablePlaylist playlist;
|
||||||
JTable innerTable;
|
private JTable innerTable;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
Loading…
Reference in New Issue
Block a user