diff --git a/src/main/java/musicplayer/db/HibernateDatabase.java b/src/main/java/musicplayer/db/HibernateDatabase.java index 919a483..d7fce91 100644 --- a/src/main/java/musicplayer/db/HibernateDatabase.java +++ b/src/main/java/musicplayer/db/HibernateDatabase.java @@ -2,14 +2,12 @@ package musicplayer.db; import musicplayer.util.ConfigManager; import musicplayer.model.*; -import musicplayer.util.AlbumArtExtractor; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.criterion.Restrictions; -import java.io.File; import java.util.List; import java.util.Optional; import java.util.Properties; @@ -154,8 +152,7 @@ public class HibernateDatabase implements IDatabase{ session.beginTransaction(); Optional albumObj = getOneAlbum(metadata.getAlbum()); if (!albumObj.isPresent()) { - byte[] art = AlbumArtExtractor.getImageData(new File(metadata.getSongFile())); - Album album = new Album(metadata.getAlbum(), art); + Album album = new Album(metadata.getAlbum()); albumObj = Optional.of(album); } Optional artistObj = getOneArtist(metadata.getArtist()); diff --git a/src/main/java/musicplayer/library/JTreeLibrary.java b/src/main/java/musicplayer/library/JTreeLibrary.java index f6b9bff..831a967 100644 --- a/src/main/java/musicplayer/library/JTreeLibrary.java +++ b/src/main/java/musicplayer/library/JTreeLibrary.java @@ -37,6 +37,8 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt private final JXTextField librarySearchField = new JXTextField(); final JTree libraryTree = new JTree(); private final Map libraryDisplayVariants = createDisplayVariantMap(); + private final static Map albumArt = new HashMap<>(); + private Thread albumArtCollector; /** * @return Map of display types for the library paired code to populate the library with data in the correct format. @@ -78,6 +80,11 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt otherContainer.add(libraryDisplayType); otherContainer.add(librarySearchField); this.add(otherContainer, BorderLayout.NORTH); + Optional> dbQuery = database.listAllT(Album.class); + dbQuery.ifPresent(x -> x.forEach(y -> { + albumArtCollector = new Thread(() -> y.getAlbumArt().ifPresent(art -> albumArt.put(y.getId(), art))); + albumArtCollector.start(); + })); } /** @@ -208,6 +215,13 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt */ public void libraryUpdated() { libraryUpdating.set(false); + albumArtCollector.interrupt(); + albumArtCollector = new Thread(() -> { + Optional> dbQuery = database.listAllT(Album.class); + dbQuery.ifPresent(x -> x.forEach(y -> y.getAlbumArt().ifPresent(art -> albumArt.put(y.getId(), + art)))); + }); + albumArtCollector.start(); refreshLibrary(); } @@ -252,7 +266,8 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt private final JLabel label; @SuppressWarnings("ConstantConditions") - private final Icon missingIcon = new ImageIcon(JTreeLibrary.class.getClassLoader().getResource("missing.gif")); + private final ImageIcon missingIcon = new ImageIcon(JTreeLibrary.class.getClassLoader().getResource("missing" + + ".gif")); LibraryTreeCellRenderer() { label = new JLabel(); @@ -266,8 +281,12 @@ public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInt label.setText(o.toString()); if (o instanceof Album) { Album album = (Album) o; - label.setIcon(missingIcon); - album.getAlbumArt().ifPresent(x -> label.setIcon(new ImageIcon(x))); + if(albumArt.containsKey(album.getId())){ + label.setIcon(albumArt.get(album.getId())); + } + else { + label.setIcon(missingIcon); + } } } return label; diff --git a/src/main/java/musicplayer/model/Album.java b/src/main/java/musicplayer/model/Album.java index b3610fa..4cfad77 100644 --- a/src/main/java/musicplayer/model/Album.java +++ b/src/main/java/musicplayer/model/Album.java @@ -1,11 +1,22 @@ 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.Tag; +import org.jaudiotagger.tag.TagException; + import javax.imageio.ImageIO; import javax.persistence.*; +import javax.swing.*; import java.awt.*; -import java.io.ByteArrayInputStream; +import java.io.File; import java.io.IOException; -import java.io.InputStream; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.NoSuchElementException; import java.util.Optional; import java.util.Set; @@ -21,21 +32,17 @@ public class Album implements Comparable, IDBType, HasSongs { @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "album") @OrderBy("discNumber ASC, trackNumber ASC") private Set songs; - @Lob - @Column(name = "art", nullable = true, length = 10000) - private byte[] art; + + @Transient + private static final String albumArtRegex = ".*(Cover|Folder|cover|folder)\\.(jpg|png)"; + @Transient + private static final int imageScaleToSize = 50; protected Album() { } public Album(String name){ this.name = name; - this.art = null; - } - - public Album(String name, byte[] art) { - this.name = name; - this.art = art; } public String toString() { @@ -46,15 +53,6 @@ public class Album implements Comparable, IDBType, HasSongs { return songs; } - public Optional getAlbumArt() { - if (art == null) return Optional.empty(); - try (InputStream in = new ByteArrayInputStream(art)) { - return Optional.of(ImageIO.read(in)); - } catch (IOException | NullPointerException e) { - return Optional.empty(); - } - } - @SuppressWarnings("MethodWithMultipleReturnPoints") @Override public boolean equals(Object o) { @@ -86,4 +84,28 @@ public class Album implements Comparable, IDBType, HasSongs { public int compareTo(Album o) { return toString().compareToIgnoreCase(o.toString()); } + + /** + * Get album art for a given album. + * @return Album art. + */ + public Optional getAlbumArt(){ + Tag audioTags; + File targetFile = songs.iterator().next().getSongFile(); + Path baseDir = targetFile.getParentFile().toPath(); + try { + audioTags = AudioFileIO.read(targetFile).getTag(); + if(audioTags != null && audioTags.getFirstArtwork() != null) { + return Optional.of(new ImageIcon(((Image)audioTags.getFirstArtwork().getImage()).getScaledInstance + (imageScaleToSize, imageScaleToSize, Image.SCALE_SMOOTH))); + } + } catch (CannotReadException | IOException | ReadOnlyFileException | TagException | InvalidAudioFrameException ignored) { + } + try (DirectoryStream stream = Files.newDirectoryStream(baseDir, albumArtRegex)) { + return Optional.of(new ImageIcon(ImageIO.read(stream.iterator().next().toFile()).getScaledInstance + (imageScaleToSize, imageScaleToSize, Image.SCALE_SMOOTH))); + } catch (IOException | NoSuchElementException ignored) { + } + return Optional.empty(); + } } diff --git a/src/main/java/musicplayer/util/AlbumArtExtractor.java b/src/main/java/musicplayer/util/AlbumArtExtractor.java deleted file mode 100644 index a917088..0000000 --- a/src/main/java/musicplayer/util/AlbumArtExtractor.java +++ /dev/null @@ -1,94 +0,0 @@ -package musicplayer.util; - -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 javax.imageio.ImageIO; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.NoSuchElementException; -import java.util.Optional; - -public final class AlbumArtExtractor { - - private static final String albumArtRegex = ".*(Cover|Folder|cover|folder)\\.(jpg|png)"; - - /** - * Get the album art for a song file. - * @param songFile File from which to get album art data - * (Data is either extracted from file metadata or an image in the same folder). - * @return Album art as a byte array. - */ - public static byte[] getImageData(File songFile){ - Tag audioTags; - try { - audioTags = AudioFileIO.read(songFile).getTag(); - } catch (CannotReadException | IOException | ReadOnlyFileException | TagException | InvalidAudioFrameException e) { - return getImageDataFromFolder(songFile); - } - byte[] tmpArt; - try { - tmpArt = audioTags.getFirstArtwork().getBinaryData(); - BufferedImage image = ImageIO.read(new ByteArrayInputStream(tmpArt)); - tmpArt = convertImageToBytes(image); - } catch (IOException | NullPointerException ignored) { - return getImageDataFromFolder(songFile); - } - return tmpArt; - } - - /** - * Convert an Image into its' byte array representation. - * @param image Image to convert. - * @return image as byte[]. - */ - private static byte[] convertImageToBytes(Image image){ - int imageScaleToSize = 50; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){ - ImageIO.write(convertToBufferedImage(image.getScaledInstance(imageScaleToSize, imageScaleToSize, Image.SCALE_SMOOTH)), "jpg", baos); - baos.flush(); - return baos.toByteArray(); - } catch (IOException e) { return null; } - } - - /** - * Convert an Image to a BufferedImage. - * @param image Source image. - * @return image as BufferedImage. - */ - private static BufferedImage convertToBufferedImage(Image image) { - BufferedImage newImage = new BufferedImage( - image.getWidth(null), image.getHeight(null), - BufferedImage.TYPE_INT_ARGB); - Graphics2D g = newImage.createGraphics(); - g.drawImage(image, 0, 0, null); - g.dispose(); - return newImage; - } - - /** - * Try to find album art for this song based on likely image file names in the folder. - * - * @return BufferedImage of album art or Optional.empty() - */ - private static byte[] getImageDataFromFolder(File songFile) { - Path dir = songFile.getParentFile().toPath(); - try (DirectoryStream stream = Files.newDirectoryStream(dir, albumArtRegex)) { - return convertImageToBytes(ImageIO.read(stream.iterator().next().toFile())); - } catch (IOException | NoSuchElementException ignored) { - return null; - } - } -}