Moved LibraryUtils to default implementations in ILibrary.

This commit is contained in:
neviyn 2016-03-25 03:40:16 +00:00
parent ac2ddda462
commit 4ffca843b4
7 changed files with 154 additions and 83 deletions

View File

@ -4,9 +4,13 @@ public interface LibraryCallbackInterface {
/**
* Call this after performing library updates so the front-end knows that updating has completed.
* @param successful Was the update function successful.
* @param message Error message.
*/
void libraryUpdated(boolean successful);
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.

View File

@ -1,10 +1,67 @@
package musicplayer.library;
import musicplayer.callbacks.LibraryCallbackInterface;
import musicplayer.db.IDatabase;
import musicplayer.model.ExtractedMetadata;
import musicplayer.model.HasSongs;
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;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
public interface ILibrary {
void showSongs();
<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 {
Files.walk(dir)
.filter(f -> f.toString().matches(musicFileExtensionRegex)).map(ILibrary::autoParse)
.forEach(x -> x.ifPresent(i -> {
database.addSong(i);
if (callbacksEnabled)
callbackInterface.currentlyUpdating(i.toString());
}));
} catch (IOException e) {
if(callbacksEnabled)
callbackInterface.libraryUpdated(e.getMessage());
}
});
if(callbacksEnabled)
callbackInterface.libraryUpdated();
}
/**
* Extract music metadata from the target file.
*
* @param targetFile Path to file to extract metadata from.
* @return Metadata contained in targetFile.
*/
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()));
}
}

View File

@ -9,7 +9,6 @@ import musicplayer.model.Artist;
import musicplayer.model.HasSongs;
import musicplayer.model.Song;
import musicplayer.playlist.IPlaylist;
import musicplayer.util.LibraryUtils;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
@ -137,7 +136,8 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt
addNodeToTreeModel(model, parentNode, updatingNode);
libraryTree.setModel(model);
List<Path> dirs = ConfigManager.getLibraryDirectories().stream().map(File::toPath).collect(Collectors.toList());
LibraryUtils.processSongsWithCallback(database, dirs, this);
Thread updaterThread = new Thread(() -> ILibrary.processSongsWithCallback(database, dirs, this), "updater");
updaterThread.start();
}
}
@ -165,7 +165,12 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt
}
@Override
public void libraryUpdated(boolean successful) {
public void libraryUpdated(String message) {
JOptionPane.showMessageDialog(this, message, "Update Error", JOptionPane.ERROR_MESSAGE);
libraryUpdated();
}
public void libraryUpdated() {
libraryUpdating.set(false);
refreshLibrary();
}

View File

@ -1,75 +0,0 @@
package musicplayer.util;
import musicplayer.callbacks.LibraryCallbackInterface;
import musicplayer.db.IDatabase;
import musicplayer.model.ExtractedMetadata;
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;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
public final class LibraryUtils {
private static final String musicFileExtensionRegex = "(?iu).*\\.(mp3|mp4|flac|ogg)";
private static Thread updaterThread;
/**
* 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.
*/
public static void processSongsWithCallback(IDatabase database, List<Path> rootDirectory, LibraryCallbackInterface callbackInterface){
boolean callbacksEnabled = callbackInterface != null;
updaterThread = new Thread(() -> {
rootDirectory.forEach(dir -> {
try {
Files.walk(dir)
.filter(f -> f.toString().matches(musicFileExtensionRegex)).map(LibraryUtils::autoParse)
.forEach(x -> x.ifPresent(i -> {
database.addSong(i);
if (callbacksEnabled)
callbackInterface.currentlyUpdating(i.toString());
}));
} catch (IOException e) {
if(callbacksEnabled)
callbackInterface.libraryUpdated(false);
}
});
if(callbacksEnabled)
callbackInterface.libraryUpdated(true);
}, "libraryUpdater");
updaterThread.start();
}
/**
* Halt execution of the library updater thread (if running)
*/
public static void cancelProcessing(){
if(updaterThread.isAlive())
updaterThread.interrupt();
}
/**
* 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 e) {
e.printStackTrace();
}
return audioTags == null ? Optional.empty() : Optional.of(new ExtractedMetadata(audioTags, targetFile.toFile()));
}
}

View File

@ -1,5 +1,6 @@
package musicplayer.util;
import musicplayer.library.ILibrary;
import musicplayer.model.Album;
import musicplayer.model.Artist;
import musicplayer.model.Song;
@ -51,7 +52,7 @@ public final class PlaylistUtils {
songPaths.set(i, Paths.get(targetFile.getParent(), songPaths.get(i).toString()));
}
}
songPaths.stream().map(LibraryUtils::autoParse).filter(Optional::isPresent).map(Optional::get)
songPaths.stream().map(ILibrary::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())));
}
} catch (IOException ignored) { }

View File

@ -0,0 +1,79 @@
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;
}
}
}

View File

@ -1,9 +1,9 @@
package musicplayer.player;
import musicplayer.library.ILibrary;
import musicplayer.model.Song;
import musicplayer.playlist.IPlaylist;
import musicplayer.playlist.JTablePlaylist;
import musicplayer.util.LibraryUtils;
import org.junit.Before;
import org.junit.Test;
@ -16,7 +16,7 @@ public class GStreamerPlayerTest {
GStreamerPlayer player;
IPlaylist playlist;
Song sampleSong = new Song(LibraryUtils.autoParse(
Song sampleSong = new Song(ILibrary.autoParse(
new File(GStreamerPlayerTest.class.getResource("/sample.mp3").getFile()).toPath()).get());
@Before