diff --git a/src/main/java/musicplayer/PlayerGUI.java b/src/main/java/musicplayer/PlayerGUI.java index 92aa912..2ee93bd 100644 --- a/src/main/java/musicplayer/PlayerGUI.java +++ b/src/main/java/musicplayer/PlayerGUI.java @@ -10,6 +10,8 @@ import musicplayer.db.Gateway; import musicplayer.model.Album; import musicplayer.model.Song; import musicplayer.swingmodels.PlaylistTableModel; +import musicplayer.util.LibraryUtils; +import musicplayer.util.PlaylistUtils; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; @@ -17,7 +19,10 @@ import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import java.awt.*; -import java.awt.event.*; +import java.awt.event.ItemEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.io.File; import java.io.IOException; import java.nio.file.Path; @@ -432,19 +437,20 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf }); playlistTools.add(menuItem); + FileNameExtensionFilter m3uExtensionFilter = new FileNameExtensionFilter("m3u playlist", "m3u"); menuItem = new JMenuItem("Save Playlist"); menuItem.addActionListener(e -> { if(!playlistTableModel.isEmpty()) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setDialogType(JFileChooser.SAVE_DIALOG); fileChooser.setSelectedFile(new File("playlist.m3u")); - fileChooser.setFileFilter(new FileNameExtensionFilter("m3u playlist", "m3u")); + fileChooser.setFileFilter(m3uExtensionFilter); if (fileChooser.showSaveDialog(mainPanel) == JFileChooser.APPROVE_OPTION) { String filename = fileChooser.getSelectedFile().toString(); if (!filename.endsWith(".m3u")) filename += ".m3u"; try { - playlistTableModel.writePlaylistToFile(new File(filename)); + PlaylistUtils.writePlaylistToFile(playlistTableModel.getSongList(), new File(filename)); } catch (IOException e1) { JOptionPane.showMessageDialog(null, e1.getMessage() + "\n" + Arrays.toString(e1.getStackTrace()), "Error", JOptionPane.ERROR_MESSAGE); } @@ -453,6 +459,17 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf }); playlistTools.add(menuItem); + menuItem = new JMenuItem("Open Playlist"); + menuItem.addActionListener(e -> { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogType(JFileChooser.OPEN_DIALOG); + fileChooser.setFileFilter(m3uExtensionFilter); + if(fileChooser.showOpenDialog(mainPanel) == JFileChooser.APPROVE_OPTION){ + PlaylistUtils.readPlaylistFromFile(fileChooser.getSelectedFile()).stream().forEach(playlistTableModel::addSong); + } + }); + playlistTools.add(menuItem); + // Add everything to the menu bar itself menuBar.add(playlistTools); return menuBar; diff --git a/src/main/java/musicplayer/swingmodels/PlaylistTableModel.java b/src/main/java/musicplayer/swingmodels/PlaylistTableModel.java index 386e8e8..1d28f19 100644 --- a/src/main/java/musicplayer/swingmodels/PlaylistTableModel.java +++ b/src/main/java/musicplayer/swingmodels/PlaylistTableModel.java @@ -3,7 +3,6 @@ package musicplayer.swingmodels; import musicplayer.model.Song; import javax.swing.table.AbstractTableModel; -import java.io.*; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -170,20 +169,10 @@ public class PlaylistTableModel extends AbstractTableModel { fireTableDataChanged(); } - public String getM3UPlaylist(){ - StringBuilder output = new StringBuilder("#EXTM3U\n"); - songList.forEach(x -> output.append(x.getM3UFormatString())); - return output.toString(); - } - - public void writePlaylistToFile(File targetFile) throws IOException { - if(!targetFile.exists()) - targetFile.createNewFile(); - try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile), "utf-8"))) { - writer.write("#EXTM3U\n"); - for(Song song : songList){ - writer.write(song.getM3UFormatString()); - } - } + /** + * @return Songs currently in the playlist. + */ + public List getSongList(){ + return songList; } } diff --git a/src/main/java/musicplayer/LibraryUtils.java b/src/main/java/musicplayer/util/LibraryUtils.java similarity index 93% rename from src/main/java/musicplayer/LibraryUtils.java rename to src/main/java/musicplayer/util/LibraryUtils.java index 41c1b28..844eccb 100644 --- a/src/main/java/musicplayer/LibraryUtils.java +++ b/src/main/java/musicplayer/util/LibraryUtils.java @@ -1,4 +1,4 @@ -package musicplayer; +package musicplayer.util; import musicplayer.callbacks.LibraryCallbackInterface; import musicplayer.db.Gateway; @@ -16,7 +16,7 @@ import java.nio.file.Path; import java.util.List; import java.util.Optional; -final class LibraryUtils { +public final class LibraryUtils { private static final String musicFileExtensionRegex = "(?iu).*\\.(mp3|mp4|flac|ogg)"; private static Thread updaterThread; @@ -35,7 +35,7 @@ final class LibraryUtils { .filter(f -> f.toString().matches(musicFileExtensionRegex)).map(LibraryUtils::autoParse) .forEach(x -> x.ifPresent(i -> { Gateway.addSong(i); - if(callbacksEnabled) + if (callbacksEnabled) callbackInterface.currentlyUpdating(i.toString()); })); } catch (IOException e) { @@ -63,7 +63,7 @@ final class LibraryUtils { * @param targetFile Path to file to extract metadata from. * @return Metadata contained in targetFile. */ - private static Optional autoParse(Path targetFile) { + public static Optional autoParse(Path targetFile) { Tag audioTags = null; try { audioTags = AudioFileIO.read(targetFile.toFile()).getTag(); diff --git a/src/main/java/musicplayer/util/PlaylistUtils.java b/src/main/java/musicplayer/util/PlaylistUtils.java new file mode 100644 index 0000000..624094f --- /dev/null +++ b/src/main/java/musicplayer/util/PlaylistUtils.java @@ -0,0 +1,61 @@ +package musicplayer.util; + +import musicplayer.model.Album; +import musicplayer.model.Artist; +import musicplayer.model.Song; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public final class PlaylistUtils { + + /** + * Write a list of songs to a file as an m3u format playlist file. + * @param playlist Playlist of songs to write. + * @param targetFile File to write playlist into. + * @throws IOException + */ + public static void writePlaylistToFile(List playlist, File targetFile) throws IOException { + if(!targetFile.exists()) + targetFile.createNewFile(); + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile), "utf-8"))) { + writer.write("#EXTM3U\n"); + for(Song song : playlist){ + writer.write(song.getM3UFormatString()); + } + } + } + + /** + * Read songs in from an m3u playlist. + * Songs in the playlist with files that don't actually exists will be dropped. + * @param targetFile File to read m3u playlist data from. + * @return List of songs contained in the playlist. + */ + public static List readPlaylistFromFile(File targetFile){ + List result = new ArrayList<>(); + if(targetFile.exists()){ + try { + List playlistData = Files.lines(targetFile.toPath()).filter(x -> !x.isEmpty() && !x.contains("#EXTINF")).collect(Collectors.toList()); + if(playlistData.get(0).equals("#EXTM3U")){ + playlistData.remove(0); + List songPaths = playlistData.stream().map(Paths::get).collect(Collectors.toList()); + for(int i = 0; i < songPaths.size(); i++){ + if(!Files.exists(songPaths.get(i))){ // If song file doesn't exists, try treating it as relative + songPaths.set(i, Paths.get(targetFile.getParent(), songPaths.get(i).toString())); + } + } + songPaths.stream().map(LibraryUtils::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) { } + } + return result; + } +}